diff --git a/source/configuration.txt b/source/configuration.txt index 432107ac..1c486bca 100644 --- a/source/configuration.txt +++ b/source/configuration.txt @@ -14,9 +14,31 @@ Configuration .. toctree:: :caption: Configuration + Application Configuration + Persistence Targets Sharding + Logging + Query Cache Middleware + Forking Servers + +.. Collection Configuration In this section, you can learn how to configure different options with {+odm+}. -- :ref:`Sharding Configuration `: Learn how to configure - sharding in your {+odm+} application. +- :ref:`mongoid-app-config`: Learn about settings you can use to + customize how your application works with MongoDB. + +- :ref:`mongoid-persistence`: Learn how to use {+odm+} to view and + customize your document storage. + +- :ref:`mongoid-sharding-configuration`: Learn how to configure + sharding in your application. + +- :ref:`mongoid-logging-config`: Learn how to configure loggers in your + application. + +- :ref:`mongoid-query-cache-config`: Learn how to implement query cache + middleware in your application. + +- :ref:`mongoid-forking-server-config`: Learn how to configure your + application to use a forking web server. diff --git a/source/configuration/app-config.txt b/source/configuration/app-config.txt new file mode 100644 index 00000000..05ad16ea --- /dev/null +++ b/source/configuration/app-config.txt @@ -0,0 +1,362 @@ +.. _mongoid-app-config: + +========================= +Application Configuration +========================= + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, customize, behavior + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure how your application +connects to MongoDB and how it processes your data. When you set up your +application, you are required to supply a **connection string**, or +connection URI, which contains a set of instructions that {+odm+} uses to +connect to a MongoDB. To learn more about connection strings, see +:manual:`Connection Strings ` in the +{+server-manual+}. + +You primarily configure {+odm+} by using a ``mongoid.yml`` file that +specifies your connection options and clients. To learn more about +creating a ``mongoid.yml`` file when setting up an application, see one +of the following guides: + +- :ref:`mongoid-quick-start-rails` +- :ref:`mongoid-quick-start-sinatra` + +Structure of mongoid.yml +------------------------ + +The simplest configuration for an application configures {+odm+} to +connect to a MongoDB server at ``localhost:27017`` and access the database +named ``myDB``. The ``mongoid.yml`` file for this configuration +resembles the following file: + +.. code-block:: yaml + :caption: mongoid.yml + + development: + clients: + default: + database: myDB + hosts: + - localhost:27017 + +The top level key in the preceding file, ``development``, refers to the +environment name which the application is running. Possible values include +``development``, ``test`` or ``production``. The third level key, +``default``, refers to the MongoDB client name. Most applications use a +single client named ``default``. + +Generate Default Configuration +------------------------------ + +If you are using {+ror+} as your application framework, you can generate +a default configuration file in your application by running the +following shell command: + +.. code-block:: bash + + rails g mongoid:config + +This command creates the configuration file at ``config/mongoid.yml``. +An initializer is also created at ``config/initializers/mongoid.rb``. We +recommended that you specify all settings in ``mongoid.yml``, but you can +also use the ``mongoid.rb`` initializer to set configuration options. +Settings in ``mongoid.yml`` always override settings in the initializer. + +If you are not using {+ror+}, you can create the ``config/mongoid.yml`` +file, then copy and paste the configuration file shown in the preceding +section. + +Load {+odm+} Configuration +-------------------------- + +.. important:: + + You must configure {+odm+} before using any {+odm+} component. After you + use or reference a component, changing the configuration might not apply + changes to already instantiated components. + +If you are using {+ror+}, Rails automatically loads your {+odm+} +configuration for the current environment as stored in ``Rails.env``. + +You can also specify {+odm+} as the ORM for your application by +adding the following lines to your ``application.rb`` file: + +.. code-block:: ruby + + config.generators do |g| + g.orm :mongoid + end + +If you are not using {+ror+}, you must load your {+odm+} configuration +manually. Call the ``Mongoid.load!`` method, which takes the +configuration file path as its argument: + +.. code-block:: ruby + + # Uses automatically detected environment name + Mongoid.load!("config/mongoid.yml") + + # Specifies environment name manually + Mongoid.load!("config/mongoid.yml", :development) + +{+odm+} detects the environment name by examining the following sources, +in order: + +1. If ``Rails`` top-level constant is defined: ``Rails.env`` +#. If ``Sinatra`` top-level constant is defined: ``Sinatra::Base.environment`` +#. ``RACK_ENV`` environment variable +#. ``MONGOID_ENV`` environment variable. + +.. note:: + + You can also configure {+odm+} directly in {+language+} without using + a configuration file. This configuration style does not support the + concept of environments. Any configuration that you provide is + applied to the current environment. + +.. _mongoid-config-options-all: + +Configuration Options +--------------------- + +The following annotated example ``mongoid.yml`` file describes where to +set each type of configuration option and provides information about +each option and its parameters. + +.. literalinclude:: /includes/configuration/sample-config-options.yml + :language: yaml + +Version Based Defaults +---------------------- + +{+odm+} supports setting configuration options to their default values +for specific versions. This might be useful for when you update to a new +{+odm+} version. When upgrading your {+odm+} version, set the following +options on ``Mongoid::Config``: + +.. code-block:: ruby + + Mongoid.configure do |config| + config.load_defaults + end + +This allows you to upgrade to a new version of {+odm+} while using +the configuration options from the previous installed version. +This feature gives you more time to implement tests for each changed +behavior to make sure your code doesn't break or behave in unexpected +ways. After you verify that your application works as expected, you can +remove this code to use the current version's default configuration. + +ERb Pre-processing +------------------ + +When loading a configuration file, {+odm+} processes it by using +:github:`ERb ` before parsing it as YAML. This allows {+odm+} +to construct the contents of the configuration file at runtime +based on environment variables. + +Because {+odm+} performs ERb rendering before YAML parsing, all ERb +directives in the configuration file are evaluated, including those +occurring in YAML comments. + +The following sample ``mongoid.yml`` file demonstrates how you can +reference an environment variable that stores your connection string: + +.. code-block:: yaml + + development: + clients: + default: + uri: "<%= ENV['MONGODB_URI'] %>" + +.. tip:: + + When outputting values from ERb, ensure the values are valid YAML + and escape them as needed. + +.. _mongoid-config-time-zones: + +Time Zone Configuration +----------------------- + +{+odm+} uses Active Support's time zone functionality, which provides +more functionality than {+language+}'s standard library. Active Support +allows configuration of the ``Time.zone`` variable, a thread-global +variable which provides context for working with date and time values. + +You can implement correct time zone handling in your application by +performing the following steps: + +1. Set the operating system's time zone to UTC. + +#. Set your application default time zone to UTC, as shown in the + following code: + + .. code-block:: ruby + + # If using Rails, in `application.rb`: + class Application < Rails::Application + config.time_zone = 'UTC' + end + + # If not using Rails: + Time.zone = 'UTC' + +#. In each controller and job class, set the appropriate time zone in a + ``before_filter`` callback: + + .. code-block:: ruby + + class ApplicationController < ActionController::Base + before_filter :fetch_user, :set_time_zone + + def set_time_zone + Time.zone = @user.time_zone + end + end + +#. Then, you can work with times in the local time zone. + +#. Use Active Support methods instead of the {+language+} standard library. + + - Use the ``Time.zone.now`` or ``Time.current`` methods instead of + ``Time.now`` + - Use the ``Date.current`` method instead of ``Date.today`` + + The {+language+} standard library methods such as ``Time.now`` and + ``Date.today`` reference your system time zone and not the value of + the ``Time.zone`` variable. + + You might mistake these similarly named methods, so we recommend + using the `Rails/TimeZone + `__ + feature from RuboCop in your tests. + +Set Time Zone on MongoDB Data +----------------------------- + +MongoDB stores all times in UTC without time zone information. +{+odm+} models load and return time values as instances of +``ActiveSupport::TimeWithZone``. You can set the ``use_utc`` option +to control how {+odm+} sets the time zone when loading values from the +database: + +- ``false`` (*default*): {+odm+} uses the value of ``Time.zone`` to set + the time zone of values from the database. +- ``true``: {+odm+} always sets the time zone as UTC on loaded + time values. + +The ``use_utc`` option affects only how data is loaded and does not affect +how data is persisted. For example, if you assign a ``Time`` or +``ActiveSupport::TimeWithZone`` instance to a time field, the time +zone information of the assigned instance is used +regardless of the ``use_utc`` value. + +Alternatively, if you assign a string value to a time field, any time +zone information in the string is parsed. If the string does not include +time zone information it is parsed according to the ``Time.zone`` value. + +The following code sets a ``Time.zone`` value and demonstrates how +{+odm+} processes different time strings: + +.. code-block:: ruby + + Time.use_zone("Asia/Kolkata") do + + # String does not include time zone, so "Asia/Kolkata" is used + ghandi.born_at = "1869-10-02 7:10 PM" + + # Time zone in string (-0600) is used + amelia.born_at = "1897-07-24 11:30 -0600" + end + +SSL/TLS Configuration +--------------------- + +You can configure advanced TLS options in your application, such as +enabling or disabling certain ciphers. To learn about the main SSL/TLS +options, see the :ref:`mongoid-config-options-all` section of this guide. + +You can set TLS context hooks on the {+ruby-driver+}. TLS context +hooks are user-provided ``Proc`` objects that are invoked before any TLS +socket connection is created in the driver. These hooks can be used to +modify the underlying ``OpenSSL::SSL::SSLContext`` object used by the +socket. + +To set TLS context hooks, add a ``Proc`` to the ``Mongo.tls_context_hooks`` +array. This task can be done in an initializer. The following example adds a hook +that only enables the ``"AES256-SHA"`` cipher: + +.. code-block:: ruby + + Mongo.tls_context_hooks.push( + Proc.new { |context| + context.ciphers = ["AES256-SHA"] + } + ) + +Every ``Proc`` in ``Mongo.tls_context_hooks`` is passed an +``OpenSSL::SSL::SSLContext`` object as its sole argument. These ``Proc`` +objects are run sequentially during socket creation. + +.. warning:: + + TLS context hooks are global and affect all ``Mongo::Client`` + instances in an application. + +To learn more about TLS context hooks, see :ruby:`Modifying SSLContext +` in the {+ruby-driver+} +documentation. + +Network Compression +------------------- + +{+odm+} supports compression of messages to and from MongoDB. This +functionality is provided by the {+ruby-driver+}, which implements the +following supported algorithms: + +- `Zstandard `__ (*Recommended*): To use ``zstandard`` + compression, you must install the `zstd-ruby + `__ library. This compressor + produces the highest compression at the same CPU consumption compared + to the other compressors. +- :github:`Snappy `: To use ``snappy`` compression, you + must install the `snappy `__ library. +- `Zlib `__: To use ``zlib`` compression, you + must install the `zlib `__ library. + +To use wire protocol compression, configure the driver options +in your ``mongoid.yml`` file: + +.. code-block:: yaml + + development: + clients: + default: + ... + options: + # Specify compresses to use. (default is no compression) + # Accepted values are zstd, zlib, snappy, or a combination + compressors: ["zstd", "snappy"] + +If you do not explicitly request any compressors, the driver does not +use compression, even if the required dependencies for one or more +compressors are installed. + +The driver chooses the first compressor, if you specify multiple, that +is supported by the MongoDB Server. diff --git a/source/configuration/forking-server-config.txt b/source/configuration/forking-server-config.txt new file mode 100644 index 00000000..ea896857 --- /dev/null +++ b/source/configuration/forking-server-config.txt @@ -0,0 +1,132 @@ +.. _mongoid-forking-server-config: + +============================ +Forking Server Configuration +============================ + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, parent process, child process, behavior + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn about configuring your application to use a +forking web server. + +When using {+odm+} with a forking web server, adhere to the following +guidelines: + +- If possible, do not perform any MongoDB operations in the parent + process before forking. To learn more about how the {+ruby-driver+} + handles forking, see :ruby:`Usage with Forking Servers + ` in the driver + documentation. + +- You can avoid connection errors such as ``Mongo::Error::SocketError`` + and ``Mongo::Error::NoServerAvailable`` by performing the following actions: + + 1. Disconnect MongoDB clients in the parent {+language+} process + immediately *before* forking by using the + ``Mongoid.disconnect_clients`` method. This ensures that the parent and + child processes do not accidentally reuse the same sockets and have + I/O conflicts. ``Mongoid.disconnect_clients`` does not disrupt any + in-flight MongoDB operations, and automatically reconnects when you + perform new operations. + + #. Reconnect your MongoDB clients in the child {+language+} process + immediately *after* forking by using the + ``Mongoid.reconnect_clients`` method. This is required to respawn + the driver's monitoring threads in the child process. + +Most web servers provide hooks that your application can use to +perform actions when the worker processes are forked. The following +sections provide configuration examples for some common {+language+} web +servers. + +Puma +---- + +Use the ``on_worker_boot`` hook to reconnect clients in the workers. Use +the ``before_fork`` and ``on_refork`` hooks to close clients in the +parent process. To learn more about these hooks, see `Clustered mode +hooks `__ in the Puma +API documentation. + +The following code uses the ``on_worker_boot``, ``before_fork``, and +``on_refork`` hooks in a sample Puma configuration file: + +.. code-block:: ruby + :caption: config/puma.rb + + # Runs in the Puma master process before it forks a child worker. + before_fork do + Mongoid.disconnect_clients + end + + # Required when using Puma's fork_worker option. Runs in the + # child worker 0 process before it forks grandchild workers. + on_refork do + Mongoid.disconnect_clients + end + + # Runs in each Puma child process after it forks from its parent. + on_worker_boot do + Mongoid.reconnect_clients + end + +Unicorn +------- + +Use the ``before_fork`` hook to close clients in the parent process. Use +the ``after_fork`` hook to reconnect clients in the workers. To +learn more about these hooks, see `Configurator +`__ in the Unicorn +API documentation. + +The following code uses the ``before_fork`` and ``after_fork`` +hooks in a sample Unicorn configuration file: + +.. code-block:: ruby + :caption: config/unicorn.rb + + before_fork do |_server, _worker| + Mongoid.disconnect_clients + end + + after_fork do |_server, _worker| + Mongoid.reconnect_clients + end + +Passenger +--------- + +Use the ``starting_worker_process`` hook to reconnect clients in the +workers. To learn more about this hook, see `Smart spawning hooks +`__ +in the Passenger documentation. + +.. note:: + + Passenger does have a hook that is invoked in the + parent process before the workers are forked. + +The following code uses the ``starting_worker_process`` hook to +reconnect clients: + +.. code-block:: ruby + + if defined?(PhusionPassenger) + PhusionPassenger.on_event(:starting_worker_process) do |forked| + Mongoid.reconnect_clients if forked + end + end diff --git a/source/configuration/logging-config.txt b/source/configuration/logging-config.txt new file mode 100644 index 00000000..1a6271f7 --- /dev/null +++ b/source/configuration/logging-config.txt @@ -0,0 +1,165 @@ +.. _mongoid-logging-config: + +===================== +Logging Configuration +===================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, customize, trace + +.. contents:: On this page + :local: + :backlinks: none + :depth: 3 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure logging in your {+odm+} +application. When configuring logging, note that {+odm+} provides a +model layer on top of the {+ruby-driver+}, and the *driver* dispatches +data operations to MongoDB. Therefore, some logging output in an +application that uses {+odm+} comes from {+odm+} itself, and some comes from +the driver. + +Driver Logger +------------- + +The {+odm+} client is a {+ruby-driver+} client instance, so the logger +of a {+odm+} client is the {+ruby-driver+} logger, not the {+odm+} +logger. The following code creates a {+odm+} client logger: + +.. code-block:: ruby + + Mongoid.client(:default).logger + +Depending on your application framework and how you configure {+odm+} +and the {+ruby-driver+}, they may use the same logger +instance or different instances, potentially with different +configurations. + +{+ror+} Configuration +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When used in a {+ror+} application, {+odm+} by default inherits +the logger and the log level from Rails. {+odm+} sets the driver's +logger to the same logger instance: + +.. code-block:: ruby + + Rails.logger === Mongoid.logger + # => true + + Mongoid.logger === Mongo::Logger.logger + # => true + +To change the log level, use the `standard Rails configuration +<{+active-record-docs+}/debugging_rails_applications.html#log-levels>`__. +Place the following block in one of your environment configuration +files, such as ``config/environments/production.rb``: + +.. code-block:: ruby + + Rails.application.configure do + config.log_level = :debug + end + +.. note:: + + The ``log_level`` {+odm+} configuration option is not used when + {+odm+} operates in a Rails application, because {+odm+} inherits + Rails' log level. + +To configure either the {+odm+} or driver logger differently from the +Rails logger, use an initializer as shown in the following code: + +.. code-block:: ruby + + Rails.application.configure do + config.after_initialize do + # Change Mongoid log destination and level + Mongoid.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + + # Change driver log destination and level + Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + end + end + +.. note:: + + There is no provision in the {+language+} standard library ``Logger`` + to return the log device, such as the ``IO`` object, that a logger is + using. + + To make, for example, {+odm+} or the {+ruby-driver+} log to the + standard Rails log file (``log/development.log``) with a + different level from standard Rails logger (``Rails.logger``), you + must open the file separately and pass the resulting ``IO`` object to + the ``Logger`` constructor. + +Because {+odm+} sets its own logger and the driver's logger to the +same instance as the Rails logger, modifying any of the instances affects +all the loggers. For example, the following code changes the log level for +all three loggers: + +.. code-block:: ruby + + Mongoid::Logger.logger.level = Logger::DEBUG + +Standalone Configuration +~~~~~~~~~~~~~~~~~~~~~~~~ + +When not loaded in a {+ror+} application, {+odm+} respects the +``log_level`` top-level configuration option: + +.. code-block:: yaml + :emphasize-lines: 6 + + development: + clients: + default: + ... + options: + log_level: :debug + +You can also configure the log level in-line: + +.. code-block:: ruby + + Mongoid.configure do |config| + config.log_level = :debug + end + +The default log destination is standard error. To change the log +destination, create a new logger instance as shown in the following +code: + +.. code-block:: ruby + + Mongoid.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + +To change the {+ruby-driver+} log level or destination, add the +following block to your application file: + +.. code-block:: ruby + + Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| + logger.level = Logger::DEBUG + end + +.. note:: + + {+odm+} does not change the driver's logger when running in + standalone mode. + diff --git a/source/data-modeling/persistence-configuration.txt b/source/configuration/persistence-config.txt similarity index 93% rename from source/data-modeling/persistence-configuration.txt rename to source/configuration/persistence-config.txt index 83a9f032..06414b5b 100644 --- a/source/data-modeling/persistence-configuration.txt +++ b/source/configuration/persistence-config.txt @@ -41,7 +41,7 @@ form of its representative class name. In the following example, for the ``Restaurant`` class, the corresponding collection is named ``restaurants``. For the ``Person`` class, the corresponding collection is named ``people``. -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start default modeling :end-before: end default modeling @@ -55,7 +55,7 @@ You can create a new pluralization rule for your model class by calling the instance method and passing the singular and plural forms of your class name. The following example specifies "reyes" as the plural of "rey": -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start set pluralization :end-before: end set pluralization @@ -68,7 +68,7 @@ collection. When {+odm+} stores a document in a database, it serializes the Ruby object to a BSON document that has the following structure: - .. literalinclude:: /includes/data-modeling/persistence-configuration.rb + .. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start BSON model :end-before: end BSON model @@ -88,7 +88,7 @@ database, and collection where documents for the ``Band`` class are persisted: .. io-code-block:: - .. input:: /includes/data-modeling/persistence-configuration.rb + .. input:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start persistence context attributes :end-before: end persistence context attributes @@ -120,7 +120,7 @@ to use the ``store_in`` macro to store documents from the ``Person`` class in a collection called ``citizens`` in the ``other`` database within a client named ``analytics``: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start store_in example :end-before: end store_in example @@ -136,7 +136,7 @@ to the current thread so that users cannot access each others' data. The following example stores documents in a database determined by a thread-local variable: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start store_in lambda example :end-before: end store_in lambda example @@ -168,7 +168,7 @@ By default, {+odm+} stores documents for the ``Band`` class in a collection call use a different client, database, and collection to perform operations on the ``Band`` class's documents: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start with example :end-before: end with example @@ -191,7 +191,7 @@ operations. The configurations apply only to the specified type of operation. The following example uses the ``with`` method to specify the use of the secondary node for all read operations within the block. -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start read configuration :end-before: end read configuration @@ -226,7 +226,7 @@ locales in different databases. The code shows how to use the ``{+odm+}.override_database`` method to globally set the persistence context based on the locale: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start global configuration example :end-before: end global configuration example @@ -242,7 +242,7 @@ Client and Collection Access You can access the client or collection of a model or document instance by using the ``mongo_client`` and ``collection`` class methods: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start access client collection :end-before: end access client collection @@ -258,7 +258,7 @@ The following code example accesses the client used by the ``Band`` model class. It then uses the ``with`` method on the client to write to the ``music`` database, setting the ``w`` write option to ``0`` to not require write acknowledgement. -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start client with example :end-before: end client with example @@ -270,7 +270,7 @@ You can override the ``:read`` and ``:write`` options on a collection by using t ``with`` method. The following example shows how to use the ``with`` method to set the ``w`` write option to ``0``: -.. literalinclude:: /includes/data-modeling/persistence-configuration.rb +.. literalinclude:: /includes/configuration/persistence-configuration.rb :language: ruby :start-after: start collection with example :end-before: end collection with example diff --git a/source/configuration/query-cache-config.txt b/source/configuration/query-cache-config.txt new file mode 100644 index 00000000..ec89cd39 --- /dev/null +++ b/source/configuration/query-cache-config.txt @@ -0,0 +1,72 @@ +.. _mongoid-query-cache-config: + +==================================== +Query Cache Middleware Configuration +==================================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: code example, storage, memory + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to configure your application to use +query cache middleware. Query cache middleware allows you to +activate the :ref:`mongoid-query-cache` for each request to store your +query results. This can improve your application speed and efficiency by +reducing the number of calls your application must make to the database. + +Enable Query Cache for Rack Web Requests +---------------------------------------- + +The {+ruby-driver+} provides a Rack middleware which enables the +query cache during each web request. The +following code demonstrates how to enable the Query Cache Middleware in +a {+ror+} application: + +.. code-block:: ruby + :caption: config/application.rb + + # Add Mongo::QueryCache::Middleware at the bottom of the middleware + # stack or before other middleware that queries MongoDB. + config.middleware.use Mongo::QueryCache::Middleware + +To learn more about using Rack middleware in Rails applications, see +`Configuring Middleware Stack +<{+active-record-docs+}/rails_on_rack.html#configuring-middleware-stack>`__ +in the Rails documentation. + +Enable Query Cache for Active Job +--------------------------------- + +The {+ruby-driver+} provides Query Cache Middleware for `Active Job +<{+active-record-docs+}/active_job_basics.html>`__. +You can enable it for all jobs in an initializer, as shown in the +following code: + +.. code-block:: ruby + :caption: config/initializers/active_job.rb + + # Enable Mongo driver query cache for Active Job + ActiveSupport.on_load(:active_job) do + include Mongo::QueryCache::Middleware::ActiveJob + end + +You can also enable it for a specific job class, as shown in the +following code: + +.. code-block:: ruby + + class MyJob < ActiveJob::Base + include Mongo::QueryCache::Middleware::ActiveJob + end diff --git a/source/data-modeling.txt b/source/data-modeling.txt index 90fa06cd..7a026f86 100644 --- a/source/data-modeling.txt +++ b/source/data-modeling.txt @@ -17,12 +17,11 @@ Model Your Data Documents Field Types Field Behaviors - Persistence Configuration Inheritance Document Validation Callbacks Data Associations - Optimize Queries With Indexes + Indexes In this section, you can learn how to model data in {+odm+}. @@ -34,9 +33,6 @@ In this section, you can learn how to model data in {+odm+}. - :ref:`mongoid-field-behaviors`: Learn how to customize the behaviors of fields in {+odm+} to meet your application requirements. - -- :ref:`mongoid-persistence`: Learn how to use {+odm+} to view and customize - your document storage. - :ref:`mongoid-modeling-inheritance`: Learn how to implement inheritance in your model classes. @@ -48,7 +44,7 @@ In this section, you can learn how to model data in {+odm+}. customize the life cycle of your models. - :ref:`mongoid-associations`: Learn how to create and manage data - associations in your model classes. + associations between your model classes. - :ref:`mongoid-optimize-queries-with-indexes`: Learn how to create and manage indexes for your model classes. diff --git a/source/data-relationships/associations.txt b/source/data-relationships/associations.txt deleted file mode 100644 index a8224430..00000000 --- a/source/data-relationships/associations.txt +++ /dev/null @@ -1,23 +0,0 @@ -.. _mongoid_associations: - -============ -Associations -============ - -.. facet:: - :name: genre - :values: reference - -.. meta:: - :keywords: ruby framework, odm, code example - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Overview --------- - -Associations in {+odm+} allow you to create relationships between models. \ No newline at end of file diff --git a/source/includes/data-modeling/persistence-configuration.rb b/source/includes/configuration/persistence-configuration.rb similarity index 100% rename from source/includes/data-modeling/persistence-configuration.rb rename to source/includes/configuration/persistence-configuration.rb diff --git a/source/includes/configuration/sample-config-options.yml b/source/includes/configuration/sample-config-options.yml new file mode 100644 index 00000000..9667583a --- /dev/null +++ b/source/includes/configuration/sample-config-options.yml @@ -0,0 +1,235 @@ +development: + # Configures available database clients. (required) + clients: + # Defines the default client. (required) + default: + # Supplies your connection URI, including the database name. + uri: mongodb+srv://user:pass@mongo0.example.com/myDB + + # OR, you can define the parameters separately. + # Defines the name of the default database. (required) + database: my_db_development + # Provides the hosts the client can connect to. + # Must be an array of host:port pairs. (required) + hosts: + - localhost:27017 + options: + # All options in this section are Ruby driver client options. + # To learn more, visit + # https://www.mongodb.com/docs/ruby-driver/current/reference/create-client/ + + # Sets the write concern. (default = { w: 1 }) + write: + w: 1 + + # Sets the read preference. Valid options for mode are: :secondary, + # :secondary_preferred, :primary, :primary_preferred, :nearest + # (default: primary) + read: + mode: :secondary_preferred + tag_sets: # If using tag sets + - use: web + + # Sets name of the user for authentication. + user: "user" + + # Sets password of the user for authentication. + password: "password" + + # Sets user's database roles. + roles: + - "dbOwner" + + # Sets the authentication mechanism. Valid options include: + # :scram, :scram256, :mongodb_cr, :mongodb_x509, :gssapi, :aws, :plain + # MongoDB Server defaults to :scram + auth_mech: :scram + + # Sets the database or source to authenticate the user against. + # (default: the database specified above or admin) + auth_source: admin + + # Specifies type of connection. Can be one of: :direct, + # :replica_set, :sharded + # Set to :direct when connecting to hidden members of a replica set. + connect: :direct + + # Changes the time taken for the server monitors to refresh + # their status via hello commands. (default: 10) + heartbeat_frequency: 10 + + # Sets time in seconds for selecting servers for a :nearest read + # preference. (default: 0.015) + local_threshold: 0.015 + + # Sets timeout in seconds for selecting a server for an + # operation. (default: 30) + server_selection_timeout: 30 + + # Sets maximum number of connections in the connection pool. + # (default: 5) + max_pool_size: 5 + + # Sets minimum number of connections in the connection pool. + # (default: 1) + min_pool_size: 1 + + # Sets time to wait, in seconds, in the connection pool for a + # connection to be checked in before timing out. (default: 5) + wait_queue_timeout: 5 + + # Sets time to wait to establish a connection before timing out, + # in seconds. (default: 10) + connect_timeout: 10 + + # Sets name of the replica set to connect to. Servers provided as + # seeds that do not belong to this replica set are ignored. + replica_set: myRS + + # Sets compressors to use for wire protocol compression. + # (default is to not use compression) + compressors: ["zstd", "snappy", "zlib"] + + # Specified whether to connect to the servers by using ssl. + # (default: false) + ssl: true + + # Sets certificate file used to identify the connection for SSL. + ssl_cert: /path/to/my.cert + + # Sets private keyfile used to identify the connection against MongoDB. + # Even if the key is stored in the same file as the certificate, + # both need to be explicitly specified. + ssl_key: /path/to/my.key + + # Sets passphrase for the private key. + ssl_key_pass_phrase: password123 + + # Specifies whether to do peer certification validation. + # (default: true) + ssl_verify: true + + # Sets file containing concatenated certificate authority + # certificates used to validate certs passed from the other end + # of the connection. + ssl_ca_cert: /path/to/ca.cert + + # Specifies whether to truncate long log lines. (default: true) + truncate_logs: true + + # Optional Mongoid-specific configuration. + options: + # Allows BSON::Decimal128 to be parsed and returned directly in + # field values. Only has effect when BSON 5+ is present. + allow_bson5_decimal128: false + + # Sets app name that is printed to the MongoDB logs upon establishing + # a connection. Value is used as the database name if the database + # name is not provided. (default: nil) + app_name: nil + + # When false, callbacks for embedded documents will not be + # called. This is the default in 9.0. + # Setting this flag to true restores the pre-9.0 behavior, where callbacks + # for embedded documents are called, which might lead to stack overflow errors. + around_callbacks_for_embeds: false + + # Sets the async_query_executor for the application. By default the + # thread pool executor is set to :immediate. Options are: + # - :immediate - Initializes a single +Concurrent::ImmediateExecutor+ + # - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+ + # that uses the +async_query_concurrency+ for the +max_threads+ value. + async_query_executor: :immediate + + # Marks belongs_to associations as required by default, so saving a + # model with a missing association triggers a validation error. + belongs_to_required_by_default: true + + # Sets the global discriminator key. + discriminator_key: "_type" + + # Raises an exception when a field is redefined. + duplicate_fields_exception: false + + # Defines how many asynchronous queries can be executed concurrently. + # This option should be set only if `async_query_executor` is set + # to `:global_thread_pool`. + global_executor_concurrency: nil + + # When this flag is true, any attempt to change the _id of a persisted + # document will raise an exception (Errors::ImmutableAttribute). + # This is the default in 9.0. Setting this flag to false restores the + # pre-9.0 behavior, where changing the _id of a persisted + # document might be ignored. + immutable_ids: true + + # Includes the root model name in json serialization. + include_root_in_json: false + + # # Include the _type field in serialization. + include_type_for_serialization: false + + # Specifies whether to join nested persistence contexts for atomic + # operations to parent contexts. + join_contexts: false + + # When this flag is false (the default for 9.0), a document that + # is created or loaded remembers the storage options that were active + # when it was loaded, and will use those same options by default when + # saving or reloading itself. + # + # When this flag is true, a document does not remember the storage + # options from when it was loaded/created, and + # subsequent updates will need to explicitly set up those options + # each time. + legacy_persistence_context_behavior: false + + # Specifies whether to use legacy read-only behavior. To learn more, + # visit https://www.mongodb.com/docs/mongoid/current/interact-data/crud + legacy_readonly: false + + # Sets the log level. This must be set before referencing clients + # or Mongo.logger, because changes to this option are not be + # propagated to any clients and loggers that already exist. + log_level: :info + + # Stores BigDecimals as Decimal128s instead of strings in the db. + map_big_decimal_to_decimal128: true + + # Preloads all models in development, needed when models use + # inheritance. + preload_models: false + + # When this flag is true, callbacks for every embedded document will be + # called only once, even if the embedded document is embedded in multiple + # documents in the root document's dependencies graph. + # This is the default in 9.0. Setting this flag to false restores the + # pre-9.0 behavior, where callbacks are called for every occurrence of an + # embedded document. + prevent_multiple_calls_of_embedded_callbacks: true + + # Raises an error when performing a find operation and no document + # matches. + raise_not_found_error: true + + # Raises an error when defining a scope with the same name as an + # existing method. + scope_overwrite_exception: false + + # Returns stored times as UTC. + use_utc: false + + # Optional driver-specific configuration. + driver_options: + # When this flag is off, an aggregation done on a view is performed on + # the documents included in that view, instead of all documents in the + # collection. When this flag is on, the view filter is ignored. + broken_view_aggregate: true + + # When this flag is set to false, the view options is correctly + # propagated to readable methods. + broken_view_options: true + + # When this flag is set to true, the update and replace methods + # validate the parameters and raise an error if they are invalid. + validate_update_replace: false diff --git a/source/includes/unicode-ballot-x.rst b/source/includes/unicode-ballot-x.rst deleted file mode 100644 index 50c3667a..00000000 --- a/source/includes/unicode-ballot-x.rst +++ /dev/null @@ -1 +0,0 @@ -.. |x| unicode:: U+2717 diff --git a/source/includes/unicode-checkmark.rst b/source/includes/unicode-checkmark.rst deleted file mode 100644 index 16f4e947..00000000 --- a/source/includes/unicode-checkmark.rst +++ /dev/null @@ -1 +0,0 @@ -.. |checkmark| unicode:: U+2713 diff --git a/source/index.txt b/source/index.txt index ae3e2931..cca00495 100644 --- a/source/index.txt +++ b/source/index.txt @@ -15,11 +15,10 @@ MongoDB in Ruby. To work with {+odm+} from the command line using Quick Start - {+ror+} Quick Start - Sinatra Add {+odm+} to an Existing Application + Configuration Interact with Data Model Your Data - Configuration Secure Your Data - /working-with-data API Documentation What's New Compatibility diff --git a/source/reference/collection-configuration.txt b/source/legacy-files/collection-configuration.txt similarity index 100% rename from source/reference/collection-configuration.txt rename to source/legacy-files/collection-configuration.txt diff --git a/source/reference/map-reduce.txt b/source/legacy-files/map-reduce.txt similarity index 100% rename from source/reference/map-reduce.txt rename to source/legacy-files/map-reduce.txt diff --git a/source/reference/rails-integration.txt b/source/legacy-files/rails-integration.txt similarity index 100% rename from source/reference/rails-integration.txt rename to source/legacy-files/rails-integration.txt diff --git a/source/reference/associations.txt b/source/reference/associations.txt deleted file mode 100644 index 46d4c880..00000000 --- a/source/reference/associations.txt +++ /dev/null @@ -1,1655 +0,0 @@ -.. _associations: - -************ -Associations -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Referenced Associations -======================= - -Mongoid supports the ``has_one``, ``has_many``, ``belongs_to`` and -``has_and_belongs_to_many`` associations familiar to ActiveRecord users. - - -Has One -------- - -Use the ``has_one`` macro to declare that the parent has a child stored in -a separate collection. The child is optional by default: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - end - -When using ``has_one``, the child model must use ``belongs_to`` to declare the -association with the parent: - -.. code-block:: ruby - - class Studio - include Mongoid::Document - - belongs_to :band - end - -Given the above definitions, every child document contains a reference to -its respective parent document: - -.. code-block:: ruby - - band = Band.create!(studio: Studio.new) - # => # - - band.studio - # => # - -Use validations to require that the child is present: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - - validates_presence_of :studio - end - - -Has Many --------- - -Use the ``has_many`` association to declare that the parent has zero or more -children stored in a separate collection: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :members - end - -Like with ``has_one``, the child model must use ``belongs_to`` to declare the -association with the parent: - -.. code-block:: ruby - - class Member - include Mongoid::Document - - belongs_to :band - end - -Also as with ``has_one``, the child documents contain references to their -respective parents: - -.. code-block:: ruby - - band = Band.create!(members: [Member.new]) - # => # - - band.members - # => [#] - -Use validations to require that at least one child is present: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :members - - validates_presence_of :members - end - - -Queries -``````` - -.. _has-many-any: - -``any?`` -~~~~~~~~ - -Use the ``any?`` method on the association to efficiently determine whether -the association contains any documents, without retrieving the entire set -of documents from the database: - -.. code-block:: ruby - - band = Band.first - band.members.any? - -``any?`` also implements the `Enumerable#any? API -`_, allowing -filtering with a block: - -.. code-block:: ruby - - band = Band.first - band.members.any? { |member| member.instrument == 'piano' } - -... or by a class name which can be useful for polymorphic associations: - -.. code-block:: ruby - - class Drummer < Member - end - - band = Band.first - band.members.any?(Drummer) - -If the association is already loaded, ``any?`` inspects the loaded -documents and does not query the database: - -.. code-block:: ruby - - band = Band.first - # Queries the database - band.members.any? - - band.members.to_a - - # Does not query the database - band.members.any? - -Note that simply calling ``any?`` would *not* load the association -(since ``any?`` only retrieves the _id field of the first matching document). - -``exists?`` -~~~~~~~~~~~ - -The ``exists?`` method on the association determines whether there are -any *persisted* documents in the association. Unlike the ``any?`` method: - -- ``exists?`` always queries the database, even if the association is already - loaded. -- ``exists?`` does not consider non-persisted documents. -- ``exists?`` does not allow filtering in the application like ``any?`` does, - and does not take any arguments. - -The following example illustrates the difference between ``exists?`` and -``any?``: - -.. code-block:: ruby - - band = Band.create! - # Member is not persisted. - band.members.build - - band.members.any? - # => true - band.members.exists? - # => false - - # Persist the member. - band.members.map(&:save!) - - band.members.any? - # => true - band.members.exists? - # => true - - -Belongs To ----------- - -Use the ``belongs_to`` macro to associate a child with a parent stored in a -separate collection. The ``_id`` of the parent (if a parent is associated) -is stored in the child. - -By default, if a ``belongs_to`` association is defined on a model, it must be -provided a value for a model instance to be saved. Use the ``optional: true``` -option to make the instances persistable without specifying the parent: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_one :studio - end - - class Studio - include Mongoid::Document - - belongs_to :band, optional: true - end - - studio = Studio.create! - # => # - -To change the default behavior of ``belongs_to`` associations to not require -their respective parents globally, set the ``belongs_to_required_by_default`` -:ref:`configuration option ` to ``false``. - -Although ``has_one`` and ``has_many`` associations require the -corresponding ``belongs_to`` association to be defined on the child, -``belongs_to`` may also be used without a corresponding ``has_one`` or -``has_many`` macro. In this case the child is not accessible from the parent -but the parent is accessible from the child: - -.. code-block:: ruby - - class Band - include Mongoid::Document - end - - class Studio - include Mongoid::Document - - belongs_to :band - end - -For clarity it is possible to add the ``inverse_of: nil`` option in cases when -the parent does not define the association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - end - - class Studio - include Mongoid::Document - - belongs_to :band, inverse_of: nil - end - - -Has And Belongs To Many ------------------------ - -Use the ``has_and_belongs_to_many`` macro to declare a many-to-many -association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_and_belongs_to_many :tags - end - - class Tag - include Mongoid::Document - - has_and_belongs_to_many :bands - end - -Both model instances store a list of ids of the associated models, if any: - -.. code-block:: ruby - - band = Band.create!(tags: [Tag.create!]) - # => # - - band.tags - # => [#] - -You can create a one-sided ``has_and_belongs_to_many`` association to store -the ids only in one document using the ``inverse_of: nil`` option: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_and_belongs_to_many :tags, inverse_of: nil - end - - class Tag - include Mongoid::Document - end - - band = Band.create!(tags: [Tag.create!]) - # => # - - band.tags - # => [#] - -A one-sided ``has_and_belongs_to_many`` association is, naturally, only -usable from the model where it is defined. - -.. note:: - - Given two models, A and B where A ``has_and_belongs_to_many`` B, - when adding a document of type B to the HABTM association on a document of - type A, Mongoid will not update the ``updated_at`` field for the document of - type A, but will update the ``updated_at`` field for the document of type B. - -Querying Referenced Associations --------------------------------- - -In most cases, efficient queries across referenced associations (and in general -involving data or conditions or multiple collections) are performed using -the aggregation pipeline. Mongoid helpers for constructing aggregation pipeline -queries are described in the :ref:`aggregation pipeline ` -section. - -For simple queries, the use of aggregation pipeline may be avoided and -associations may be queried directly. When querying associations directly, -all conditions must be on that association's collection only (which typically -means association in question and any associations embedded in it). - -For example, given the following models: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :tours - has_many :awards - - field :name, type: String - end - - class Tour - include Mongoid::Document - - belongs_to :band - - field :year, type: Integer - end - - class Award - include Mongoid::Document - - belongs_to :band - - field :name, type: String - end - -One could retrieve all bands that have toured since 2000 as follows: - -.. code-block:: ruby - - band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) - bands = Band.find(band_ids) - -The conditions on ``Tour`` can be arbitrarily complex, but they must all -be on the same ``Tour`` document (or documents embedded in ``Tour``). - -To find awards for bands that have toured since 2000: - -.. code-block:: ruby - - band_ids = Tour.where(year: {'$gte' => 2000}).pluck(:band_id) - awards = Award.where(band_id: {'$in' => band_ids}) - - -Embedded Associations -===================== - -Thanks to MongoDB's document model, Mongoid also offers embedded associations -which allow documents of different types to be stored hierarchically -in the same collection. Embedded associations are defined using -``embeds_one``, ``embeds_many`` and ``embedded_in`` macros, plus -``recursively_embeds_one`` and ``recursively_embeds_many`` for recursive -embedding. - -Embeds One ----------- - -One to one associations where the children are embedded in the parent -document are defined using Mongoid's ``embeds_one`` and ``embedded_in`` macros. - -Defining -```````` - -The parent document of the association should use the ``embeds_one`` macro to -indicate is has one embedded child, where the document that is embedded uses -``embedded_in``. Definitions are required on both sides to the association -in order for it to work properly. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label - end - - class Label - include Mongoid::Document - field :name, type: String - embedded_in :band - end - -Storage -``````` - -Documents that are embedded using the ``embeds_one`` macro are stored as a -hash inside the parent in the parent's database collection. - -.. code-block:: ruby - - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), - "label" : { - "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), - "name" : "Mute", - } - } - -You can optionally tell Mongoid to store the embedded document in a different -attribute other than the name, by providing the ``:store_as`` option. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label, store_as: "lab" - end - - -Embeds Many ------------ - -One to many relationships where the children are embedded in the parent -document are defined using Mongoid's ``embeds_many`` and ``embedded_in`` macros. - -Defining -```````` - -The parent document of the association should use the ``embeds_many`` macro -to indicate it has many embedded children, where the document that is -embedded uses ``embedded_in``. Definitions are required on both sides of -the association in order for it to work properly. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - end - - class Album - include Mongoid::Document - field :name, type: String - embedded_in :band - end - -Storage -``````` - -Documents that are embedded using the ``embeds_many`` macro are stored as -an array of hashes inside the parent in the parent's database collection. - -.. code-block:: ruby - - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e9"), - "albums" : [ - { - "_id" : ObjectId("4d3ed089fb60ab534684b7e0"), - "name" : "Violator", - } - ] - } - -You can optionally tell Mongoid to store the embedded document in a different -attribute other than the name, by providing the ``:store_as`` option. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums, store_as: "albs" - end - -Recursive Embedding -------------------- - -A document can recursively embed itself using ``recursively_embeds_one`` or -``recursively_embeds_many``, which provides accessors for the parent and -children via ``parent_`` and ``child_`` methods. - -.. code-block:: ruby - - class Tag - include Mongoid::Document - field :name, type: String - recursively_embeds_many - end - - root = Tag.new(name: "programming") - child_one = root.child_tags.build - child_two = root.child_tags.build - - root.child_tags # [ child_one, child_two ] - child_one.parent_tag # [ root ] - child_two.parent_tag # [ root ] - - class Node - include Mongoid::Document - recursively_embeds_one - end - - root = Node.new - child = Node.new - root.child_node = child - - root.child_node # child - child.parent_node # root - -Referencing Vs Embedding ------------------------- - -While a complete discussion of referencing vs embedding is beyond the scope -of this tutorial, here are some high level considerations for choosing -one over the other. - -When an association is embedded, both parent and child documents are stored -in the same collection. This permits efficient persistence and retrieval -when both are used/needed. For example, if the navigation bar on a web site -shows attributes of a user that are stored in documents themselves, it is -often a good idea to use embedded associations. - -Using embedded associations allows using MongoDB tools like the -`aggregation pipeline -`_ to query -these documents in a powerful way. - -Because embedded documents are stored as part of their parent top-level -documents, it is not possible to persist an embedded document by itself, -nor is it possible to retrieve embedded documents directly. However, -embedded documents can still be efficiently queried and retrieved with the -help of MongoDB projection operation: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :started_on, type: Date - embeds_one :label - end - - class Label - include Mongoid::Document - field :name, type: String - embedded_in :band - end - - # Retrieve labels for bands started in the last year. - # - # Sends a find query like this: - # {"find"=>"bands", - # "filter"=>{"started_on"=>{"$gt"=>2018-07-01 00:00:00 UTC}}, - # "projection"=>{"_id"=>1, "label"=>1}} - Band.where(started_on: {'$gt' => Time.now - 1.year}).only(:label).map(&:label).compact.uniq - -Setting Stale Values on Referenced Associations -``````````````````````````````````````````````` - -Setting a stale value to a referenced association can sometimes result in -a ``nil`` value being persisted to the database. Take the following case: - -.. code-block:: ruby - - class Post - include Mongoid::Document - - has_one :comment, inverse_of: :post - end - - class Comment - include Mongoid::Document - - belongs_to :post, inverse_of: :comment, optional: true - end - - post.comment = comment1 - post.reload - -At this point, ``post.comment`` is set to ``comment1``, however since a reload -happened, ``post.comment`` does not refer to the same object as ``comment1``. -Meaning, updating one object does not implicitly update the other. This matters -for the next operation: - -.. code-block:: ruby - - post.comment = comment2 - post.reload - -Now, ``post.comment`` is set to ``comment2``, and the ``post_id`` of the old -comment is set to ``nil``. However, the value that was assigned to -``post.comment`` did not refer to the same object as ``comment1``, therefore, -while the old value of ``post.comment`` was updated to have a ``nil`` -``post_id``, ``comment1`` still has the ``post_id`` set. - -.. code-block:: ruby - - post.comment = comment1 - post.reload - -Finally, this last assignment attempts to set the ``post_id`` on ``comment1``, -which should be ``nil`` at this point, but is set to the old ``post_id``. -During this operation, the ``post_id`` is cleared from ``comment2``, and the -new ``post_id`` is set on ``comment1``. However, since the ``post_id`` was -already set on ``comment1``, nothing is persisted, and we end up with both -comments having a ``nil`` ``post_id``. At this point, running ``post.comment`` -returns ``nil``. - - -Querying Embedded Associations ------------------------------- - -When querying top-level documents, conditions can be specified on documents -in embedded associations using the dot notation. For example, given the -following models: - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :tours - embeds_many :awards - field :name, type: String - end - - class Tour - include Mongoid::Document - embedded_in :band - field :year, type: Integer - end - - class Award - include Mongoid::Document - embedded_in :band - field :name, type: String - end - -To retrieve bands based on tour attributes, use the dot notation as follows: - -.. code-block:: ruby - - # Get all bands that have toured since 2000 - Band.where('tours.year' => {'$gte' => 2000}) - -To retrieve only documents of embedded associations, without retrieving -top-level documents, use the ``pluck`` projection method: - -.. code-block:: ruby - - # Get awards for bands that have toured since 2000 - Band.where('tours.year' => {'$gte' => 2000}).pluck(:awards) - -.. _embedded-matching: - -Querying Loaded Associations -```````````````````````````` - -Mongoid query methods can be used on embedded associations of documents which -are already loaded in the application. This mechanism is called -"embedded matching" and it is implemented entirely in Mongoid--the queries -are NOT sent to the server. - -The following operators are supported: - -- :manual:`Comparison operators ` -- :manual:`Logical operators ` -- :manual:`Array query operators ` -- :manual:`$exists ` -- :manual:`$mod ` -- :manual:`$type ` -- :manual:`$regex ` (``$options`` field - is only supported when the ``$regex`` argument is a string) -- :manual:`Bitwise operators ` -- :manual:`$comment ` - -For example, using the model definitions just given, we could query -tours on a loaded band: - -.. code-block:: ruby - - band = Band.where(name: 'Astral Projection').first - tours = band.tours.where(year: {'$gte' => 2000}) - -Embedded Matching vs Server Behavior -```````````````````````````````````` - -Mongoid's embedded matching aims to support the same functionality and -semantics as native queries on the latest MongoDB server version. -Note the following known limitations: - -- Embedded matching is not implemented for :ref:`text search `, - :manual:`geospatial query operators `, - operators that execute JavaScript code (:manual:`$where `) - and operators that are implemented via other server functionality such as - :manual:`$expr ` - and :manual:`$jsonSchema `. - -- Mongoid DSL expands ``Range`` arguments to hashes with ``$gte`` and ``$lte`` - conditions. `In some cases `_ - this creates bogus queries. Embedded matchers raise the ``InvalidQuery`` - exception in these cases. The operators that are known to be affected are - ``$elemMatch``, ``$eq``, ``$gt``, ``$gte``, ``$lt``, ``$lte`` and ``$ne``. - -- When performing embedded matching with ``$regex``, `it is not currently - possible `_ to specify a - regular expression object as the pattern and also provide options. - -- MongoDB Server 4.0 and earlier servers do not validate ``$type`` arguments strictly - (for example, allowing invalid arguments like 0). This is validated more strictly on - the client side. - - -.. _omit-id: - -Omitting ``_id`` Fields ------------------------ - -By default, Mongoid adds an ``_id`` field to each embedded document. This -permits easy referencing of and operations on the embedded documents. - -These ``_id`` fields may be omitted to save storage space. To do so, -:ref:`override the _id field definition in the child documents ` -and remove the default value: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - embeds_many :line_items - end - - class LineItem - include Mongoid::Document - - embedded_in :order - - field :_id, type: Object - end - -In the current version of Mongoid the field definition is required, but -without a default value specified no value will be stored in the database. -A future version of Mongoid may allow removing previously defined fields. - -.. note:: - - Removing the ``_id`` field means that embedded documents must be identified - by their content attribute values during queries, updates and deletes. - - -Deleting --------- - -Mongoid provides three methods for deleting children from ``embeds_many`` -associations: ``clear``, ``destroy_all`` and ``delete_all``. - -``clear`` -````````` - -The ``clear`` method uses the :manual:`$unset operator -` to remove the entire association from the -host document. It does not run destroy callbacks on the documents being removed, -acting like ``delete_all`` in this regard: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.clear - -If ``clear`` is called on an association in an unsaved host document, it will -still try to remove the association from the database based on the host -document's ``_id``: - -.. code-block:: ruby - - band = Band.find(...) - band.tours << Tour.new(...) - - unsaved_band = Band.new(id: band.id, tours: [Tour.new]) - # Removes all tours from the persisted band due to _id match. - unsaved_band.tours.clear - - band.tours - # => [] - -``delete_all`` -`````````````` - -The ``delete_all`` method removes the documents that are in the association -using the :manual:`$pullAll operator `. -Unlike ``clear``, ``delete_all``: - -- Loads the association, if it wasn't yet loaded; -- Only removes the documents that exist in the application. - -``delete_all`` does not run destroy callbacks on the documents being removed. - -Example: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.delete_all - - -``destroy_all`` -``````````````` - -The ``delete_all`` method removes the documents that are in the association -using the :manual:`$pullAll operator ` -while running the destroy callbacks. Like ``delete_all``, ``destroy_all`` -loads the entire association if it wasn't yet loaded and it only removes -documents that exist in the application: - -.. code-block:: ruby - - band = Band.find(...) - band.tours.destroy_all - - -.. _hash-assignment: - -Hash Assignment ---------------- - -Embedded associations allow the user to assign a ``Hash`` instead of a document -to an association. On assignment, this hash is coerced into a document of the -class of the association that it's being assigned to. Take the following -example: - -.. code:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - end - - class Album - include Mongoid::Document - field :name, type: String - embedded_in :band - end - - band = Band.create! - band.albums = [ { name: "Narrow Stairs" }, { name: "Transatlanticism" } ] - p band.albums - # => [ #, # ] - -This works for ``embeds_one``, ``embeds_many``, and ``embedded_in`` associations. -Note that you cannot assign hashes to referenced associations. - - -Common Behavior -=============== - -Extensions ----------- - -All associations can have extensions, which provides a way to add application specific -functionality to the association. They are defined by providing a block to the association definition. - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses do - def find_by_country(country) - where(country: country).first - end - def chinese - _target.select { |address| address.country == "China" } - end - end - end - - person.addresses.find_by_country("Mongolia") # returns address - person.addresses.chinese # returns [ address ] - -Custom Association Names ------------------------- - -You can name your associations whatever you like, but if the class cannot be inferred by -Mongoid from the name, and neither can the opposite side you'll want to provide the -macro with some additional options to tell Mongoid how to hook them up. - -.. code-block:: ruby - - class Car - include Mongoid::Document - embeds_one :engine, class_name: "Motor", inverse_of: :machine - end - - class Motor - include Mongoid::Document - embedded_in :machine, class_name: "Car", inverse_of: :engine - end - -Custom Primary & Foreign Keys ------------------------------ - -The fields used when looking up associations can be explicitly specified. -The default is to use ``id`` on the "parent" association and ``#{association_name}_id`` -on the "child" association, for example with a has_many/belongs_to: - -.. code-block:: ruby - - class Company - include Mongoid::Document - has_many :emails - end - - class Email - include Mongoid::Document - belongs_to :company - end - - company = Company.find(id) - # looks up emails where emails.company_id == company.id - company.emails - -Specify a different ``primary_key`` to change the field name on the "parent" -association and ``foreign_key`` to change the field name on the "child" -association: - -.. code-block:: ruby - - class Company - include Mongoid::Document - field :c, type: String - has_many :emails, foreign_key: 'c_ref', primary_key: 'c' - end - - class Email - include Mongoid::Document - # This definition of c_ref is automatically generated by Mongoid: - # field :c_ref, type: Object - # But the type can also be specified: - field :c_ref, type: String - belongs_to :company, foreign_key: 'c_ref', primary_key: 'c' - end - - company = Company.find(id) - # looks up emails where emails.c_ref == company.c - company.emails - -With a has_and_belongs_to_many association, since the data is stored on both -sides of the association, there are 4 fields configurable when the association -is defined: - -- ``:primary_key`` is the field on the remote model that contains the value - by which the remote model is looked up. -- ``:foreign_key`` is the field on the local model which stores the - ``:primary_key`` values. -- ``:inverse_primary_key`` is the field on the local model that the remote - model uses to look up the local model documents. -- ``:inverse_foreign_key`` is the field on the remote model storing the - values in ``:inverse_primary_key``. - -An example might make this more clear: - -.. code-block:: ruby - - class Company - include Mongoid::Document - - field :c_id, type: Integer - field :e_ids, type: Array - - has_and_belongs_to_many :employees, - primary_key: :e_id, foreign_key: :e_ids, - inverse_primary_key: :c_id, inverse_foreign_key: :c_ids - end - - class Employee - include Mongoid::Document - - field :e_id, type: Integer - field :c_ids, type: Array - - has_and_belongs_to_many :companies, - primary_key: :c_id, foreign_key: :c_ids, - inverse_primary_key: :e_id, inverse_foreign_key: :e_ids - end - - company = Company.create!(c_id: 123) - # => # - - employee = Employee.create!(e_id: 456) - # => # - - company.employees << employee - - company - # => # - - employee - # => # - -Note that just like with the default ``#{association_name}_id`` field, -Mongoid automatically adds a field for the custom foreign key ``c_ref`` to -the model. However, since Mongoid doesn't know what type of data should be -allowed in the field, the field is created with a type of Object. It is a -good idea to explicitly define the field with the appropriate type. - - -.. _association-scope: - -Custom Scopes -------------- - -You may set a specific scope on an association using the ``:scope`` parameter. -The scope is an additional filter that restricts which objects are considered -to be a part of the association - a scoped association will return only -documents which satisfy the scope condition.. The scope may be either: - -- a ``Proc`` with arity zero, or -- a ``Symbol`` which references a :ref:`named scope ` on the - associated model. - -.. code-block:: ruby - - class Trainer - has_many :pets, scope: -> { where(species: 'dog') } - has_many :toys, scope: :rubber - end - - class Pet - belongs_to :trainer - end - - class Toy - scope :rubber, where(material: 'rubber') - belongs_to :trainer - end - -.. note:: - - It is possible to add documents that do not satisfy an association's scope - to that association. In this case, such documents will appear associated - in memory, and will be saved to the database, but will not be present when - the association is queried in the future. For example: - - .. code-block:: ruby - - trainer = Trainer.create! - dog = Pet.create!(trainer: trainer, species: 'dog') - cat = Pet.create!(trainer: trainer, species: 'cat') - - trainer.pets #=> [dog, cat] - - trainer.reload.pets #=> [dog] - -.. note:: - - Mongoid's syntax for scoped association differs from that of ActiveRecord. - Mongoid uses the ``:scope`` keyword argument for consistency with other - association options, whereas in ActiveRecord the scope is a positional - argument. - - -Validations ------------ - -It is important to note that by default, Mongoid will validate the children of any -association that are loaded into memory via a ``validates_associated``. The associations that -this applies to are: - -- ``embeds_many`` -- ``embeds_one`` -- ``has_many`` -- ``has_one`` -- ``has_and_belongs_to_many`` - -If you do not want this behavior, you may turn it off when defining the association. - -.. code-block:: ruby - - class Person - include Mongoid::Document - - embeds_many :addresses, validate: false - has_many :posts, validate: false - end - - -Polymorphism ------------- - -One to one and one to many associations support polymorphism, which is -having a single association potentially contain objects of different classes. -For example, we could model an organization in which departments and teams -have managers as follows: - -.. code-block:: ruby - - class Department - include Mongoid::Document - - has_one :manager, as: :unit - end - - class Team - include Mongoid::Document - - has_one :manager, as: :unit - end - - class Manager - include Mongoid::Document - - belongs_to :unit, polymorphic: true - end - - dept = Department.create! - team = Team.create! - - alice = Manager.create!(unit: dept) - alice.unit == dept - # => true - dept.manager == alice - # => true - -To provide another example, suppose we want to track price history for -products and bundles. This can be achieved via an embedded one to many -polymorphic association: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - field :name, type: String - has_and_belongs_to_many :bundles - - embeds_many :prices, as: :item - end - - class Bundle - include Mongoid::Document - - field :name, type: String - has_and_belongs_to_many :products - - embeds_many :prices, as: :item - end - - class Price - include Mongoid::Document - - embedded_in :item, polymorphic: true - end - - pants = Product.create!(name: 'Pants', - prices: [Price.new, Price.new]) - costume = Bundle.create!(name: 'Costume', products: [pants], - prices: [Price.new, Price.new]) - -To define a polymorphic association, specify the ``polymorphic: true`` option -on the child association and add the ``as: :association_name`` option to the -parent association. - -Note that Mongoid currently supports polymorphism only in one direction - from -the child to the parent. For example, polymorphism cannot be used to specify -that a bundle may contain other bundles or products: - -.. code-block:: ruby - - class Bundle - include Mongoid::Document - - # Does not work: - has_many :items, polymorphic: true - end - -``has_and_belongs_to_many`` associations do not support polymorphism. - - -Cascading Callbacks -------------------- - -If you want the embedded document callbacks to fire when calling a persistence -operation on its parent, you will need to provide the cascade callbacks option -to the association. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums, cascade_callbacks: true - embeds_one :label, cascade_callbacks: true - end - - band.save # Fires all save callbacks on the band, albums, and label. - - -.. _dependent-behavior: - -Dependent Behavior ------------------- - -You can provide dependent options to referenced associations to instruct Mongoid -how to handle situations where one side of the association is deleted, or is attempted -to be deleted. The options are as follows: - -- ``:delete_all``: Delete the child document(s) without running any of the model callbacks. -- ``:destroy``: Destroy the child document(s) and run all of the model callbacks. -- ``:nullify``: Set the foreign key field of the child document to nil. The child may become orphaned if it is ordinarily only referenced via the parent. -- ``:restrict_with_exception``: ``raise`` an error if the child is not empty. -- ``:restrict_with_error``: Cancel operation and return false if the child is not empty. - -If no ``:dependent`` option is provided, deleting the parent document leaves the child document unmodified -(in other words, the child document continues to reference the now deleted parent document via the foreign key field). -The child may become orphaned if it is ordinarily only referenced via the parent. - -.. code-block:: ruby - - class Band - include Mongoid::Document - has_many :albums, dependent: :delete_all - belongs_to :label, dependent: :nullify - end - - class Album - include Mongoid::Document - belongs_to :band - end - - class Label - include Mongoid::Document - has_many :bands, dependent: :restrict_with_exception - end - - label = Label.first - label.bands.push(Band.first) - label.delete # Raises an error since bands is not empty. - - Band.first.destroy # Will delete all associated albums. - - -Autosaving ----------- - -One core difference between Mongoid and ActiveRecord is that Mongoid does not -automatically save associated documents for referenced (i.e., non-embedded) -associations when the parent is saved, for performance reasons. - -If autosaving is not used, it is possible to create dangling references -to non-existent documents via associations: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :albums - end - - class Album - include Mongoid::Document - - belongs_to :band - end - - band = Band.new - album = Album.create!(band: band) - - # The band is not persisted at this point. - - album.reload - - album.band_id - # => BSON::ObjectId('6257699753aefe153121a3d5') - - # Band does not exist. - album.band - # => nil - -To make referenced associations save automatically when the parent is saved, -add the ``:autosave`` option to the association: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - has_many :albums - end - - class Album - include Mongoid::Document - - belongs_to :band, autosave: true - end - - band = Band.new - album = Album.create!(band: band) - - # The band is persisted at this point. - - album.reload - - album.band_id - # => BSON::ObjectId('62576b4b53aefe178b65b8e3') - - album.band - # => # - -The autosaving functionality is automatically added to an association when -using ``accepts_nested_attributes_for``, so that the application does not -need to track which associations were modified when processing a form -submission. - -Embedded associations always autosave, because they are stored as part of the -parent document. - -Some operations on associations always save the parent and the child documents -as part of the operation, regardless of whether autosaving is enabled. -A non-exhaustive list of these operations is as follows: - -- Assignment to the association: - - .. code-block:: ruby - - # Saves the band and the album. - band.albums = [Album.new] - -- ``push``, ``<<``: - - .. code-block:: ruby - - band.albums << Album.new - band.albums.push(Album.new) - - -Existence Predicates --------------------- - -All associations have existence predicates on them in the form of ``name?`` and ``has_name?`` -to check if the association is blank. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label - embeds_many :albums - end - - band.label? - band.has_label? - band.albums? - band.has_albums? - -Autobuilding ------------- - -One to one associations (``embeds_one``, ``has_one``) have an autobuild option which tells -Mongoid to instantiate a new document when the association is accessed and it is ``nil``. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_one :label, autobuild: true - has_one :producer, autobuild: true - end - - band = Band.new - band.label # Returns a new empty label. - band.producer # Returns a new empty producer. - -Touching --------- -Any ``belongs_to`` association can take an optional ``:touch`` option which -will cause the parent document to be touched whenever the child document is -updated: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name - belongs_to :label, touch: true - end - - band = Band.first - band.name = "The Rolling Stones" - band.save! # Calls touch on the parent label. - band.touch # Calls touch on the parent label. - -``:touch`` can also take a string or symbol argument specifying a field to -be touched on the parent association in addition to updated_at: - -.. code-block:: ruby - - class Label - include Mongoid::Document - include Mongoid::Timestamps - field :bands_updated_at, type: Time - has_many :bands - end - - class Band - include Mongoid::Document - belongs_to :label, touch: :bands_updated_at - end - - label = Label.create! - band = Band.create!(label: label) - - band.touch # Updates updated_at and bands_updated_at on the label. - -When an embedded document is touched, its parents are recursively touched -through the composition root (because all of the parents are necessarily saved -when the embedded document is saved). The ``:touch`` attribute therefore is -unnecessary on ``embedded_in`` associations. - -Mongoid currently `does not support specifying an additional field to be -touched on an embedded_in association `_. - -``:touch`` should not be set to ``false`` on an ``embedded_in`` association, -since composition hierarchy is always updated upon a touch of an embedded -document. This is currently not enforced but enforcement is `intended in the -future `_. - -The counter_cache Option ------------------------- - -As with ActiveRecord, the ``:counter_cache`` option can be used on an association -to make finding the number of belonging objects more efficient. Also similar -to ActiveRecord, you must take into account that there will be an extra -attribute on the associated model. This means that with Mongoid, -you need to include ``Mongoid::Attributes::Dynamic`` on the associated model. -For example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - belongs_to :customer, counter_cache: true - end - - class Customer - include Mongoid::Document - include Mongoid::Attributes::Dynamic - has_many :orders - end - -Association Proxies -------------------- - -Associations employ transparent proxies to the target objects. This can -cause surprising behavior in some situations. - -The method visibility may be lost when methods on association targets are -accessed, depending on the association: - -.. code-block:: ruby - - class Order - include Mongoid::Document - belongs_to :customer - - private - - def internal_status - 'new' - end - end - - class Customer - include Mongoid::Document - has_many :orders - - private - - def internal_id - 42 - end - end - - order = Order.new - customer = Customer.create!(orders: [order]) - - # has_many does not permit calling private methods on the target - customer.orders.first.internal_status - # NoMethodError (private method `internal_status' called for #) - - # belongs_to permits calling private methods on the target - order.customer.internal_id - # => 42 - -Association Metadata -==================== - -All associations in Mongoid contain metadata that holds information about the association in -question, and is a valuable tool for third party developers to use to extend Mongoid. - -You can access the association metadata of the association in a few different ways. - -.. code-block:: ruby - - # Get the metadata for a named association from the class or document. - Model.reflect_on_association(:association_name) - model.reflect_on_association(:association_name) - - # Get the metadata with a specific association itself on a specific - # document. - model.associations[:association_name] - -Attributes ----------- - -All associations contain a ``_target``, which is the proxied document or documents, a ``_base`` -which is the document the association hangs off, and ``_association`` which provides information -about the association. - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses - end - - person.addresses = [ address ] - person.addresses._target # returns [ address ] - person.addresses._base # returns person - person.addresses._association # returns the association metadata - -The Association Object ----------------------- - -The association object itself contains more information than one might know what to do -with, and is useful for developers of extensions to Mongoid. - - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Method - - Description - * - ``Association#as`` - - Returns the name of the parent to a polymorphic child. - * - ``Association#as?`` - - Returns whether or not an as option exists. - * - ``Association#autobuilding?`` - - Returns whether or not the association is autobuilding. - * - ``Association#autosaving?`` - - Returns whether or not the association is autosaving. - * - ``Association#cascading_callbacks?`` - - Returns whether the association has callbacks cascaded down from the parent. - * - ``Association#class_name`` - - Returns the class name of the proxied document. - * - ``Association#cyclic?`` - - Returns whether the association is a cyclic association. - * - ``Association#dependent`` - - Returns the association's dependent option. - * - ``Association#destructive?`` - - Returns true if the association has a dependent delete or destroy. - * - ``Association#embedded?`` - - Returns whether the association is embedded in another document. - * - ``Association#forced_nil_inverse?`` - - Returns whether the association has a nil inverse defined. - * - ``Association#foreign_key`` - - Returns the name of the foreign key field. - * - ``Association#foreign_key_check`` - - Returns the name of the foreign key field dirty check method. - * - ``Association#foreign_key_setter`` - - Returns the name of the foreign key field setter. - * - ``Association#indexed?`` - - Returns whether the foreign key is auto indexed. - * - ``Association#inverses`` - - Returns the names of all inverse association. - * - ``Association#inverse`` - - Returns the name of a single inverse association. - * - ``Association#inverse_class_name`` - - Returns the class name of the association on the inverse side. - * - ``Association#inverse_foreign_key`` - - Returns the name of the foreign key field on the inverse side. - * - ``Association#inverse_klass`` - - Returns the class of the association on the inverse side. - * - ``Association#inverse_association`` - - Returns the metadata of the association on the inverse side. - * - ``Association#inverse_of`` - - Returns the explicitly defined name of the inverse association. - * - ``Association#inverse_setter`` - - Returns the name of the method used to set the inverse. - * - ``Association#inverse_type`` - - Returns the name for the polymorphic type field of the inverse. - * - ``Association#inverse_type_setter`` - - Returns the name for the polymorphic type field setter of the inverse. - * - ``Association#key`` - - Returns the name of the field in the attributes hash to use to get the association. - * - ``Association#klass`` - - Returns the class of the proxied documents in the association. - * - ``Association#name`` - - Returns the association name. - * - ``Association#options`` - - Returns self, for API compatibility with ActiveRecord. - * - ``Association#order`` - - Returns the custom sorting options on the association. - * - ``Association#polymorphic?`` - - Returns whether the association is polymorphic. - * - ``Association#setter`` - - Returns the name of the field to set the association. - * - ``Association#store_as`` - - Returns the name of the attribute to store an embedded association in. - * - ``Association#touchable?`` - - Returns whether or not the association has a touch option. - * - ``Association#type`` - - Returns the name of the field to get the polymorphic type. - * - ``Association#type_setter`` - - Returns the name of the field to set the polymorphic type. - * - ``Association#validate?`` - - Returns whether the association has an associated validation. diff --git a/source/reference/callbacks.txt b/source/reference/callbacks.txt deleted file mode 100644 index 972451dd..00000000 --- a/source/reference/callbacks.txt +++ /dev/null @@ -1,120 +0,0 @@ -.. _callbacks: - -********* -Callbacks -********* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid implements many of the `ActiveRecord callbacks -`_. - - -Document Callbacks -================== - -Mongoid supports the following callbacks for :doc:`documents `: - -- ``after_initialize`` -- ``after_build`` -- ``before_validation`` -- ``after_validation`` -- ``before_create`` -- ``around_create`` -- ``after_create`` -- ``after_find`` -- ``before_update`` -- ``around_update`` -- ``after_update`` -- ``before_upsert`` -- ``around_upsert`` -- ``after_upsert`` -- ``before_save`` -- ``around_save`` -- ``after_save`` -- ``before_destroy`` -- ``around_destroy`` -- ``after_destroy`` - -Callbacks are available on any document, whether it is embedded within -another document or not. Note that to be efficient, Mongoid only invokes -the callback on the document that the persistence action was executed on. -This enables Mongoid to support large hierarchies and to handle optimized -atomic updates efficiently (without invoking callbacks throughout the document -hierarchy). - -Note that using callbacks for domain logic is a bad design practice, and can -lead to unexpected errors that are hard to debug when callbacks in -the chain halt execution. It is our recommendation to only use them -for cross-cutting concerns, like queueing up background jobs. - -.. code-block:: ruby - - class Article - include Mongoid::Document - field :name, type: String - field :body, type: String - field :slug, type: String - - before_create :send_message - - after_save do |document| - # Handle callback here. - end - - protected - def send_message - # Message sending code here. - end - end - -Callbacks are coming from Active Support, so you can use the new -syntax as well: - -.. code-block:: ruby - - class Article - include Mongoid::Document - field :name, type: String - - set_callback(:create, :before) do |document| - # Message sending code here. - end - end - - -Association Callbacks -===================== - -Mongoid has a set of callbacks that are specific to associations - these are: - -- ``after_add`` -- ``after_remove`` -- ``before_add`` -- ``before_remove`` - -Each time a document is added or removed from any of the following -associations, the respective callbacks are invoked: ``embeds_many``, -``has_many`` and ``has_and_belongs_to_many``. - -Association callbacks are specified as options on the respective association. -The document added/removed will be passed as the parameter to the specified -callback. Example: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - has_many :posts, after_add: :send_email_to_subscribers - end - - def send_email_to_subscribers(post) - Notifications.new_post(post).deliver - end diff --git a/source/reference/configuration.txt b/source/reference/configuration.txt deleted file mode 100644 index c948673d..00000000 --- a/source/reference/configuration.txt +++ /dev/null @@ -1,1027 +0,0 @@ -.. _configuration: - -************* -Configuration -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid is customarily configured through a ``mongoid.yml`` file that specifies -options and clients. The simplest configuration is as follows, which configures -Mongoid to talk to a MongoDB server at "localhost:27017" and use the database -named "mongoid". - -.. code-block:: yaml - - development: - clients: - default: - database: mongoid - hosts: - - localhost:27017 - -The top level key in the configuration file, ``development`` in the above -example, refers to the environment name which the application is executing in, -i.e. ``development``, ``test`` or ``production``. The third level key, -``default`` in the above example, refers to the Mongo client name. -Most applications will use a single client named ``default``. - - -Generating Default Configuration -================================ - -If you are using {+ror+}, you can have Mongoid generate a default -configuration file for you by running the following command: - -.. code-block:: bash - - rails g mongoid:config - -The configuration file will be placed in ``config/mongoid.yml``. An -initializer will also be created and placed in -``config/initializers/mongoid.rb``. It is recommended that all configuration -be specified in ``config/mongoid.yml``, but if you prefer, the ``mongoid.rb`` -initializer may also be used to set configuration options. Note, though, that -settings in ``mongoid.yml`` always take precedence over settings in the -initializer. - -If you are not using {+ror+}, you can copy the minimal configuration -given above and save it as ``config/mongoid.yml``. - - -Loading Mongoid Configuration -============================= - -If you are using {+ror+}, Mongoid configuration is automatically loaded -for the current environment as stored in ``Rails.env`` when the application -loads. - -You may need to configure the ORM for your application to be Mongoid by -adding the following to ``application.rb``: - -.. code-block:: ruby - - config.generators do |g| - g.orm :mongoid - end - -If you are not using {+ror+}, Mongoid configuration must be loaded -manually. This can be done via the ``Mongoid.load!`` method, which takes -the configuration file path as its argument, as follows: - -.. code-block:: ruby - - # Use automatically detected environment name - Mongoid.load!("path/to/your/mongoid.yml") - - # Specify environment name manually - Mongoid.load!("path/to/your/mongoid.yml", :production) - -When Mongoid is asked to automatically detect the environment name, -it does so by examining the following sources, in order: - -- If ``Rails`` top level constant is defined, ``Rails.env``. -- If ``Sinatra`` top level constant is defined, ``Sinatra::Base.environment``. -- The ``RACK_ENV`` environment variable. -- The ``MONGOID_ENV`` environment variable. - -It is also possible to configure Mongoid directly in Ruby, without using -a configuration file. This configuration style does not support the concept -of environments - whatever configuration is provided, it is applied to the -current environment - but it does support defining multiple clients. - -.. code-block:: ruby - - Mongoid.configure do |config| - config.clients.default = { - hosts: ['localhost:27017'], - database: 'my_db', - } - - config.log_level = :warn - end - -.. note:: - - Mongoid must be configured *before* any component of it is used or referenced. - Once a component is used or referenced, changing configuration may not apply - changes to already instantiated components. - - -.. _configuration-options: - -Mongoid Configuration Options -============================= - -The following annotated example ``mongoid.yml`` demonstrates how Mongoid -can be configured. - -Mongoid delegates to the Ruby driver for client configuration. Please review -`the driver documentation `_ -for details on driver options. - -.. code-block:: yaml - - development: - # Configure available database clients. (required) - clients: - # Defines the default client. (required) - default: - # Mongoid can connect to a URI accepted by the driver: - # uri: mongodb://user:password@mongodb.domain.com:27017/my_db_development - - # Otherwise define the parameters separately. - # This defines the name of the default database that Mongoid can connect to. - # (required). - database: my_db_development - # Provides the hosts the default client can connect to. Must be an array - # of host:port pairs. (required) - hosts: - - localhost:27017 - options: - # Note that all options listed below are Ruby driver client options (the mongo gem). - # Please refer to the driver documentation of the version of the mongo gem you are using - # for the most up-to-date list of options. - - # Change the default write concern. (default = { w: 1 }) - # write: - # w: 1 - - # Change the default read preference. Valid options for mode are: :secondary, - # :secondary_preferred, :primary, :primary_preferred, :nearest - # (default: primary) - # read: - # mode: :secondary_preferred - # tag_sets: - # - use: web - - # The name of the user for authentication. - # user: 'user' - - # The password of the user for authentication. - # password: 'password' - - # The user's database roles. - # roles: - # - 'dbOwner' - - # Change the default authentication mechanism. Valid options include: - # :scram, :scram256, :mongodb_cr, :mongodb_x509, :gssapi, :aws, :plain. - # MongoDB Server defaults to :scram, which will use "SCRAM-SHA-256" if available, - # otherwise fallback to "SCRAM-SHA-1" (:scram256 will always use "SCRAM-SHA-256".) - # This setting is handled by the MongoDB Ruby Driver. Please refer to: - # https://mongodb.com/docs/ruby-driver/current/reference/authentication/ - # auth_mech: :scram - - # The database or source to authenticate the user against. - # (default: the database specified above or admin) - # auth_source: admin - - # Force a the driver cluster to behave in a certain manner instead of auto- - # discovering. Can be one of: :direct, :replica_set, :sharded. Set to :direct - # when connecting to hidden members of a replica set. - # connect: :direct - - # Changes the default time in seconds the server monitors refresh their status - # via hello commands. (default: 10) - # heartbeat_frequency: 10 - - # The time in seconds for selecting servers for a near read preference. (default: 0.015) - # local_threshold: 0.015 - - # The timeout in seconds for selecting a server for an operation. (default: 30) - # server_selection_timeout: 30 - - # The maximum number of connections in the connection pool. (default: 5) - # max_pool_size: 5 - - # The minimum number of connections in the connection pool. (default: 1) - # min_pool_size: 1 - - # The time to wait, in seconds, in the connection pool for a connection - # to be checked in before timing out. (default: 5) - # wait_queue_timeout: 5 - - # The time to wait to establish a connection before timing out, in seconds. - # (default: 10) - # connect_timeout: 10 - - # How long to wait for a response for each operation sent to the - # server. This timeout should be set to a value larger than the - # processing time for the longest operation that will be executed - # by the application. Note that this is a client-side timeout; - # the server may continue executing an operation after the client - # aborts it with the SocketTimeout exception. - # (default: nil, meaning no timeout) - # socket_timeout: 5 - - # The name of the replica set to connect to. Servers provided as seeds that do - # not belong to this replica set will be ignored. - # replica_set: name - - # Compressors to use for wire protocol compression. (default is to not use compression) - # "zstd" requires zstd-ruby gem. "snappy" requires snappy gem. - # Refer to: https://www.mongodb.com/docs/ruby-driver/current/reference/create-client/#compression - # compressors: ["zstd", "snappy", "zlib"] - - # Whether to connect to the servers via ssl. (default: false) - # ssl: true - - # The certificate file used to identify the connection against MongoDB. - # ssl_cert: /path/to/my.cert - - # The private keyfile used to identify the connection against MongoDB. - # Note that even if the key is stored in the same file as the certificate, - # both need to be explicitly specified. - # ssl_key: /path/to/my.key - - # A passphrase for the private key. - # ssl_key_pass_phrase: password - - # Whether to do peer certification validation. (default: true) - # ssl_verify: true - - # The file containing concatenated certificate authority certificates - # used to validate certs passed from the other end of the connection. - # ssl_ca_cert: /path/to/ca.cert - - # Whether to truncate long log lines. (default: true) - # truncate_logs: true - - # Configure Mongoid-specific options. (optional) - options: - # Allow BSON::Decimal128 to be parsed and returned directly in - # field values. When BSON 5 is present and the this option is set to false - # (the default), BSON::Decimal128 values in the database will be returned - # as BigDecimal. - # - # @note this option only has effect when BSON 5+ is present. Otherwise, - # the setting is ignored. - # allow_bson5_decimal128: false - - # Application name that is printed to the MongoDB logs upon establishing - # a connection. Note that the name cannot exceed 128 bytes in length. - # It is also used as the database name if the database name is not - # explicitly defined. (default: nil) - # app_name: nil - - # When this flag is false, callbacks for embedded documents will not be - # called. This is the default in 9.0. - # - # Setting this flag to true restores the pre-9.0 behavior, where callbacks - # for embedded documents are called. This may lead to stack overflow errors - # if there are more than cicrca 1000 embedded documents in the root - # document's dependencies graph. - # See https://jira.mongodb.org/browse/MONGOID-5658 for more details. - # around_callbacks_for_embeds: false - - # Sets the async_query_executor for the application. By default the thread pool executor - # is set to `:immediate`. Options are: - # - # - :immediate - Initializes a single +Concurrent::ImmediateExecutor+ - # - :global_thread_pool - Initializes a single +Concurrent::ThreadPoolExecutor+ - # that uses the +async_query_concurrency+ for the +max_threads+ value. - # async_query_executor: :immediate - - # Mark belongs_to associations as required by default, so that saving a - # model with a missing belongs_to association will trigger a validation - # error. - # belongs_to_required_by_default: true - - # Set the global discriminator key. - # discriminator_key: "_type" - - # Raise an exception when a field is redefined. - # duplicate_fields_exception: false - - # Defines how many asynchronous queries can be executed concurrently. - # This option should be set only if `async_query_executor` is set - # to `:global_thread_pool`. - # global_executor_concurrency: nil - - # When this flag is true, any attempt to change the _id of a persisted - # document will raise an exception (`Errors::ImmutableAttribute`). - # This is the default in 9.0. Setting this flag to false restores the - # pre-9.0 behavior, where changing the _id of a persisted - # document might be ignored, or it might work, depending on the situation. - # immutable_ids: true - - # Include the root model name in json serialization. - # include_root_in_json: false - - # # Include the _type field in serialization. - # include_type_for_serialization: false - - # Whether to join nested persistence contexts for atomic operations - # to parent contexts by default. - # join_contexts: false - - # When this flag is false (the default as of Mongoid 9.0), a document that - # is created or loaded will remember the storage options that were active - # when it was loaded, and will use those same options by default when - # saving or reloading itself. - # - # When this flag is true you'll get pre-9.0 behavior, where a document will - # not remember the storage options from when it was loaded/created, and - # subsequent updates will need to explicitly set up those options each time. - # - # For example: - # - # record = Model.with(collection: 'other_collection') { Model.first } - # - # This will try to load the first document from 'other_collection' and - # instantiate it as a Model instance. Pre-9.0, the record object would - # not remember that it came from 'other_collection', and attempts to - # update it or reload it would fail unless you first remembered to - # explicitly specify the collection every time. - # - # As of Mongoid 9.0, the record will remember that it came from - # 'other_collection', and updates and reloads will automatically default - # to that collection, for that record object. - # legacy_persistence_context_behavior: false - - # When this flag is false, a document will become read-only only once the - # #readonly! method is called, and an error will be raised on attempting - # to save or update such documents, instead of just on delete. When this - # flag is true, a document is only read-only if it has been projected - # using #only or #without, and read-only documents will not be - # deletable/destroyable, but they will be savable/updatable. - # When this feature flag is turned on, the read-only state will be reset on - # reload, but when it is turned off, it won't be. - # legacy_readonly: false - - # The log level. - # - # It must be set prior to referencing clients or Mongo.logger, - # changes to this option are not be propagated to any clients and - # loggers that already exist. - # - # Additionally, only when the clients are configured via the - # configuration file is the log level given by this option honored. - # log_level: :info - - # Store BigDecimals as Decimal128s instead of strings in the db. - # map_big_decimal_to_decimal128: true - - # Preload all models in development, needed when models use inheritance. - # preload_models: false - - # When this flag is true, callbacks for every embedded document will be - # called only once, even if the embedded document is embedded in multiple - # documents in the root document's dependencies graph. - # This is the default in 9.0. Setting this flag to false restores the - # pre-9.0 behavior, where callbacks are called for every occurrence of an - # embedded document. The pre-9.0 behavior leads to a problem that for multi - # level nested documents callbacks are called multiple times. - # See https://jira.mongodb.org/browse/MONGOID-5542 - # prevent_multiple_calls_of_embedded_callbacks: true - - # Raise an error when performing a #find and the document is not found. - # raise_not_found_error: true - - # Raise an error when defining a scope with the same name as an - # existing method. - # scope_overwrite_exception: false - - # Return stored times as UTC. - # use_utc: false - - # Configure Driver-specific options. (optional) - driver_options: - # When this flag is off, an aggregation done on a view will be executed over - # the documents included in that view, instead of all documents in the - # collection. When this flag is on, the view filter is ignored. - # broken_view_aggregate: true - - # When this flag is set to false, the view options will be correctly - # propagated to readable methods. - # broken_view_options: true - - # When this flag is set to true, the update and replace methods will - # validate the parameters and raise an error if they are invalid. - # validate_update_replace: false - - -.. _load-defaults: - -Version Based Defaults -====================== - -Mongoid supports setting the configuration options to the defaults for specific -versions. This is useful for upgrading to a new Mongoid version. When upgrading -your Mongoid version, the following should be set on ``Mongoid::Config``: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults - end - -This way, when upgrading to a new version of Mongoid, your code will run with -the configuration options from the previous version of Mongoid. Then, -one-by-one, you can change the feature flags for the new version, and test that -your code still acts as expected. Once all of the new feature flags have been -accounted for, the call to ``load_defaults`` may be changed to take in the *new* -version, and all of the changed feature flags may be removed. - -For example, suppose we're upgrading from 7.5 to 8.0. Between these two versions, -two feature flags were added: ``legacy_attributes`` and ``map_big_decimal_to_decimal128``. -Before upgrading to Mongoid 8, add the following to your ``Mongoid::Config``: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 7.5 - end - -After upgrading to Mongoid 8.0 in your ``Gemfile``, any feature flags will -remain set to their 7.5 default behavior: ``legacy_attributes: true, -map_big_decimal_to_decimal128: false``. You may then flip these feature flags -one-by-one to their 8.0 behavior: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 7.5 - config.legacy_attributes = false - # config.map_big_decimal_to_decimal128 = true - end - -We recommend do these one at a time, so in the example above we leave the -second flag commented out. After verifying your code works as expected with the -``legacy_attributes`` flag turned off, the ``map_big_decimal_to_decimal128`` -setting can be uncommented. Once that functionality is verified as well, both -of those lines can be removed and the ``load_defaults`` replaced with: - -.. code:: ruby - - Mongoid.configure do |config| - config.load_defaults 8.0 - end - - -ERb Preprocessing -================= - -When loading a configuration file, Mongoid processes it with ERb before -parsing it as YAML. This allows, for example, constructing the contents of -the configuration file at runtime based on environment variables: - -.. code-block:: yaml - - development: - clients: - default: - uri: "<%= ENV['MONGODB_URI'] %>" - -.. note:: - - When outputting values from ERb, ensure the values are valid YAML and - escape them as needed. - -.. note:: - - Since ERb rendering is performed prior to YAML parsing, all ERb directives - in the configuration file are evaluated, including those occurring in YAML - comments. - -Logging -======= - -When configuring logging, it is important to keep in mind that Mongoid -provides a model layer on top of the MongoDB Ruby driver, and the driver -dispatches the CRUD operations to the MongoDB deployment. Therefore, some -of the logging output in an application using Mongoid comes from Mongoid -itself, and some comes from the driver. - -The Mongo client is a Ruby driver client instance, therefore -the logger of a Mongo client is the Ruby driver logger, not the Mongoid -logger. In other words: - -.. code-block:: ruby - - # Ruby driver logger, not Mongoid logger - Mongoid.client(:default).logger - -Depending on whether Mongoid is used in a {+ror+} application, and how -both Mongoid and Ruby driver are configured, they may use the same logger -instance or different instances, potentially with different configurations. - -In {+ror+} Application ----------------------------- - -When used in a {+ror+} application, Mongoid by default inherits -the logger and the log level from Rails, and sets the driver's logger -to the same logger instance: - -.. code-block:: ruby - - Rails.logger === Mongoid.logger - # => true - - Mongoid.logger === Mongo::Logger.logger - # => true - -To change the log level, use `standard Rails configuration -`_. -Place the following in one of environment configuration files, such as -``config/environments/production.rb``: - -.. code-block:: ruby - - Rails.application.configure do - config.log_level = :debug - end - -.. note:: - - The ``log_level`` Mongoid configuration option is not used when Mongoid operates - in a Rails application, because Mongoid inherits Rails' log level in this case. - -To configure either Mongoid or driver logger differently from the Rails logger, -use an initializer as follows: - -.. code-block:: ruby - - Rails.application.configure do - config.after_initialize do - # Change Mongoid log destination and/or level - Mongoid.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - - # Change driver log destination and/or level - Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - end - end - -.. note:: - - There is currently no provision in the Ruby standard library ``Logger`` - to return the log device (i.e. the ``IO`` object) that a logger is using. - To have, for example, Mongoid and/or the Ruby driver log to the - standard Rails log file (e.g. ``log/development.log``) but with a - different level from standard Rails logger (``Rails.logger``), the - file must be opened separately and the resulting ``IO`` object passed to - the ``Logger`` constructor. - -.. note:: - - Since by default Mongoid sets its own logger and the driver's logger to the - same instance as the Rails logger, modifying any of the instances affects - all of them. For example the following changes log level for all three - loggers, unless the application assigned a separate ``Logger`` instance - to ``Mongo::Logger.logger`` as described above: - - .. code-block:: ruby - - Mongoid::Logger.logger.level = Logger::DEBUG - -Standalone ----------- - -When not loaded in a {+ror+} application, Mongoid respects the -``log_level`` top level configuration option. It can be given in the -configuration file as follows: - -.. code-block:: yaml - - development: - clients: - default: - # ... - options: - log_level: :debug - -... or when configuring Mongoid inline: - -.. code-block:: ruby - - Mongoid.configure do |config| - config.log_level = :debug - end - -The default log destination in Mongoid 7.1 and higher is standard error. -The default log destination in Mongoid 7.0 and lower is standard output. -To change the log destination, create a new logger instance as follows: - -.. code-block:: ruby - - Mongoid.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - -To change the Ruby driver log level or destination: - -.. code-block:: ruby - - Mongo::Logger.logger = Logger.new(STDERR).tap do |logger| - logger.level = Logger::DEBUG - end - -To set the driver logger to be the same as the Mongoid logger: - -.. code-block:: ruby - - Mongo::Logger.logger = Mongoid.logger - -.. note:: - - Mongoid does not alter the driver's logger when running in - standalone mode. - -.. _time-zones: - -Time Zones -========== - -Mongoid uses ActiveSupport's time zone functionality, which is far -more robust than Ruby's standard library. Importantly, ActiveSupport -allows configuration of ``Time.zone``, a thread-global variable which -provides context for working with date and time values. - -While a thorough treatment of time zones in Ruby is outside the scope -of this tutorial, the easiest and most reliable way of achieving correct -time zone handling is as follows: - -1. Set the operating system's time zone to UTC. For example, on Linux: - -.. code-block:: bash - - cp /usr/share/zoneinfo/UTC /etc/localtime - -2. Set your application default time zone to UTC: - -.. code-block:: ruby - - # If using Rails, in application.rb: - class Application < Rails::Application - config.time_zone = 'UTC' - end - - # If not using Rails: - Time.zone = 'UTC' - -3. In each controller and job class, set the appropriate time zone in a - ``before_filter`` at the earliest possible stage. As an example, - if each user of your application can set their own time zone, - you may wish to do: - -.. code-block:: ruby - - class ApplicationController < ActionController::Base - before_filter :fetch_user, - :set_time_zone - - def set_time_zone - Time.zone = @user.time_zone - end - end - -4. From here, you may work with times naturally in the local time zone. - For example, in a view: - -.. code-block:: ruby - - Turned into a pumpkin after <%= cinderella.updated_at.seconds_after_midnight %> seconds! - -5. Use ActiveSupport methods instead of the Ruby standard library. - - - ``Time.zone.now`` or ``Time.current` instead of ``Time.now`` - - ``Date.current`` instead of ``Date.today`` - - Critically, note that the latter Ruby standard library methods reference - your system time zone (e.g. UTC) and not the value of ``Time.zone``. - As it is very easy to mistake these similarly named methods, we recommend to - use `Rubocop's Rails/TimeZone cop - `_ in your CI. - -Setting time zone on data loaded from MongoDB ---------------------------------------------- - -MongoDB stores all times in UTC without time zone information. -Mongoid models load and returns time values as instances of -``ActiveSupport::TimeWithZone``. You may set the ``use_utc`` option -to control how Mongoid sets the time zone when loading from the database: - -- If false (default), Mongoid will use ``Time.zone`` to set the time - zone of time values are loaded from database. -- If true, Mongoid will always set the time zone as UTC on loaded - time values. - -``use_utc`` only affects how data is loaded, and does not affect -how data is persisted. For example, if you assign a ``Time`` or -``ActiveSupport::TimeWithZone`` instance to a time field, the time -zone information of the assigned instance always will be used -irrespective of the ``use_utc`` setting. Alternatively, if you -assign a string value to a time field, any time zone information -in the string will be used if present. If the string does not include -time zone information it will be parsed according to ``Time.zone``. -To illustrate: - -.. code-block:: ruby - - Time.use_zone("Asia/Kolkata") do - - # String does not include time zone, so "Asia/Kolkata" will be used - ghandi.born_at = "1869-10-02 7:10 PM" - - # Time zone in string (-0600) will be used - amelia.born_at = "1897-07-24 11:30 -0600" - end - - -Configuring ``SSLContext`` -========================== -It may be desirable to further configure TLS options in your application, for -example by enabling or disabling certain ciphers. - -This can be done by setting TLS context hooks on the Ruby driver -- TLS context -hooks are user-provided ``Proc``\(s) that will be invoked before any TLS socket -connection in the driver and can be used to modify the underlying -``OpenSSL::SSL::SSLContext`` object used by the socket. - -To set TLS context hooks, add ``Proc``\(s) to the ``Mongo.tls_context_hooks`` -array. This can be done in an initializer. The example below adds a hook -that only enables the "AES256-SHA" cipher. - -.. code-block:: ruby - - Mongo.tls_context_hooks.push( - Proc.new { |context| - context.ciphers = ["AES256-SHA"] - } - ) - - # Only the AES256-SHA cipher will be enabled from this point forward - -Every ``Proc`` in ``Mongo.tls_context_hooks`` will be passed an -``OpenSSL::SSL::SSLContext`` object as its sole argument. These procs will -be executed sequentially during socket creation. - -.. warning:: - - TLS context hooks are global and will affect all ``Mongo::Client`` instances - in an application. - -For more information about TLS context hooks, including best practices for -assigning and removing them, see `the Ruby driver documentation `_. - - -Network Compression -=================== - -Mongoid supports compression of messages to and from MongoDB servers. This functionality is provided by -the Ruby driver, which implements the three algorithms that are supported by MongoDB servers: - -- `Snappy `_: ``snappy`` compression - can be used when connecting to MongoDB servers starting with the 3.4 release, - and requires the `snappy `_ library to be - installed. -- `Zlib `_: ``zlib`` compression can be used when - connecting to MongoDB servers starting with the 3.6 release. -- `Zstandard `_: ``zstd`` compression can be - used when connecting to MongoDB servers starting with the 4.2 release, and - requires the `zstd-ruby `_ library to - be installed. - -To use wire protocol compression, configure the Ruby driver options within ``mongoid.yml``: - -.. code-block:: yaml - - development: - # Configure available database clients. (required) - clients: - # Define the default client. (required) - default: - # ... - options: - # These options are Ruby driver options, documented in - # https://mongodb.com/docs/ruby-driver/current/reference/create-client/ - # ... - # Compressors to use. (default is to not use compression) - # Valid values are zstd, zlib or snappy - or any combination of the three - compressors: ["zstd", "snappy"] - -If no compressors are explicitly requested, the driver will not use compression, -even if the required dependencies for one or more compressors are present on the -system. - -The driver chooses the first compressor of the ones requested that is also supported -by the server. The ``zstd`` compressor is recommended as it produces the highest -compression at the same CPU consumption compared to the other compressors. - -For maximum server compatibility all three compressors can be specified, e.g. -as ``compressors: ["zstd", "snappy", "zlib"]``. - - -Client-Side Encryption -====================== - -When loading the configuration file, Mongoid permits the file to contain -``BSON::Binary`` instances which are used for specifying ``keyId`` in -the schema map for `client-side encryption -`_, -as the following example shows: - -.. code-block:: yaml - - development: - clients: - default: - database: blog_development - hosts: [localhost:27017] - options: - auto_encryption_options: - key_vault_namespace: 'keyvault.datakeys' - kms_providers: - local: - key: "z7iYiYKLuYymEWtk4kfny1ESBwwFdA58qMqff96A8ghiOcIK75lJGPUIocku8LOFjQuEgeIP4xlln3s7r93FV9J5sAE7zg8U" - schema_map: - blog_development.comments: - properties: - message: - encrypt: - keyId: - - !ruby/object:BSON::Binary - data: !binary |- - R/AgNcxASFiiJWKXqWGo5w== - type: :uuid - bsonType: "string" - algorithm: "AEAD_AES_256_CBC_HMAC_SHA_512-Deterministic" - bsonType: "object" - - -Usage with Forking Servers -========================== - -When using Mongoid with a forking web server such as Puma, or any application -that otherwise forks to spawn child processes, special considerations apply. - -If possible, we recommend to not perform any MongoDB operations in the parent -process prior to forking, which will avoid any forking-related pitfalls. - -A detailed technical explanation of how the Mongo Ruby Driver handles forking -is given in the `driver's "Usage with Forking Servers" documentation -`. -In a nutshell, to avoid various connection errors such as ``Mongo::Error::SocketError`` -and ``Mongo::Error::NoServerAvailable``, you must do the following: - -1. Disconnect MongoDB clients in the parent Ruby process immediately *before* - forking using ``Mongoid.disconnect_clients``. This ensures the parent and child - process do not accidentally reuse the same sockets and have I/O conflicts. - Note that ``Mongoid.disconnect_clients`` does not disrupt any in-flight - MongoDB operations, and will automatically reconnect when you perform new - operations. -2. Reconnect your MongoDB clients in the child Ruby process immediately *after* - forking using ``Mongoid.reconnect_clients``. This is required to respawn - the driver's monitoring threads in the child process. - -Most web servers provide hooks that can be used by applications to -perform actions when the worker processes are forked. The following -are configuration examples for several common Ruby web servers. - -Puma ----- - -Use the ``on_worker_boot`` hook to reconnect clients in the workers and -the ``before_fork`` and ``on_refork`` hooks to close clients in the -parent process (`Puma documentation `_). - -.. code-block:: ruby - - # config/puma.rb - - # Runs in the Puma master process before it forks a child worker. - before_fork do - Mongoid.disconnect_clients - end - - # Required when using Puma's fork_worker option. Runs in the - # child worker 0 process before it forks grandchild workers. - on_refork do - Mongoid.disconnect_clients - end - - # Runs in each Puma child process after it forks from its parent. - on_worker_boot do - Mongoid.reconnect_clients - end - -Unicorn -------- - -Use the ``after_fork`` hook to reconnect clients in the workers and -the ``before_fork`` hook to close clients in the parent process -(`Unicorn documentation `_): - -.. code-block:: ruby - - # config/unicorn.rb - - before_fork do |_server, _worker| - Mongoid.disconnect_clients - end - - after_fork do |_server, _worker| - Mongoid.reconnect_clients - end - -Passenger ---------- - -Use the ``starting_worker_process`` hook to reconnect clients in the workers -(`Passenger documentation -`_). -Passenger does not appear to have a hook that is invoked in the parent process -before the workers are forked. - -.. code-block:: ruby - - if defined?(PhusionPassenger) - PhusionPassenger.on_event(:starting_worker_process) do |forked| - Mongoid.reconnect_clients if forked - end - end - - -.. _query-cache-middleware: - -Query Cache Middleware -====================== - -Enabling Query Cache for Rack Web Requests ------------------------------------------- - -The MongoDB Ruby Driver provides a Rack middleware which enables the :ref:`Query Cache -` for the duration of each web request. Below is an example of -how to enable the Query Cache Middleware in a {+ror+} application: - -.. code-block:: ruby - - # config/application.rb - - # Add Mongo::QueryCache::Middleware at the bottom of the middleware stack - # or before other middleware that queries MongoDB. - config.middleware.use Mongo::QueryCache::Middleware - -Please refer to the `Rails on Rack guide -`_ -for more information about using Rack middleware in Rails applications. - -Enabling Query Cache for ActiveJob ----------------------------------- - -The MongoDB Ruby Driver also provides Query Cache middleware for ActiveJob. -You may enable it for all jobs in an initializer: - -.. code-block:: ruby - - # config/initializers/active_job.rb - - # Enable Mongo driver query cache for ActiveJob - ActiveSupport.on_load(:active_job) do - include Mongo::QueryCache::Middleware::ActiveJob - end - -Or for a specific job class: - -.. code-block:: ruby - - class MyJob < ActiveJob::Base - include Mongo::QueryCache::Middleware::ActiveJob - end - - -Development Configuration -========================= - -Driver's default configuration is suitable for production deployment. -In development, some settings can be adjusted to provide a better developer -experience. - -- ``:server_selection_timeout``: set this to a low value (e.g., ``1``) - if your MongoDB server is running locally and you start it manually. A low - server selection timeout will cause the driver to fail quickly when there is - no server running. - -Sample recommended development configuration: - -.. code-block:: yaml - - development: - clients: - default: - database: mongoid - hosts: - - localhost:27017 - options: - server_selection_timeout: 1 diff --git a/source/reference/crud.txt b/source/reference/crud.txt deleted file mode 100644 index 33d2b077..00000000 --- a/source/reference/crud.txt +++ /dev/null @@ -1,1074 +0,0 @@ -.. _crud: - -*************** -CRUD Operations -*************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Saving Documents -================ - -Mongoid supports all expected CRUD operations for those familiar with other -Ruby mappers like Active Record or Data Mapper. What distinguishes Mongoid -from other mappers for MongoDB is that the general persistence operations -perform atomic updates on only the fields that have changed instead of -writing the entire document to the database each time. - -The persistence sections will provide examples on what database operation is -performed when executing the documented command. - -Standard --------- - -Mongoid's standard persistence methods come in the form of common methods you -would find in other mapping frameworks. The following table shows all standard -operations with examples. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Model#attributes`` - - *Returns the document's attributes as a ``Hash`` with string keys, and - its values in Mongoized form (i.e. the way they are stored in the db).* - - *The attributes hash also contains the attributes of all embedded - documents, as well as their embedded documents, etc. If an embedded - association is empty, its key will not show up in the returned hash.* - - - - .. code-block:: ruby - - person = Person.new(first_name: "Heinrich", last_name: "Heine") - - person.attributes - # => { "_id" => BSON::ObjectId('633467d03282a43784c2d56e'), "first_name" => "Heinrich", "last_name" => "Heine" } - - * - ``Model.create!`` - - *Insert a document or multiple documents into the database, raising an - error if a validation or server error occurs.* - - *Pass a hash of attributes to create one document with the specified - attributes, or an array of hashes to create multiple documents. - If a single hash is passed, the corresponding document is returned. - If an array of hashes is passed, an array of documents corresponding - to the hashes is returned.* - - *If a block is given to* ``create!`` *, it will be invoked with each - document as the argument in turn prior to attempting to save that - document.* - - *If there is a problem saving any of the documents, such as - a validation error or a server error, an exception is raised - and, consequently, none of the documents are returned. - However, if an array of hashes was passed and previous documents were - successfully saved, those documents will remain in the database.* - - - .. code-block:: ruby - - Person.create!( - first_name: "Heinrich", - last_name: "Heine" - ) # => Person instance - - Person.create!([ - { first_name: "Heinrich", last_name: "Heine" }, - { first_name: "Willy", last_name: "Brandt" } - ]) # => Array of two Person instances - - Person.create!(first_name: "Heinrich") do |doc| - doc.last_name = "Heine" - end # => Person instance - - * - ``Model.create`` - - *Instantiate a document or multiple documents and, if validations pass, - insert them into the database.* - - ``create`` *is similar to* ``create!`` *but does not raise - exceptions on validation errors. It still raises errors on server - errors, such as trying to insert a document with an* ``_id`` *that - already exists in the collection.* - - *If any validation errors are encountered, the respective document - is not inserted but is returned along with documents that were inserted. - Use* ``persisted?`` *,* ``new_record?`` *or* ``errors`` *methods - to check which of the returned documents were inserted into the - database.* - - - .. code-block:: ruby - - Person.create( - first_name: "Heinrich", - last_name: "Heine" - ) # => Person instance - - Person.create([ - { first_name: "Heinrich", last_name: "Heine" }, - { first_name: "Willy", last_name: "Brandt" } - ]) # => Array of two Person instances - - Person.create(first_name: "Heinrich") do |doc| - doc.last_name = "Heine" - end # => Person instance - - class Post - include Mongoid::Document - - validates_uniqueness_of :title - end - - posts = Post.create([{title: "test"}, {title: "test"}]) - # => array of two Post instances - posts.map { |post| post.persisted? } # => [true, false] - - * - ``Model#save!`` - - *Save the changed attributes to the database atomically, or insert the document if - new. Raises an exception if validations fail or there is a server error.* - - *Returns true if the changed attributes were saved, raises an exception otherwise.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.save! - - person.first_name = "Christian Johan" - person.save! - - * - ``Model#save`` - - *Save the changed attributes to the database atomically, or insert the document - if new.* - - *Returns true if the changed attributes were saved. Returns false - if there were any validation errors. Raises an exception if - the document passed validation but there was a server error during - the save.* - - *Pass* ``validate: false`` *option to bypass validations.* - - *Pass* ``touch: false`` *option to ignore the updates to the updated_at - field. If the document being save has not been previously persisted, - this option is ignored and the created_at and updated_at fields will be - updated with the current time.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.save - person.save(validate: false) - person.save(touch: false) - - person.first_name = "Christian Johan" - person.save - - * - ``Model#update_attributes`` - - *Update the document attributes in the database. Will return true if validation passed, - false if not.* - - - .. code-block:: ruby - - person.update_attributes( - first_name: "Jean", - last_name: "Zorg" - ) - - * - ``Model#update_attributes!`` - - *Update the document attributes in the database and raise an error if validation failed.* - - - .. code-block:: ruby - - person.update_attributes!( - first_name: "Leo", - last_name: "Tolstoy" - ) - - * - ``Model#update_attribute`` - - *Update a single attribute, bypassing validations.* - - - .. code-block:: ruby - - person.update_attribute(:first_name, "Jean") - - * - ``Model#upsert`` - - *Performs a MongoDB replace with upsert on the document. If the document - exists in the database and the* ``:replace`` *option is set to true, it - will get overwritten with the current document in the application (any - attributes present in the database but not in the application's document - instance will be lost). If the* ``:replace`` *option is false (default), - the document will be updated, and any attributes not in the application's - document will be maintained. - If the document does not exist in the database, it will be inserted. - Note that this only runs the* ``{before|after|around}_upsert`` *callbacks.* - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.upsert - person.upsert(replace: true) - - * - ``Model#touch`` - - *Update the document's updated_at timestamp, optionally with one extra - provided time field. This will cascade the touch to all* - ``belongs_to`` *associations of the document with the option set. - This operation skips validations and callbacks.* - - *Attempting to touch a destroyed document will raise* ``FrozenError``, - *same as if attempting to update an attribute on a destroyed - document.* - - - .. code-block:: ruby - - person.touch - person.touch(:audited_at) - - * - ``Model#delete`` - - *Deletes the document from the database without running callbacks.* - - *If the document is not persisted, Mongoid will attempt to delete from - the database any document with the same* ``_id``. - - - .. code-block:: ruby - - person.delete - - person = Person.create!(...) - unsaved_person = Person.new(id: person.id) - unsaved_person.delete - person.reload - # raises Mongoid::Errors::DocumentNotFound because the person was deleted - - * - ``Model#destroy`` - - *Deletes the document from the database while running destroy callbacks.* - - *If the document is not persisted, Mongoid will attempt to delete from - the database any document with the same* ``_id``. - - - .. code-block:: ruby - - person.destroy - - person = Person.create!(...) - unsaved_person = Person.new(id: person.id) - unsaved_person.destroy - person.reload - # raises Mongoid::Errors::DocumentNotFound because the person was deleted - - * - ``Model.delete_all`` - - *Deletes all documents from the database without running any callbacks.* - - - .. code-block:: ruby - - Person.delete_all - - * - ``Model.destroy_all`` - - *Deletes all documents from the database while running callbacks. This is a - potentially expensive operation since all documents will be loaded into memory.* - - - .. code-block:: ruby - - Person.destroy_all - -Mongoid provides the following persistence-related attributes: - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Attribute - - Example - - - * - ``Model#new_record?`` - - *Returns* ``true`` *if the model instance has not yet been saved - to the database. Opposite of* ``persisted?`` - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.new_record? # => true - person.save! - person.new_record? # => false - - * - ``Model#persisted?`` - - *Returns* ``true`` *if the model instance has been saved - to the database. Opposite of* ``new_record?`` - - - .. code-block:: ruby - - person = Person.new( - first_name: "Heinrich", - last_name: "Heine" - ) - person.persisted? # => false - person.save! - person.persisted? # => true - - -Atomic ------- - -Mongoid exposes :manual:`MongoDB update operators ` -as methods on Mongoid documents. When these methods are used, callbacks are -not invoked and validations are not performed. The supported update operators -are: - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Model#add_to_set`` - - *Performs an atomic $addToSet on the field.* - - - .. code-block:: ruby - - person.add_to_set(aliases: "Bond") - - * - ``Model#bit`` - - *Performs an atomic $bit on the field.* - - - .. code-block:: ruby - - person.bit(age: { and: 10, or: 12 }) - - * - ``Model#inc`` - - *Performs an atomic $inc on the field.* - - - .. code-block:: ruby - - person.inc(age: 1) - - * - ``Model#pop`` - - *Performs an atomic $pop on the field.* - - - .. code-block:: ruby - - person.pop(aliases: 1) - - * - ``Model#pull`` - - *Performs an atomic $pull on the field.* - - - .. code-block:: ruby - - person.pull(aliases: "Bond") - - * - ``Model#pull_all`` - - *Performs an atomic $pullAll on the field.* - - - .. code-block:: ruby - - person.pull_all(aliases: [ "Bond", "James" ]) - - * - ``Model#push`` - - *Performs an atomic $push on the field.* - - - .. code-block:: ruby - - person.push(aliases: ["007","008"]) - - * - ``Model#rename`` - - *Performs an atomic $rename on the field.* - - - .. code-block:: ruby - - person.rename(bday: :dob) - - * - ``Model#set`` - - *Updates an attribute on the model instance and, if the instance - is already persisted, performs an atomic $set on the field, bypassing - validations.* - - ``set`` *can also deeply set values on Hash fields.* - - ``set`` *can also deeply set values on* ``embeds_one`` *associations. - If such an association's document is nil, one will be created prior - to the update.* - - ``set`` *should not be used with* ``has_one`` *associations, as it - does not correctly work in such cases.* - - - - .. code-block:: ruby - - person = Person.create!(name: "Ricky Bobby") - person.set(name: "Tyler Durden") # updates name in the database - - - person = Person.new - person.set(name: "Tyler Durden") # does not write to database - person.name # => "Tyler Durden" - person.persisted? # => true - - - class Post - include Mongoid::Document - - field :metadata, type: Hash - end - - post = Post.create! - post.set('metadata.published_at' => Time.now) - post.metadata['published_at'] # => Time instance - - post.set('metadata.approved.today' => true) - post.metadata['approved'] # => {'today' => true} - - - class Flight - include Mongoid::Document - - embeds_one :plan - end - - class Plan - include Mongoid::Document - - embedded_in :flight - - field :route, type: String - end - - flight = Flight.create! - flight.plan # => nil - flight.set('plan.route', 'test route') - flight.plan # => Plan instance - flight.plan.route # => "test route" - - - * - ``Model#unset`` - - *Performs an atomic $unset on the field.* - - - .. code-block:: ruby - - person.unset(:name) - -Note that, because these methods skip validations, it is possible to both -save invalid documents into the database and end up with invalid documents -in the application (which would subsequently fail to save via a ``save`` -call due to the failing validations). - - -.. _atomic-operation-grouping: - -Atomic Operation Grouping -````````````````````````` - -Atomic operations may be grouped together using the ``#atomically`` method -on a document. All operations inside the block given to ``#atomically`` -are sent to the cluster in a single atomic command. For example: - -.. code-block:: ruby - - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - end - -``#atomically`` blocks may be nested. The default behavior is to write -changes performed by each block as soon as the block ends: - -.. code-block:: ruby - - person.atomically do - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - end - raise 'An exception' - # name and age changes are still persisted - end - -This behavior can be changed by specifying the ``join_context: true`` option -to ``#atomically``, or globally by setting the ``join_contexts`` -:ref:`configuration option ` to ``true``. When -context joining is enabled, nested ``#atomically`` blocks are joined with -the outer blocks, and only the outermost block (or the first block where -``join_contexts`` is false) actually writes changes to the cluster. -For example: - -.. code-block:: ruby - - person.atomically do - person.atomically(join_context: true) do - person.inc(age: 1) - person.set(name: 'Jake') - end - raise 'An exception' - # name and age changes are not persisted - end - -The context joining behavior can be enabled globally by default by setting -``join_context`` option in Mongoid configuration. In this case specifying -``join_context: false`` on an ``#atomically`` block can be used to -obtain the independent persistence context behavior. - -If an exception is raised in an ``#atomically`` block which has not yet -persisted its changes to the cluster, any pending attribute changes on -Mongoid models are reverted. For example: - -.. code-block:: ruby - - person = Person.new(name: 'Tom') - begin - person.atomically do - person.inc(age: 1) - person.set(name: 'Jake') - person.name # => 'Jake' - raise 'An exception' - end - rescue Exception - person.name # => 'Tom' - end - -Atomic operations described in this section apply to one document at a time, -therefore nesting ``#atomically`` blocks invoked on multiple documents does -not make changes to the different documents be persisted atomically together. -However, MongoDB offers :ref:`multi-document transactions ` -as of server version 4.0 which provide atomic persistence across multiple -documents. - - -Reloading -========= - -Use the ``reload`` method to fetch the most recent version of a document from -the database. Any unsaved modifications to the document's attributes are lost: - -.. code-block:: ruby - - band = Band.create!(name: 'foo') - # => # - - band.name = 'bar' - band - # => # - - band.reload - # => # - -When a document is reloaded, all of its embedded associations are also reloaded -in the same query (since embedded documents are stored in the parent document -on the server). If a document has referenced associations, the loaded -associations' are not reloaded but their values are cleared, such that these -associations would be loaded from the database at the next access. - -.. note:: - - Some operations on associations, for example assignment, persists the new - document. In these cases there may not be any unsaved modifications to - revert by reloading. In the following example, the assignment of the - empty array to the association is immediately persisted and reloading - does not make any changes to the document: - - .. code-block:: ruby - - # Assuming band has many tours, which could be referenced: - band = Band.create!(tours: [Tour.create!]) - # ... or embedded: - band = Band.create!(tours: [Tour.new]) - - # This writes the empty tour list into the database. - band.tours = [] - - # There are no unsaved modifications in band at this point to be reverted. - band.reload - - # Returns the empty array since this is what is in the database. - band.tours - # => [] - -If the model has a :ref:`shard key ` defined, the shard key value -is included in the reloading query. - -If the database does not contain a matching document, Mongoid normally raises -``Mongoid::Errors::DocumentNotFound``. However, if the configuration option -``raise_not_found_error`` is set to ``false``, and the database does not -contain a matching document, Mongoid replaces the current document with a newly -created document whose attributes are set to default values. Importantly, this -generally causes the ``_id`` of the document to change, as the following -example demonstrates: - -.. code-block:: ruby - - band = Band.create! - # => # - - Mongoid.raise_not_found_error = false - band.destroy - - band.reload - # => # - -For this reason, it is not recommended to use ``reload`` when -``raise_not_found_error`` is set to ``false``. - - -Reloading Unsaved Documents ---------------------------- - -``reload`` can be called when the document has not yet been persisted. -In this case ``reload`` performs a ``find`` query using the ``id`` value -specified in the document (and the shard key value, if a shard key is defined): - -.. code-block:: ruby - - existing = Band.create!(name: 'Photek') - - # Unsaved document - band = Band.new(id: existing.id) - band.reload - band.name - # => "Photek" - - -Accessing Field Values -====================== - -Mongoid provides several ways of accessing field values. - -.. note:: - - All of the access methods described below raise - ``Mongoid::Errors::AttributeNotLoaded`` when the field being accessed is - :ref:`projected out `, either by virtue of not being included in - :ref:`only ` or by virtue of being included in - :ref:`without `. This applies to both reads and writes. - - -Getters & Setters ------------------ - -The recommended way is to use the getter and setter methods generated for -each declared field: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name - end - - person = Person.new - - person.first_name = "Artem" - person.first_name - # => "Artem" - -To use this mechanism, each field must be explicitly declared, or the -model class must enable :ref:`dynamic fields `. - - -Custom Getters & Setters ------------------------- - -It is possible to explicitly define the getter and setter methods to provide -custom behavior when reading or writing fields, for example value -transformations or storing values under different field names. In this case -``read_attribute`` and ``write_attribute`` methods can be used to read and -write the values directly into the attributes hash: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - def first_name - read_attribute(:fn) - end - - def first_name=(value) - write_attribute(:fn, value) - end - end - - person = Person.new - - person.first_name = "Artem" - person.first_name - # => "Artem" - - person.attributes - # => {"_id"=>BSON::ObjectId('606477dc2c97a628cf47075b'), "fn"=>"Artem"} - -.. note:: - - The custom setters are called during the assignment of - :ref:`nested attributes `, however they are called before - the associations are set up. Because of this, associations may not always - be available during these methods, and it is encouraged to include checks - for their presence whenever referring to them. :ref:`Callbacks ` - can also be used to perform operations on certain events, and associations - will have already been setup and are available during their execution. - -.. _read-write-attribute: - -``read_attribute`` & ``write_attribute`` ----------------------------------------- - -The ``read_attribute`` and ``write_attribute`` methods can be used explicitly -as well. Note that if a field specifies its :ref:`storage field name -`, both ``read_attribute`` and ``write_attribute`` -accept either the declared field name or the storage field name for operations: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name, as: :fn - field :last_name, as: :ln - end - - person = Person.new(first_name: "Artem") - # => # - - person.read_attribute(:first_name) - # => "Artem" - - person.read_attribute(:fn) - # => "Artem" - - person.write_attribute(:last_name, "Pushkin") - person - # => # - - person.write_attribute(:ln, "Medvedev") - person - # => # - -``read_attribute`` and ``write_attribute`` do not require that a field with -the used name is defined, but writing field values with ``write_attribute`` -does not cause the respective field to be defined either: - -.. code-block:: ruby - - person.write_attribute(:undefined, "Hello") - person - # => # - person.attributes - # => {"_id"=>BSON::ObjectId('60647b212c97a6292c195b4c'), "first_name"=>"Artem", "last_name"=>"Medvedev", "undefined"=>"Hello"} - - person.read_attribute(:undefined) - # => "Hello" - person.undefined - # raises NoMethodError - -When ``read_attribute`` is used to access a missing field, it returns ``nil``. - - -Hash Access ------------ - -Mongoid model instances define the ``[]`` and ``[]=`` methods to provide -``Hash`` style access to the attributes. ``[]`` is an alias for -``read_attribute`` and ``[]=`` is an alias for ``write_attribute``; see -the section on :ref:`read_attribute and write_attribute ` -for the detailed description of their behavior. - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :first_name, as: :fn - field :last_name, as: :ln - end - - person = Person.new(first_name: "Artem") - - person["fn"] - # => "Artem" - - person[:first_name] - # => "Artem" - - person[:ln] = "Medvedev" - person - # => # - - person["last_name"] = "Pushkin" - person - # => # - - -Bulk Attribute Writes ---------------------- - -In cases where you want to set multiple field values at once, there are a few -different ways of accomplishing this as well. - -.. code-block:: ruby - - # Get the field values as a hash. - person.attributes - - # Set the field values in the document. - Person.new(first_name: "Jean-Baptiste", middle_name: "Emmanuel") - person.attributes = { first_name: "Jean-Baptiste", middle_name: "Emmanuel" } - person.write_attributes( - first_name: "Jean-Baptiste", - middle_name: "Emmanuel", - ) - - -Dirty Tracking -============== - -Mongoid supports tracking of changed or "dirty" fields with an API that mirrors that of -Active Model. If a defined field has been modified in a model the model will be marked as -dirty and some additional behavior comes into play. - - -Viewing Changes ---------------- - -There are various ways to view what has been altered on a model. Changes are recorded -from the time a document is instantiated, either as a new document or via loading from -the database up to the time it is saved. Any persistence operation clears the changes. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :name, type: String - end - - person = Person.first - person.name = "Alan Garner" - - # Check to see if the document has changed. - person.changed? # true - - # Get an array of the names of the changed fields. - person.changed # [ :name ] - - # Get a hash of the old and changed values for each field. - person.changes # { "name" => [ "Alan Parsons", "Alan Garner" ] } - - # Check if a specific field has changed. - person.name_changed? # true - - # Get the changes for a specific field. - person.name_change # [ "Alan Parsons", "Alan Garner" ] - - # Get the previous value for a field. - person.name_was # "Alan Parsons" - -.. note:: - - Setting the associations on a document does not cause the ``changes`` or - ``changed_attributes`` hashes to be modified. This is true for all associations - whether referenced or embedded. Note that changing the _id(s) field on - referenced associations does cause the changes to show up in the ``changes`` - and the ``changed_attributes`` hashes. - - -Resetting Changes ------------------ - -You can reset changes of a field to its previous value by calling the reset method. - -.. code-block:: ruby - - person = Person.first - person.name = "Alan Garner" - - # Reset the changed name back to the original - person.reset_name! - person.name # "Alan Parsons" - - -Persistence ------------ - -Mongoid uses dirty tracking as the core of its persistence operations. It looks at the -changes on a document and atomically updates only what has changed, unlike other frameworks -that write the entire document on each save. If no changes have been made, Mongoid will -not hit the database on a call to ``Model#save``. - - -Viewing Previous Changes ------------------------- - -After a document has been persisted, you can see what the changes were previously by -calling ``Model#previous_changes``. - -.. code-block:: ruby - - person = Person.first - person.name = "Alan Garner" - person.save # Clears out current changes. - - # View the previous changes. - person.previous_changes # { "name" => [ "Alan Parsons", "Alan Garner" ] } - - -Updating Container Fields -========================= - -Be aware that, until -`MONGOID-2951 `_ -is resolved, all fields including container ones must be assigned to for -their values to be persisted to the database. - -For example, adding to a set like this does not work: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :tours, type: Set - end - - band = Band.new - band.tours - # => # - - band.tours << 'London' - # => # - band.tours - # => # - -Instead, the field value must be modified outside of the model and assigned -back to the model as follows: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :tours, type: Set - end - - band = Band.new - - tours = band.tours - # => # - - tours << 'London' - # => # - - band.tours = tours - # => # - - band.tours - # => # - - -.. _readonly-documents: - -Readonly Documents -================== - -Documents can be marked read-only in two ways, depending on the value of the -``Mongoid.legacy_readonly`` feature flag: - -If this flag is turned off, a document is marked read-only when the ``#readonly!`` -method is called on that documnet. A read-only document, with this flag turned off, -will raise a ReadonlyDocument error on attempting to perform any persistence -operation, including (but not limited to) saving, updating, deleting and -destroying. Note that reloading does not reset the read-only state. - -.. code:: ruby - - band = Band.first - band.readonly? # => false - band.readonly! - band.readonly? # => true - band.name = "The Rolling Stones" - band.save # => raises ReadonlyDocument error - band.reload.readonly? # => true - -If this flag is turned on, a document is marked read-only when that document -has been projected (i.e. using ``#only`` or ``#without``). A read-only document, -with this flag turned on, will not be deletable or destroyable (a -``ReadonlyDocument`` error will be raised), but will be saveable and updatable. -The read-only status is reset on reloading the document. - -.. code:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :genre, type: String - end - - band = Band.only(:name).first - band.readonly? # => true - band.destroy # => raises ReadonlyDocument error - band.reload.readonly? # => false - - -Overriding ``readonly?`` ------------------------- - -Another way to make a document read-only is by overriding the readonly? method: - -.. code:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :genre, type: String - - def readonly? - true - end - end - - band = Band.first - band.readonly? # => true - band.destroy # => raises ReadonlyDocument error diff --git a/source/reference/fields.txt b/source/reference/fields.txt deleted file mode 100644 index 6198bb6c..00000000 --- a/source/reference/fields.txt +++ /dev/null @@ -1,1776 +0,0 @@ -.. _fields: - -**************** -Field Definition -**************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -.. _field-types: - -Field Types -=========== - -MongoDB stores underlying document data using -`BSON types `_, and -Mongoid converts BSON types to Ruby types at runtime in your application. -For example, a field defined with ``type: :float`` will use the Ruby ``Float`` -class in-memory and will persist in the database as the the BSON ``double`` type. - -Field type definitions determine how Mongoid behaves when constructing queries -and retrieving/writing fields from/to the database. Specifically: - -1. When assigning values to fields at runtime, the values are converted to the - specified type. -2. When persisting data to MongoDB, the data is sent in an appropriate - type, permitting richer data manipulation within MongoDB or by other - tools. -3. When querying documents, query parameters are converted to the specified - type before being sent to MongoDB. -4. When retrieving documents from the database, field values are converted - to the specified type. - -Changing the field definitions in a model class does not alter data already stored in -MongoDB. To update type or contents of fields of existing documents, -the field must be re-saved to the database. Note that, due to Mongoid -tracking which attributes on a model change and only saving the changed ones, -it may be necessary to explicitly write a field value when changing the -type of an existing field without changing the stored values. - -Consider a simple class for modeling a person in an application. A person may -have a name, date_of_birth, and weight. We can define these attributes -on a person by using the ``field`` macro. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :name, type: String - field :date_of_birth, type: Date - field :weight, type: Float - end - -The valid types for fields are as follows: - -- ``Array`` -- ``BSON::Binary`` -- :ref:`BigDecimal ` -- ``Mongoid::Boolean``, which may be specified simply as ``Boolean`` in the - scope of a class which included ``Mongoid::Document``. -- :ref:`Date ` -- :ref:`DateTime ` -- ``Float`` -- :ref:`Hash ` -- ``Integer`` -- :ref:`Object ` -- ``BSON::ObjectId`` -- ``Range`` -- :ref:`Regexp ` -- ``Set`` -- ``String`` -- :ref:`Mongoid::StringifiedSymbol `, - which may be specified simply as ``StringifiedSymbol`` in the scope of a - class which included ``Mongoid::Document``. -- :ref:`Symbol ` -- :ref:`Time ` -- ``ActiveSupport::TimeWithZone`` - -Mongoid also recognizes the string ``"Boolean"`` as an alias for the -``Mongoid::Boolean`` class. - -To define custom field types, refer to :ref:`Custom Field Types ` below. - -.. note:: - - Using the ``BSON::Int64`` and ``BSON::Int32`` types as field types is unsupported. - Saving these types to the database will work as expected, however, querying them - will return the native Ruby ``Integer`` type. Querying fields of type - ``BSON::Decimal128`` will return values of type ``BSON::Decimal128`` in - BSON <=4 and values of type ``BigDecimal`` in BSON 5+. - - -.. _untyped-fields: - -Untyped Fields --------------- - -Not specifying a type for a field is the same as specifying the ``Object`` -type. Such fields are untyped: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - field :properties - # Equivalent to: - field :properties, type: Object - end - -An untyped field can store values of any type which is directly serializable -to BSON. This is useful when a field may contain values of different types -(i.e. it is a variant type field), or when the type of values is not known -ahead of time: - -.. code-block:: ruby - - product = Product.new(properties: "color=white,size=large") - product.properties - # => "color=white,size=large" - - product = Product.new(properties: {color: "white", size: "large"}) - product.properties - # => {:color=>"white", :size=>"large"} - -When values are assigned to the field, Mongoid still performs mongoization but -uses the class of the value rather than the field type for mongoization logic. - -.. code-block:: ruby - - product = Product.new(properties: 0..10) - product.properties - # The range 0..10, mongoized: - # => {"min"=>0, "max"=>10} - -When reading data from the database, Mongoid does not perform any type -conversions on untyped fields. For this reason, even though it is possible -to write any BSON-serializable value into an untyped fields, values which -require special handling on the database reading side will generally not work -correctly in an untyped field. Among field types supported by Mongoid, -values of the following types should not be stored in untyped fields: - -- ``Date`` (values will be returned as ``Time``) -- ``DateTime`` (values will be returned as ``Time``) -- ``Range`` (values will be returned as ``Hash``) - - -.. _field-type-stringified-symbol: - -Field Type: StringifiedSymbol ------------------------------ - -The ``StringifiedSymbol`` field type is the recommended field type for storing -values that should be exposed as symbols to Ruby applications. When using the ``Symbol`` field type, -Mongoid defaults to storing values as BSON symbols. For more information on the -BSON symbol type, see :ref:`here `. -However, the BSON symbol type is deprecated and is difficult to work with in programming languages -without native symbol types, so the ``StringifiedSymbol`` type allows the use of symbols -while ensuring interoperability with other drivers. The ``StringifiedSymbol`` type stores all data -on the database as strings, while exposing values to the application as symbols. - -An example usage is shown below: - -.. code-block:: ruby - - class Post - include Mongoid::Document - - field :status, type: StringifiedSymbol - end - - post = Post.new(status: :hello) - # status is stored as "hello" on the database, but returned as a Symbol - post.status - # => :hello - - # String values can be assigned also: - post = Post.new(status: "hello") - # status is stored as "hello" on the database, but returned as a Symbol - post.status - # => :hello - -All non-string values will be stringified upon being sent to the database (via ``to_s``), and -all values will be converted to symbols when returned to the application. Values that cannot be -converted directly to symbols, such as integers and arrays, will first be converted to strings and -then symbols before being returned to the application. - -For example, setting an integer as ``status``: - -.. code-block:: ruby - - post = Post.new(status: 42) - post.status - # => :"42" - -If the ``StringifiedSymbol`` type is applied to a field that contains BSON symbols, the values -will be stored as strings instead of BSON symbols on the next save. This permits transparent lazy -migration from fields that currently store either strings or BSON symbols in the database to the -``StringifiedSymbol`` field type. - - -.. _field-type-symbol: - -Field Type: Symbol ------------------- - -New applications should use the :ref:`StringifiedSymbol field type ` -to store Ruby symbols in the database. The ``StringifiedSymbol`` field type -provides maximum compatibility with other applications and programming languages -and has the same behavior in all circumstances. - -Mongoid also provides the deprecated ``Symbol`` field type for serializing -Ruby symbols to BSON symbols. Because the BSON specification deprecated the -BSON symbol type, the ``bson`` gem will serialize Ruby symbols into BSON strings -when used on its own. However, in order to maintain backwards compatibility -with older datasets, the ``mongo`` gem overrides this behavior to serialize Ruby -symbols as BSON symbols. This is necessary to be able to specify queries for -documents which contain BSON symbols as fields. - -To override the default behavior and configure the ``mongo`` gem (and thereby -Mongoid as well) to encode symbol values as strings, include the following code -snippet in your project: - -.. code-block:: ruby - - class Symbol - def bson_type - BSON::String::BSON_TYPE - end - end - - -.. _field-type-hash: - -Field Type: Hash ----------------- - -When using a field of type Hash, be wary of adhering to the -`legal key names for mongoDB `_, -or else the values will not store properly. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :first_name - field :url, type: Hash - - # will update the fields properly and save the values - def set_vals - self.first_name = 'Daniel' - self.url = {'home_page' => 'http://www.homepage.com'} - save - end - - # all data will fail to save due to the illegal hash key - def set_vals_fail - self.first_name = 'Daniel' - self.url = {'home.page' => 'http://www.homepage.com'} - save - end - end - - -.. _field-type-time: - -Field Type: Time ----------------- - -``Time`` fields store values as ``Time`` instances in the :ref:`configured -time zone `. - -``Date`` and ``DateTime`` instances are converted to ``Time`` instances upon -assignment to a ``Time`` field: - -.. code-block:: ruby - - class Voter - include Mongoid::Document - - field :registered_at, type: Time - end - - Voter.new(registered_at: Date.today) - # => # - -In the above example, the value was interpreted as the beginning of today in -local time, because the application was not configured to use UTC times. - -.. note:: - - When the database contains a string value for a ``Time`` field, Mongoid - parses the string value using ``Time.parse`` which considers values without - time zones to be in local time. - - -.. _field-type-date: - -Field Type: Date ----------------- - -Mongoid allows assignment of values of several types to ``Date`` fields: - -- ``Date`` - the provided date is stored as is. -- ``Time``, ``DateTime``, ``ActiveSupport::TimeWithZone`` - the date component - of the value is taken in the value's time zone. -- ``String`` - the date specified in the string is used. -- ``Integer``, ``Float`` - the value is taken to be a UTC timestamp which is - converted to the :ref:`configured time zone ` (note that - ``Mongoid.use_utc`` has no effect on this conversion), then the date is - taken from the resulting time. - -In other words, if a date is specified in the value, that date is used without -first converting the value to the configured time zone. - -As a date & time to date conversion is lossy (it discards the time component), -especially if an application operates with times in different time zones it is -recommended to explicitly convert ``String``, ``Time`` and ``DateTime`` -objects to ``Date`` objects before assigning the values to fields of type -``Date``. - -.. note:: - - When the database contains a string value for a ``Date`` field, Mongoid - parses the string value using ``Time.parse``, discards the time portion of - the resulting ``Time`` object and uses the date portion. ``Time.parse`` - considers values without time zones to be in local time. - - -.. _field-type-date-time: - -Field Type: DateTime ---------------------- - -MongoDB stores all times as UTC timestamps. When assigning a value to a -``DateTime`` field, or when querying a ``DateTime`` field, Mongoid -converts the passed in value to a UTC ``Time`` before sending it to the -MongoDB server. - -``Time``, ``ActiveSupport::TimeWithZone`` and ``DateTime`` objects embed -time zone information, and the value persisted is the specified moment in -time, in UTC. When the value is retrieved, the time zone in which it is -returned is defined by the :ref:`configured time zone settings `. - -.. code-block:: ruby - - class Ticket - include Mongoid::Document - field :opened_at, type: DateTime - end - - Time.zone = 'Berlin' - - ticket = Ticket.create!(opened_at: '2018-02-18 07:00:08 -0500') - - ticket.opened_at - # => Sun, 18 Feb 2018 13:00:08 +0100 - ticket - # => # - - Time.zone = 'America/New_York' - ticket.opened_at - # => Sun, 18 Feb 2018 07:00:08 -0500 - - Mongoid.use_utc = true - ticket.opened_at - # => Sun, 18 Feb 2018 12:00:08 +0000 - -Mongoid also supports casting integers and floats to ``DateTime``. When -doing so, the integers/floats are assumed to be Unix timestamps (in UTC): - -.. code-block:: ruby - - ticket.opened_at = 1544803974 - ticket.opened_at - # => Fri, 14 Dec 2018 16:12:54 +0000 - -If a string is used as a ``DateTime`` field value, the behavior depends on -whether the string includes a time zone. If no time zone is specified, -the :ref:`default Mongoid time zone ` is used: - -.. code-block:: ruby - - Time.zone = 'America/New_York' - ticket.opened_at = 'Mar 4, 2018 10:00:00' - ticket.opened_at - # => Sun, 04 Mar 2018 15:00:00 +0000 - -If a time zone is specified, it is respected: - -.. code-block:: ruby - - ticket.opened_at = 'Mar 4, 2018 10:00:00 +01:00' - ticket.opened_at - # => Sun, 04 Mar 2018 09:00:00 +0000 - -.. note:: - - When the database contains a string value for a ``DateTime`` field, Mongoid - parses the string value using ``Time.parse`` which considers values without - time zones to be in local time. - - -.. _field-type-regexp: - -Field Type: Regexp ------------------- - -MongoDB supports storing regular expressions in documents, and querying using -regular expressions. Note that MongoDB uses -`Perl-compatible regular expressions (PCRE) `_ -and Ruby uses `Onigmo `_, which is a -fork of `Oniguruma regular expression engine `_. -The two regular expression implementations generally provide equivalent -functionality but have several important syntax differences. - -When a field is declared to be of type Regexp, Mongoid converts Ruby regular -expressions to BSON regular expressions and stores the result in MongoDB. -Retrieving the field from the database produces a ``BSON::Regexp::Raw`` -instance: - -.. code-block:: ruby - - class Token - include Mongoid::Document - - field :pattern, type: Regexp - end - - token = Token.create!(pattern: /hello.world/m) - token.pattern - # => /hello.world/m - - token.reload - token.pattern - # => # - -Use ``#compile`` method on ``BSON::Regexp::Raw`` to get back the Ruby regular -expression: - -.. code-block:: ruby - - token.pattern.compile - # => /hello.world/m - -Note that, if the regular expression was not originally a Ruby one, calling -``#compile`` on it may produce a different regular expression. For example, -the following is a PCRE matching a string that ends in "hello": - -.. code-block:: ruby - - BSON::Regexp::Raw.new('hello$', 's') - # => # - -Compiling this regular expression produces a Ruby regular expression that -matches strings containing "hello" before a newline, besides strings ending in -"hello": - -.. code-block:: ruby - - BSON::Regexp::Raw.new('hello$', 's').compile =~ "hello\nworld" - # => 0 - -This is because the meaning of ``$`` is different between PCRE and Ruby -regular expressions. - -.. _field-type-big-decimal: - -BigDecimal Fields ------------------ - -The ``BigDecimal`` field type is used to store numbers with increased precision. - -The ``BigDecimal`` field type stores its values in two different ways in the -database, depending on the value of the ``Mongoid.map_big_decimal_to_decimal128`` -global config option. If this flag is set to false (which is the default), -the ``BigDecimal`` field will be stored as a string, otherwise it will be stored -as a ``BSON::Decimal128``. - -The ``BigDecimal`` field type has some limitations when converting to and from -a ``BSON::Decimal128``: - -- ``BSON::Decimal128`` has a limited range and precision, while ``BigDecimal`` - has no restrictions in terms of range and precision. ``BSON::Decimal128`` has - a max value of approximately ``10^6145`` and a min value of approximately - ``-10^6145``, and has a maximum of 34 bits of precision. When attempting to - store values that don't fit into a ``BSON::Decimal128``, it is recommended to - have them stored as a string instead of a ``BSON::Decimal128``. You can do - that by setting ``Mongoid.map_big_decimal_to_decimal128`` to ``false``. If a - value that does not fit in a ``BSON::Decimal128`` is attempted to be stored - as one, an error will be raised. - -- ``BSON::Decimal128`` is able to accept signed ``NaN`` values, while - ``BigDecimal`` is not. When retrieving signed ``NaN`` values from - the database using the ``BigDecimal`` field type, the ``NaN`` will be - unsigned. - -- ``BSON::Decimal128`` maintains trailing zeroes when stored in the database. - ``BigDecimal``, however, does not maintain trailing zeroes, and therefore - retrieving ``BSON::Decimal128`` values using the ``BigDecimal`` field type - may result in a loss of precision. - -There is an additional caveat when storing a ``BigDecimal`` in a field with no -type (i.e. a dynamically typed field) and ``Mongoid.map_big_decimal_to_decimal128`` -is ``false``. In this case, the ``BigDecimal`` is stored as a string, and since a -dynamic field is being used, querying for that field with a ``BigDecimal`` will -not find the string for that ``BigDecimal``, since the query is looking for a -``BigDecimal``. In order to query for that string, the ``BigDecimal`` must -first be converted to a string with ``to_s``. Note that this is not a problem -when the field has type ``BigDecimal``. - -If you wish to avoid using ``BigDecimal`` altogether, you can set the field -type to ``BSON::Decimal128``. This will allow you to keep track of trailing -zeroes and signed ``NaN`` values. - -Migration to ``decimal128``-backed ``BigDecimal`` Field -``````````````````````````````````````````````````````` -In a future major version of Mongoid, the ``Mongoid.map_big_decimal_to_decimal128`` -global config option will be defaulted to ``true``. When this flag is turned on, -``BigDecimal`` values in queries will not match to the strings that are already -stored in the database; they will only match to ``decimal128`` values that are -in the database. If you have a ``BigDecimal`` field that is backed by strings, -you have three options: - -1. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``false``, and you can continue storing your ``BigDecimal`` values as - strings. Note that you are surrendering the advantages of storing ``BigDecimal`` - values as a ``decimal128``, like being able to do queries and aggregations - based on the numerical value of the field. - -2. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``true``, and you can convert all values for that field from strings to - ``decimal128`` values in the database. You should do this conversion before - setting the global config option to true. An example query to accomplish this - is as follows: - - .. code-block:: javascript - - db.bands.updateMany({ - "field": { "$exists": true } - }, [ - { - "$set": { - "field": { "$toDecimal": "$field" } - } - } - ]) - - This query updates all documents that have the given field, setting that - field to its corresponding ``decimal128`` value. Note that this query only - works in MongoDB 4.2+. - -3. The ``Mongoid.map_big_decimal_to_decimal128`` global config option can be - set to ``true``, and you can have both strings and ``decimal128`` values for - that field. This way, only ``decimal128`` values will be inserted into and - updated to the database going forward. Note that you still don't get the - full advantages of using only ``decimal128`` values, but your dataset is - slowly migrating to all ``decimal128`` values, as old string values are - updated to ``decimal128`` and new ``decimal128`` values are added. With this - setup, you can still query for ``BigDecimal`` values as follows: - - .. code-block:: ruby - - Mongoid.map_big_decimal_to_decimal128 = true - big_decimal = BigDecimal('2E9') - Band.in(sales: [big_decimal, big_decimal.to_s]).to_a - - This query will find all values that are either a ``decimal128`` value or - a string that match that value. - - -Using Symbols Or Strings Instead Of Classes -------------------------------------------- - -Mongoid permits using symbols or strings instead of classes to specify the -type of fields, for example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :state, type: :integer - # Equivalent to: - field :state, type: "integer" - # Equivalent to: - field :state, type: Integer - end - -Only standard field types as listed below can be specified using symbols or -strings in this manner. Mongoid recognizes the following expansions: - -- ``:array`` => ``Array`` -- ``:big_decimal`` => ``BigDecimal`` -- ``:binary`` => ``BSON::Binary`` -- ``:boolean`` => ``Mongoid::Boolean`` -- ``:date`` => ``Date`` -- ``:date_time`` => ``DateTime`` -- ``:float`` => ``Float`` -- ``:hash`` => ``Hash`` -- ``:integer`` => ``Integer`` -- ``:object_id`` => ``BSON::ObjectId`` -- ``:range`` => ``Range`` -- ``:regexp`` => ``Regexp`` -- ``:set`` => ``Set`` -- ``:string`` => ``String`` -- ``:stringified_symbol`` => ``StringifiedSymbol`` -- ``:symbol`` => ``Symbol`` -- ``:time`` => ``Time`` - - -.. _field-default-values: - -Specifying Field Default Values -------------------------------- - -A field can be configured to have a default value. The default value can be -fixed, as in the following example: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :state, type: String, default: 'created' - end - -The default value can also be specified as a ``Proc``: - -.. code-block:: ruby - - class Order - include Mongoid::Document - - field :fulfill_by, type: Time, default: ->{ Time.now + 3.days } - end - -.. note:: - - Default values that are not ``Proc`` instances are evaluated at class load - time, meaning the following two definitions are not equivalent: - - .. code-block:: ruby - - field :submitted_at, type: Time, default: Time.now - field :submitted_at, type: Time, default: ->{ Time.now } - - The second definition is most likely the desired one, which causes the - time of submission to be set to the current time at the moment of - document instantiation. - -To set a default which depends on the document's state, use ``self`` -inside the ``Proc`` instance which would evaluate to the document instance -being operated on: - -.. code-block:: ruby - - field :fulfill_by, type: Time, default: ->{ - # Order should be fulfilled in 2 business hours. - if (7..8).include?(self.submitted_at.hour) - self.submitted_at + 4.hours - elsif (9..3).include?(self.submitted_at.hour) - self.submitted_at + 2.hours - else - (self.submitted_at + 1.day).change(hour: 11) - end - } - -When defining a default value as a ``Proc``, Mongoid will apply the default -after all other attributes are set and associations are initialized. -To have the default be applied before the other attributes are set, -use the ``pre_processed: true`` field option: - -.. code-block:: ruby - - field :fulfill_by, type: Time, default: ->{ Time.now + 3.days }, - pre_processed: true - -The ``pre_processed: true`` option is also necessary when specifying a custom -default value via a ``Proc`` for the ``_id`` field, to ensure the ``_id`` -is set correctly via associations: - -.. code-block:: ruby - - field :_id, type: String, default: -> { 'hello' }, pre_processed: true - - -.. _storage-field-names: - -Specifying Storage Field Names ------------------------------- - -One of the drawbacks of having a schemaless database is that MongoDB must -store all field information along with every document, meaning that it -takes up a lot of storage space in RAM and on disk. A common pattern to limit -this is to alias fields to a small number of characters, while keeping the -domain in the application expressive. Mongoid allows you to do this and -reference the fields in the domain via their long names in getters, setters, -and criteria while performing the conversion for you. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :n, as: :name, type: String - end - - band = Band.new(name: "Placebo") - band.attributes # { "n" => "Placebo" } - - criteria = Band.where(name: "Placebo") - criteria.selector # { "n" => "Placebo" } - -.. _mongoid-field-aliases: - -Field Aliases -------------- - -It is possible to define field aliases. The value will be stored in the -destination field but can be accessed from either the destination field or -from the aliased field: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - alias_attribute :n, :name - end - - band = Band.new(n: 'Astral Projection') - # => # - - band.attributes - # => {"_id"=>BSON::ObjectId('5fc1c1ee2c97a64accbeb5e1'), "name"=>"Astral Projection"} - - band.n - # => "Astral Projection" - -Aliases can be removed from model classes using the ``unalias_attribute`` -method. - -.. code-block:: ruby - - class Band - unalias_attribute :n - end - -.. _unalias-id: - -Unaliasing ``id`` -````````````````` - -``unalias_attribute`` can be used to remove the predefined ``id`` alias. -This is useful for storing different values in ``id`` and ``_id`` fields: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - unalias_attribute :id - field :id, type: String - end - - Band.new(id: '42') - # => # - - -Reserved Names --------------- - -Attempting to define a field on a document that conflicts with a reserved -method name in Mongoid will raise an error. The list of reserved names can -be obtained by invoking the ``Mongoid.destructive_fields`` method. - - -Field Redefinition ------------------- - -By default Mongoid allows redefining fields on a model. To raise an error -when a field is redefined, set the ``duplicate_fields_exception`` -:ref:`configuration option ` to ``true``. - -With the option set to true, the following example will raise an error: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name - - field :name, type: String - end - -To define the field anyway, use the ``overwrite: true`` option: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name - - field :name, type: String, overwrite: true - end - - -.. _custom-id: - -Custom IDs ----------- - -By default, Mongoid defines the ``_id`` field on documents to contain a -``BSON::ObjectId`` value which is automatically generated by Mongoid. - -It is possible to replace the ``_id`` field definition to change the type -of the ``_id`` values or have different default values: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :_id, type: String, default: ->{ name } - end - -It is possible to omit the default entirely: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :_id, type: String - end - -If the default on ``_id`` is omitted, and no ``_id`` value is provided by -your application, Mongoid will persist the document without the ``_id`` -value. In this case, if the document is a top-level document, an ``_id`` -value will be assigned by the server; if the document is an embedded document, -no ``_id`` value will be assigned. Mongoid will not automatically retrieve -this value, if assigned, when the document is persisted - you -must obtain the persisted value (and the complete persisted document) using -other means: - -.. code-block:: ruby - - band = Band.create! - => # - band.id - => nil - band.reload - # raises Mongoid::Errors::DocumentNotFound - Band.last - => # - -Omitting ``_id`` fields is more common in :ref:`embedded documents `. - -Mongoid also defines the ``id`` field aliased to ``_id``. The ``id`` -alias can :ref:`be removed ` if desired (such as to integrate -with systems that use the ``id`` field to store value different from ``_id``. - -.. _uncastable-values: - -Uncastable Values ------------------ - -In Mongoid 8, Mongoid has standardized the treatment of the assignment and -reading of "uncastable" values. A value is considered "uncastable" when it -cannot be coerced to the type of its field. For example, an array would be an -"uncastable" value to an Integer field. - - -Assigning Uncastable Values -``````````````````````````` - -The assignment of uncastable values has been standardized to assign ``nil`` by -default. Consider the following example: - -.. code:: - - class User - include Mongoid::Document - - field :name, type: Integer - end - - User.new(name: [ "hello" ]) - -Assigning an array to a field of type Integer doesn't work since an array can't -be coerced to an Integer. The assignment of uncastable values to a field will -cause a ``nil`` to be written: - -.. code:: - - user = User.new(name: [ "Mike", "Trout" ]) - # => # - -Note that the original uncastable values will be stored in the -``attributes_before_type_cast`` hash with their field names: - -.. code:: - - user.attributes_before_type_cast["name"] - # => ["Mike", "Trout"] - -.. note:: - - Note that for numeric fields, any class that defines ``to_i`` for Integer - fields, ``to_f`` for Floats, and ``to_d`` for BigDecimals, is castable. - Strings are the exception and will only call the corresponding ``to_*`` - method if the string is numeric. If a class only defines ``to_i`` and not - ``to_f`` and is being assigned to a Float field, this is uncastable, and Mongoid - will not perform a two-step conversion (i.e. ``to_i`` and then ``to_f``). - - -Reading Uncastable Values -````````````````````````` - -When documents in the database contain values of different types than their -representations in Mongoid, if Mongoid cannot coerce them into the correct type, -it will replace the value with ``nil``. Consider the following model and document in the -database: - -.. code:: - - class User - include Mongoid::Document - - field :name, type: Integer - end - -.. code:: - - { _id: ..., name: [ "Mike", "Trout" ] } - -Reading this document from the database will result in the model's name field -containing ``nil``: - -.. code:: - - User.first.name - # => nil - -The database value of type array cannot be stored in the attribute, since the -array can't be coerced to an Integer. Note that the original uncastable values -will be stored in the ``attributes_before_type_cast`` hash with their field -names: - -.. code:: - - user.attributes_before_type_cast["name"] - # => ["Mike", "Trout"] - -.. note:: - - The ``demongoize`` methods on container objects (i.e. Hash, Array) have not - been changed to permit automatic persistence of mutated container attributes. - See `MONGOID-2951 `_ for a - longer discussion of this topic. - - -.. _customizing-field-behavior: - -Customizing Field Behavior -========================== - -Mongoid offers several ways to customize the behavior of fields. - - -.. _custom-getters-and-setters: - -Custom Getters And Setters --------------------------- - -You may override getters and setters for fields to modify the values -when they are being accessed or written. The getters and setters use the -same name as the field. Use ``read_attribute`` and ``write_attribute`` -methods inside the getters and setters to operate on the raw attribute -values. - -For example, Mongoid provides the ``:default`` field option to write a -default value into the field. If you wish to have a field default value -in your application but do not wish to persist it, you can override the -getter as follows: - -.. code-block:: ruby - - class DistanceMeasurement - include Mongoid::Document - - field :value, type: Float - field :unit, type: String - - def unit - read_attribute(:unit) || "m" - end - - def to_s - "#{value} #{unit}" - end - end - - measurement = DistanceMeasurement.new(value: 2) - measurement.to_s - # => "2.0 m" - measurement.attributes - # => {"_id"=>BSON::ObjectId('613fa0b0a15d5d61502f3447'), "value"=>2.0} - -To give another example, a field which converts empty strings to nil values -may be implemented as follows: - -.. code-block:: ruby - - class DistanceMeasurement - include Mongoid::Document - - field :value, type: Float - field :unit, type: String - - def unit=(value) - if value.blank? - value = nil - end - write_attribute(:unit, value) - end - end - - measurement = DistanceMeasurement.new(value: 2, unit: "") - measurement.attributes - # => {"_id"=>BSON::ObjectId('613fa15aa15d5d617216104c'), "value"=>2.0, "unit"=>nil} - - -.. _custom-field-types: - -Custom Field Types ------------------- - -You can define custom types in Mongoid and determine how they are serialized -and deserialized. In this example, we define a new field type ``Point``, which we -can use in our model class as follows: - -.. code-block:: ruby - - class Profile - include Mongoid::Document - field :location, type: Point - end - -Then make a Ruby class to represent the type. This class must define methods -used for MongoDB serialization and deserialization as follows: - -.. code-block:: ruby - - class Point - - attr_reader :x, :y - - def initialize(x, y) - @x, @y = x, y - end - - # Converts an object of this instance into a database friendly value. - # In this example, we store the values in the database as array. - def mongoize - [ x, y ] - end - - class << self - - # Takes any possible object and converts it to how it would be - # stored in the database. - def mongoize(object) - case object - when Point then object.mongoize - when Hash then Point.new(object[:x], object[:y]).mongoize - else object - end - end - - # Get the object as it was stored in the database, and instantiate - # this custom class from it. - def demongoize(object) - Point.new(object[0], object[1]) - end - - # Converts the object that was supplied to a criteria and converts it - # into a query-friendly form. - def evolve(object) - case object - when Point then object.mongoize - else object - end - end - end - end - -The instance method ``mongoize`` takes an instance of your custom type object, and -converts it into a representation of how it will be stored in the database, i.e. to pass -to the MongoDB Ruby driver. In our example above, we want to store our ``Point`` -object as an ``Array`` in the form ``[ x, y ]``. - -The class method ``mongoize`` is similar to the instance method, however it must handle -objects of all possible types as inputs. The ``mongoize`` method is used when calling the -setter methods for fields of your custom type. - -.. code-block:: ruby - - point = Point.new(12, 24) - venue = Venue.new(location: point) # This uses the Point#mongoize instance method. - venue = Venue.new(location: [ 12, 24 ]) # This uses the Point.mongoize class method. - -The class method ``demongoize`` does the inverse of ``mongoize``. It takes the raw object -from the MongoDB Ruby driver and converts it to an instance of your custom type. -In this case, the database driver returns an ``Array`` and we instantiate a ``Point`` from it. -The ``demongoize`` method is used when calling the getters of fields for your custom type. -Note that in the example above, since ``demongoize`` calls ``Point.new``, a new instance of -``Point`` will be generated on each call to the getter. - -Mongoid will always call the ``demongoize`` method on values that were -retrieved from the database, but applications may, in theory, call -``demongoize`` with arbitrary input. It is recommended that applications add -handling for arbitrary input in their ``demongoize`` methods. We can rewrite -``Point``'s demongoize method as follows: - -.. code:: ruby - - def demongoize(object) - if object.is_a?(Array) && object.length == 2 - Point.new(object[0], object[1]) - end - end - -Notice that ``demongoize`` will only create a new ``Point`` if given an array -of length 2, and will return ``nil`` otherwise. Both the ``mongoize`` and -``demongoize`` methods should be prepared to receive arbitrary input and should -return ``nil`` on values that are uncastable to your custom type. See the -section on :ref:`Uncastable Values ` for more details. - -Lastly, the class method ``evolve`` is similar to ``mongoize``, however it is used -when transforming objects for use in Mongoid query criteria. - -.. code-block:: ruby - - point = Point.new(12, 24) - Venue.where(location: point) # This uses Point.evolve - -The ``evolve`` method should also be prepared to receive arbitrary input, -however, unlike the ``mongoize`` and ``demongoize`` methods, it should return -the inputted value on values that are uncastable to your custom type. See the -section on :ref:`Uncastable Values ` for more details. - - -.. _phantom-custom-field-types: - -Phantom Custom Field Types -`````````````````````````` - -The custom field type may perform conversions from user-visible attribute -values to the values stored in the database when the user-visible attribute -value type is different from the declared field type. For example, this -can be used to implement a mapping from one enumeration to another, to -have more descriptive values in the application and more compact values stored -in the database: - -.. code-block:: ruby - - class ColorMapping - - MAPPING = { - 'black' => 0, - 'white' => 1, - }.freeze - - INVERSE_MAPPING = MAPPING.invert.freeze - - class << self - - # Takes application-scope value and converts it to how it would be - # stored in the database. Converts invalid values to nil. - def mongoize(object) - MAPPING[object] - end - - # Get the value as it was stored in the database, and convert to - # application-scope value. Converts invalid values to nil. - def demongoize(object) - INVERSE_MAPPING[object] - end - - # Converts the object that was supplied to a criteria and converts it - # into a query-friendly form. Returns invalid values as is. - def evolve(object) - MAPPING.fetch(object, object) - end - end - end - - class Profile - include Mongoid::Document - field :color, type: ColorMapping - end - - profile = Profile.new(color: 'white') - profile.color - # => "white" - - # Writes 0 to color field - profile.save! - - -.. _custom-field-options: - -Custom Field Options --------------------- - -You may define custom options for the ``field`` macro function -which extend its behavior at the your time model classes are loaded. - -As an example, we will define a ``:max_length`` option which will add a length -validator for the field. First, declare the new field option in an initializer, -specifying its handler function as a block: - -.. code-block:: ruby - - # in /config/initializers/mongoid_custom_fields.rb - - Mongoid::Fields.option :max_length do |model, field, value| - model.validates_length_of field.name, maximum: value - end - -Then, use it your model class: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :name, type: String, max_length: 10 - end - -Note that the handler function will be invoked whenever the option is used -in the field definition, even if the option's value is false or nil. - -.. _mongoid-dynamic-fields: - -Dynamic Fields -============== - -By default, Mongoid requires all fields that may be set on a document to -be explicitly defined using ``field`` declarations. Mongoid also supports -creating fields on the fly from an arbitrary hash or documents stored in -the database. When a model uses fields not explicitly defined, such fields -are called *dynamic fields*. - -To enable dynamic fields, include ``Mongoid::Attributes::Dynamic`` module -in the model: - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Attributes::Dynamic - end - - bob = Person.new(name: 'Bob', age: 42) - bob.name - # => "Bob" - -It is possible to use ``field`` declarations and dynamic fields in the same -model class. Attributes for which there is a ``field`` declaration will be -treated according to the ``field`` declaration, with remaining attributes -being treated as dynamic fields. - -Attribute values in the dynamic fields must initially be set by either -passing the attribute hash to the constructor, mass assignment via -``attributes=``, mass assignment via ``[]=``, using ``write_attribute``, -or they must already be present in the database. - -.. code-block:: ruby - - # OK - bob = Person.new(name: 'Bob') - - # OK - bob = Person.new - bob.attributes = {age: 42} - - # OK - bob = Person.new - bob['age'] = 42 - - # Raises NoMethodError: undefined method age= - bob = Person.new - bob.age = 42 - - # OK - bob = Person.new - # OK - string access - bob.write_attribute('age', 42) - # OK - symbol access - bob.write_attribute(:name, 'Bob') - - # OK, initializes attributes from whatever is in the database - bob = Person.find('123') - -If an attribute is not present in a particular model instance's attributes -hash, both the reader and the writer for the corresponding field are not -defined, and invoking them raises ``NoMethodError``: - -.. code-block:: ruby - - bob = Person.new - bob.attributes = {age: 42} - - bob.age - # => 42 - - # raises NoMethodError - bob.name - - # raises NoMethodError - bob.name = 'Bob' - - # OK - bob['name'] = 'Bob' - - bob.name - # => "Bob" - -Attributes can always be read using mass attribute access or ``read_attribute`` -(this applies to models not using dynamic fields as well): - -.. code-block:: ruby - - bob = Person.new(age: 42) - - # OK - string access - bob['name'] - # => nil - - # OK - symbol access - bob[:name] - # => nil - - # OK - string access - bob['age'] - # => 42 - - # OK - symbol access - bob[:age] - # => 42 - - # OK - bob.attributes['name'] - # => nil - - # OK - bob.attributes['age'] - # => 42 - - # Returns nil - keys are always strings - bob.attributes[:age] - # => nil - - # OK - bob.read_attribute('name') - # => nil - - # OK - bob.read_attribute(:name) - # => nil - - # OK - string access - bob.read_attribute('age') - # => 42 - - # OK - symbol access - bob.read_attribute(:age) - # => 42 - -.. note:: - - The values returned from the ``read_attribute`` method, and those stored in - the ``attributes`` hash, are the ``mongoized`` values. - - -Special Characters in Field Names ---------------------------------- - -Mongoid permits dynamic field names to include spaces and punctuation: - -.. code-block:: ruby - - bob = Person.new('hello world' => 'MDB') - bob.send('hello world') - # => "MDB" - - bob.write_attribute("hello%world", 'MDB') - bob[:"hello%world"] - # => "MDB" - - -.. _localized-fields: - -Localized Fields -================ - -Mongoid supports localized fields via the `I18n gem `_. - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, type: String, localize: true - end - -By telling the field to ``localize``, Mongoid will under the covers store the field -as a hash of locale/value pairs, but normal access to it will behave like a string. - -.. code-block:: ruby - - I18n.default_locale = :en - product = Product.new - product.description = "Marvelous!" - I18n.locale = :de - product.description = "Fantastisch!" - - product.attributes - # { "description" => { "en" => "Marvelous!", "de" => "Fantastisch!" } - -You can get and set all the translations at once by using the corresponding ``_translations`` method. - -.. code-block:: ruby - - product.description_translations - # { "en" => "Marvelous!", "de" => "Fantastisch!" } - product.description_translations = - { "en" => "Marvelous!", "de" => "Wunderbar!" } - -Localized fields can be used with any field type. For example, they can be used -with float fields for differences with currency: - -.. code:: ruby - - class Product - include Mongoid::Document - - field :price, type: Float, localize: true - field :currency, type: String, localize: true - end - -By creating the model in this way, we can separate the price from the currency -type, which allows you to use all of the number-related functionalities on the -price when querying or aggregating that field (provided that you index into the -stored translations hash). We can create an instance of this model as follows: - -.. code:: ruby - - product = Product.new - I18n.locale = :en - product.price = 1.00 - product.currency = "$" - I18n.locale = :he - product.price = 3.24 - product.currency = "₪" - - product.attributes - # => { "price" => { "en" => 1.0, "he" => 3.24 }, "currency" => { "en" => "$", "he" => "₪" } } - - -.. _present-fields: - -Localize ``:present`` Field Option ----------------------------------- - -Mongoid supports the ``:present`` option when creating a localized field: - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, localize: :present - end - -This option automatically removes ``blank`` values (i.e. those that return true -for the ``blank?`` method) from the ``_translations`` hash: - -.. code-block:: ruby - - I18n.default_locale = :en - product = Product.new - product.description = "Marvelous!" - I18n.locale = :de - product.description = "Fantastisch!" - - product.description_translations - # { "en" => "Marvelous!", "de" => "Fantastisch!" } - - product.description = "" - product.description_translations - # { "en" => "Marvelous!" } - -When the empty string is written for the ``:de`` locale, the ``"de"`` key is -removed from the ``_translations`` hash instead of writing the empty string. - - -Fallbacks ---------- - -Mongoid integrates with -`i18n fallbacks `_. -To use the fallbacks, the respective functionality must be explicitly enabled. - -In a Rails application, set the ``config.i18n.fallbacks`` configuration setting -to ``true`` in your environment and specify the fallback languages: - -.. code-block:: ruby - - config.i18n.fallbacks = true - config.after_initialize do - I18n.fallbacks[:de] = [ :en, :es ] - end - -In a non-Rails application, include the fallbacks module into the I18n backend -you are using and specify the fallback languages: - -.. code-block:: ruby - - require "i18n/backend/fallbacks" - I18n::Backend::Simple.send(:include, I18n::Backend::Fallbacks) - I18n.fallbacks[:de] = [ :en, :es ] - -When fallbacks are enabled, if a translation is not present in the active -language, translations will be looked up in the fallback languages: - -.. code-block:: ruby - - product = Product.new - I18n.locale = :en - product.description = "Marvelous!" - I18n.locale = :de - product.description # "Marvelous!" - -Mongoid also defines a ``:fallbacks`` option on fields, which can be used to -disable fallback functionality on a specific field: - -.. code:: ruby - - class Product - include Mongoid::Document - field :description, type: String, localize: true, fallbacks: false - end - - product = Product.new - I18n.locale = :en - product.description = "Marvelous!" - I18n.locale = :de - product.description # nil - -Note that this option defaults to ``true``. - -.. note:: - - In i18n 1.1, the behavior of fallbacks `changed `_ - to always require an explicit list of fallback locales rather than falling - back to the default locale when no fallback locales have been provided. - - -Querying --------- - -When querying for localized fields using Mongoid's criteria API, Mongoid will automatically -alter the criteria to match the current locale. - -.. code-block:: ruby - - # Match all products with Marvelous as the description. Locale is en. - Product.where(description: "Marvelous!") - # The resulting MongoDB query filter: { "description.en" : "Marvelous!" } - - -Indexing --------- - -If you plan to be querying extensively on localized fields, you should index each of the -locales that you plan on searching on. - -.. code-block:: ruby - - class Product - include Mongoid::Document - field :description, localize: true - - index "description.de" => 1 - index "description.en" => 1 - end - - -.. _read-only: - -Read-Only Attributes -==================== - -You can tell Mongoid that certain attributes are read-only. This will allow -documents to be created with these attributes, but changes to them will be -ignored when using mass update methods such as ``update_attributes``: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :origin, type: String - - attr_readonly :name, :origin - end - - band = Band.create(name: "Placebo") - band.update_attributes(name: "Tool") # Filters out the name change. - -If you explicitly try to update or remove a read-only attribute by itself, -a ``ReadonlyAttribute`` exception will be raised: - -.. code-block:: ruby - - band.update_attribute(:name, "Tool") # Raises the error. - band.remove_attribute(:name) # Raises the error. - -Assignments to read-only attributes using their setters will be ignored: - -.. code-block:: ruby - - b = Band.create!(name: "The Rolling Stones") - # => # - b.name = "The Smashing Pumpkins" - # => "The Smashing Pumpkins" - b.name - # => "The Rolling Stones" - -Calls to atomic persistence operators, like ``bit`` and ``inc``, will persist -changes to read-only fields. - -Timestamp Fields -================ - -Mongoid supplies a timestamping module in ``Mongoid::Timestamps`` which -can be included to get basic behavior for ``created_at`` and -``updated_at`` fields. - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Timestamps - end - -You may also choose to only have specific timestamps for creation or -modification. - -.. code-block:: ruby - - class Person - include Mongoid::Document - include Mongoid::Timestamps::Created - end - - class Post - include Mongoid::Document - include Mongoid::Timestamps::Updated - end - -If you want to turn off timestamping for specific calls, use the timeless -method: - -.. code-block:: ruby - - person.timeless.save - Person.timeless.create! - -If you'd like shorter timestamp fields with aliases on them to save space, -you can include the short versions of the modules. - -.. code-block:: ruby - - class Band - include Mongoid::Document - include Mongoid::Timestamps::Short # For c_at and u_at. - end - - class Band - include Mongoid::Document - include Mongoid::Timestamps::Created::Short # For c_at only. - end - - class Band - include Mongoid::Document - include Mongoid::Timestamps::Updated::Short # For u_at only. - end - - -.. _field-names-with-periods-and-dollar-signs: - -Field Names with Dots/Periods (``.``) and Dollar Signs (``$``) -============================================================== - -Using dots/periods (``.``) in fields names and starting a field name with -a dollar sign (``$``) is not recommended, as Mongoid provides limited support -for retrieving and operating on the documents stored in those fields. - -Both Mongoid and MongoDB query language (MQL) generally use the dot/period -character (``.``) to separate field names in a field path that traverses -embedded documents, and words beginning with the dollar sign (``$``) as -operators. MongoDB provides `limited support -`_ -for using field names containing dots and starting with the dollar sign -for interoperability with other software, -however, due to this support being confined to specific operators -(e.g. :manual:`getField `, -:manual:`setField `) and -requiring the usage of the aggregation pipeline for both queries and updates, -applications should avoid using dots in field names and starting field names -with the dollar sign if possible. - -Mongoid, starting in version 8, now allows users to access fields that begin with -dollar signs and that contain dots/periods. They can be accessed using the ``send`` -method as follows: - -.. code:: - - class User - include Mongoid::Document - field :"first.last", type: String - field :"$_amount", type: Integer - end - - user = User.first - user.send(:"first.last") - # => Mike.Trout - user.send(:"$_amount") - # => 42650000 - -It is also possible to use ``read_attribute`` to access these fields: - -.. code:: - - user.read_attribute("first.last") - # => Mike.Trout - -Due to `server limitations `_, -updating and replacing fields containing dots and dollars requires using special -operators. For this reason, calling setters on these fields is prohibited and -will raise an error: - -.. code:: - - class User - include Mongoid::Document - field :"first.last", type: String - field :"$_amount", type: Integer - end - - user = User.new - user.send(:"first.last=", "Shohei.Ohtani") - # raises a InvalidDotDollarAssignment error - user.send(:"$_amount=", 8500000) - # raises a InvalidDotDollarAssignment error - diff --git a/source/reference/indexes.txt b/source/reference/indexes.txt deleted file mode 100644 index f5de6805..00000000 --- a/source/reference/indexes.txt +++ /dev/null @@ -1,279 +0,0 @@ -.. _mongoid-indexes: -.. _indexes: - -**************** -Index Management -**************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Specifying Indexes -================== - -You can define indexes on documents using the index macro. Provide the key for -the index along with a direction. Additional options can be supplied in the -second options hash parameter: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - - index({ ssn: 1 }, { unique: true, name: "ssn_index" }) - end - -You can define indexes on embedded document fields as well: - -.. code-block:: ruby - - class Person - include Mongoid::Document - embeds_many :addresses - index "addresses.street" => 1 - end - -You can index on multiple fields and provide direction: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :first_name - field :last_name - - index({ first_name: 1, last_name: 1 }, { unique: true }) - end - -Indexes can be sparse: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - - index({ ssn: -1 }, { sparse: true }) - end - -For geospatial indexes, make sure the field being indexed is of type Array: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :location, type: Array - - index({ location: "2d" }, { min: -200, max: 200 }) - end - -Indexes can be scoped to a specific database: - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - index({ ssn: 1 }, { database: "users", unique: true, background: true }) - end - -You may use aliased field names in index definitions. Field aliases -will also be resolved on the following options: ``partial_filter_expression``, -``weights``, ``wildcard_projection``. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :a, as: :age - index({ age: 1 }, { partial_filter_expression: { age: { '$gte' => 20 } }) - end - -.. note:: - - The expansion of field name aliases in index options such as - ``partial_filter_expression`` is performed according to the behavior of MongoDB - server 6.0. Future server versions may change how they interpret these options, - and Mongoid's functionality may not support such changes. - -Mongoid can define indexes on "foreign key" fields for associations. -This only works on the association macro that the foreign key is stored on: - -.. code-block:: ruby - - class Comment - include Mongoid::Document - belongs_to :post, index: true - has_and_belongs_to_many :preferences, index: true - end - -*Deprecated:* In MongoDB 4.0 and earlier, users could control whether to build indexes -in the foreground (blocking) or background (non-blocking, but less efficient) using the -``background`` option. - -.. code-block:: ruby - - class Person - include Mongoid::Document - field :ssn - index({ ssn: 1 }, { unique: true, background: true }) - end - -The default value of ``background`` is controlled by Mongoid's -``background_indexing`` :ref:`configuration option `. - -The ``background`` option has `no effect as of MongoDB 4.2 -`_. - - -Specifying Search Indexes on MongoDB Atlas -========================================== - -If your application is connected to MongoDB Atlas, you can declare and manage -search indexes on your models. (This feature is only available on MongoDB -Atlas.) - -To declare a search index, use the ``search_index`` macro in your model: - -.. code-block:: ruby - - class Message - include Mongoid::Document - - search_index { ... } - search_index :named_index, { ... } - end - -Search indexes may be given an explicit name; this is necessary if you have -more than one search index on a model. - - -Index Management Rake Tasks -=========================== - -When you want to create the indexes in the database, use the provided -``db:mongoid:create_indexes`` Rake task: - -.. code-block:: bash - - $ rake db:mongoid:create_indexes - -Mongoid also provides a Rake task to delete all secondary indexes. - -.. code-block:: bash - - $ rake db:mongoid:remove_indexes - -Note: the output of these Rake tasks goes to the default logger configured -by Rails. This is usually a file like ``log/development.log`` and not standard -output. - -These create/remove indexes commands also works for just one model by running -in Rails console: - -.. code-block:: ruby - - # Create indexes for Model - Model.create_indexes - - # Remove indexes for Model - Model.remove_indexes - -Managing Search Indexes on MongoDB Atlas ----------------------------------------- - -If you have defined search indexes on your model, there are rake tasks available -for creating and removing those search indexes: - -.. code-block:: bash - - $ rake db:mongoid:create_search_indexes - $ rake db:mongoid:remove_search_indexes - -By default, creating search indexes will wait for the indexes to be created, -which can take quite some time. If you want to simply let the database create -the indexes in the background, you can set the ``WAIT_FOR_SEARCH_INDEXES`` -environment variable to 0, like this: - -.. code-block:: bash - - $ rake WAIT_FOR_SEARCH_INDEXES=0 db:mongoid:create_search_indexes - -Note that the task for removing search indexes will remove all search indexes -from all models, and should be used with caution. - -You can also add and remove search indexes for a single model by invoking the -following in a Rails console: - -.. code-block:: ruby - - # Create all defined search indexes on the model; this will return - # immediately and the indexes will be created in the background. - Model.create_search_indexes - - # Remove all search indexes from the model - Model.remove_search_indexes - - # Enumerate all search indexes on the model - Model.search_indexes.each { |index| ... } - - -Telling Mongoid Where to Look For Models ----------------------------------------- - -For non-Rails applications, Mongoid's rake tasks will look for models in -``./app/models`` and ``./lib/models``. For Rails, Mongoid will look in -``./app/models`` (or wherever you've configured Rails to look for models). If -your models are in another location, you will need to tell Mongoid where to -look for them with ``Mongoid.model_paths=``. You can do this by setting it -in your application's Rakefile: - -.. code-block:: ruby - - # Rakefile - - # keep the defaults, but add more paths to look for models - Mongoid.model_paths += [ "./src/models", "./lib/documents" ] - - # or, override the defaults entirely - Mongoid.model_paths = [ "./src/models", "./lib/documents" ] - -Make sure that these paths are in your application's load path, as well. For -example: - -.. code-block:: ruby - - # Rakefile - - $LOAD_PATHS.concat [ "./src/models", "./lib/documents" ] - - -Using Rake Tasks With Non-Rails Applications --------------------------------------------- - -Mongoid's Rake tasks are automatically loaded in Rails applications using -Mongoid. When using Mongoid with a non-Rails application, these tasks must -be loaded manually: - -.. code-block:: ruby - - # Rakefile - - require 'mongoid' - load 'mongoid/tasks/database.rake' - -If your application uses Bundler, you can require ``bundler/setup`` instead of -explicitly requiring ``mongoid``: - -.. code-block:: ruby - - # Rakefile - - require 'bundler/setup' - load 'mongoid/tasks/database.rake' \ No newline at end of file diff --git a/source/reference/inheritance.txt b/source/reference/inheritance.txt deleted file mode 100644 index dcba02a9..00000000 --- a/source/reference/inheritance.txt +++ /dev/null @@ -1,308 +0,0 @@ -.. _inheritance: - -*********** -Inheritance -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -.. _inheritance-overview: - -Overview -======== - -Mongoid supports inheritance in both top level and embedded documents. When -a child document inherits from a parent document, the parent document's -fields, associations, validations and scopes are copied to the child document. - -.. code-block:: ruby - - class Canvas - include Mongoid::Document - field :name, type: String - embeds_many :shapes - end - - class Browser < Canvas - field :version, type: Integer - scope :recent, ->{ where(:version.gt => 3) } - end - - class Firefox < Browser - end - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -In the above example, ``Canvas``, ``Browser`` and ``Firefox`` will all save in the canvases -collection. An additional attribute ``_type`` is stored in order to make sure when loaded -from the database the correct document is returned. This also holds true for the embedded -documents ``Circle``, ``Rectangle``, and ``Shape``. - -.. note:: - - When searching for a ``Circle``, the query will only return documents in the shape collection - where the ``_type`` (or whatever the discriminator key was set to) field has the value ``Circle`` (or - whatever the discriminator value was set to), all other discriminator values will be considered an object - of the Shape class. - - Similarly, when querying by parent classes (``Canvas`` in this example), any documents in the collection - that do not have a discriminator value, or whose discriminator value does not map to either the parent - or any of its descendants, will be returned as instances of the parent class. - - -.. _discriminator-key: - -Changing the Discriminator Key -============================== - -Mongoid supports changing the discriminator key from the default ``_type``. There are a few -cases where one might want to do this: - -1. For optimization: The user might want to use a shorter key like ``_t``. - -2. When trying to work with an existing system: It's possible the user is working with an existing system or dataset that has predefined keys. - - -There are two ways to change the discriminator key, on the class level and on the global level. -To change the discriminator key on the class level the user can set it directly on the parent class using -the ``discriminator_key=`` method. -Take the above example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - - self.discriminator_key = "shape_type" - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -Here a call to the ``discriminator_key=`` setter was added to the parent class. Now, on -creation of a Rectangle or Circle, a ``shape_type`` field will be added. - -Note that the discriminator key can only be modified in the parent class, and an error -will be raised if trying to set it on the child class. - -If the discriminator key is changed after the child class is created, a new field is -added with the new discriminator key value, and the old field will remain unchanged. -For example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - - Shape.discriminator_key = "shape_type" - -In this case, on creation of a Rectangle or Circle, there will be both a ``shape_type`` -and a ``_type`` field that both default to ``Rectangle`` or ``Circle`` respectively. - - -The discriminator key can also be set on the global level. Meaning, all classes will -use the globally set discriminator key instead of ``_type``. Take the above example: - -.. code-block:: ruby - - Mongoid.discriminator_key = "_the_type" - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -After setting the global discriminator key, all classes will use ``_the_type`` as -the discriminator key and will not contain a ``_type`` field. - -Note that when defining the discriminator key on the global level, it must be set before the -child class is defined for the child class to use that global value. -On the global level, however, if the user does not set the discriminator key before defining a child -class, the discriminator field will use the default ``_type`` and not the new global setting in -that child class. - - -.. _discriminator-value: - -Changing the Discriminator Value -================================ - -Mongoid also supports changing the discriminator value from the default value, which is the class name. -One can change the discriminator value by using the ``discriminator_value=`` method on that specific class. - -Take the above example: - -.. code-block:: ruby - - class Shape - include Mongoid::Document - field :x, type: Integer - field :y, type: Integer - embedded_in :canvas - end - - class Circle < Shape - field :radius, type: Float - - self.discriminator_value = "round thing" - end - - class Rectangle < Shape - field :width, type: Float - field :height, type: Float - end - -Here, a call to the ``discriminator_value=`` setter was added to ``Circle``. -Now, on creation of a ``Circle``, the document will contain a field with the key ``_type`` (or whatever the ``discriminator_key`` was changed to) -and the value "round thing." - -.. note:: - - Because the discriminator value overrides are declared in child classes, - the child classes potentially found by a query must be loaded prior to - sending that query. In the above example, the ``Circle`` class definition - must be loaded when querying on ``Shape`` if the returned documents could - potentially be instances of ``Circle`` (since autoloading wouldn't resolve - ``"round thing"`` to ``Circle``). - - -Querying Subclasses -=================== - -Querying for subclasses is handled in the normal manner, and although the documents are -all in the same collection, queries will only return documents of the correct type, -similar to Single Table Inheritance in ActiveRecord. - -.. code-block:: ruby - - # Returns Canvas documents and subclasses - Canvas.where(name: "Paper") - # Returns only Firefox documents - Firefox.where(name: "Window 1") - - -Associations -============ - -You can add any type of subclass to a has one or has many association, through -either normal setting or through the build and create methods on the association: - -.. code-block:: ruby - - firefox = Firefox.new - # Builds a Shape object - firefox.shapes.build({ x: 0, y: 0 }) - # Builds a Circle object - firefox.shapes.build({ x: 0, y: 0 }, Circle) - # Creates a Rectangle object - firefox.shapes.create({ x: 0, y: 0 }, Rectangle) - - rect = Rectangle.new(width: 100, height: 200) - firefox.shapes - - -.. _inheritance-persistence-context: - -Persistence Contexts -==================== - -Mongoid allows the persistence context of a subclass to be changed from the -persistence context of its parent. This means that, using the ``store_in`` -method, we can store the documents of the subclasses in different collections -(as well as different databases, clients) than their parents: - -.. code:: ruby - - class Shape - include Mongoid::Document - store_in collection: :shapes - end - - class Circle < Shape - store_in collection: :circles - end - - class Square < Shape - store_in collection: :squares - end - - Shape.create! - Circle.create! - Square.create! - -Setting the collection on the children causes the documents for those children -to be stored in the set collection, instead of in the parent's collection: - -.. code:: javascript - - > db.shapes.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e10"), "_type" : "Shape" } - > db.circles.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e11"), "_type" : "Circle" } - > db.squares.find() - { "_id" : ObjectId("62fe9a493282a43d6b725e12"), "_type" : "Square" } - -If the collection is set on some of the subclasses and not others, the subclasses -with set collections will store documents in those collections, and the -subclasses without set collections will be store documents in the parent's -collection. - -.. note:: - - Note that changing the collection that a subclass is stored in will cause - documents of that subclass to no longer be found in the results of querying - its parent class. diff --git a/source/reference/nested-attributes.txt b/source/reference/nested-attributes.txt deleted file mode 100644 index 5803206e..00000000 --- a/source/reference/nested-attributes.txt +++ /dev/null @@ -1,140 +0,0 @@ -.. _nested-attributes: - -***************** -Nested Attributes -***************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Nested attributes provide a mechanism for updating documents and their -associations in a single operation by nesting attributes in a single -parameters hash. This is useful when wanting to edit multiple documents -within a single web form. - -Behavior -======== - -Nested attributes can be enabled for any association, embedded or referenced. -To enable this for an association, simply provide the association name to the -``accepts_nested_attributes_for`` macro. - -.. code-block:: ruby - - class Band - include Mongoid::Document - embeds_many :albums - belongs_to :producer - accepts_nested_attributes_for :albums, :producer - end - -Note that when you add nested attributes functionality to a referenced -association, Mongoid will automatically enable autosave for that association. - -When an association gains nested attributes behavior, an additional method is -added to the base model, which should be used to update the attributes with -the new functionality. This method is the association name plus ``_attributes=``. -You can use this method directly, or more commonly the name of the method can -be an attribute in the updates for the base class, in which case -Mongoid will call the appropriate setter under the covers. - -.. code-block:: ruby - - band = Band.first - band.producer_attributes = { name: "Flood" } - band.attributes = { producer_attributes: { name: "Flood" }} - -Note that this will work with any attribute based setter method in Mongoid, -including ``update``, ``update_attributes`` and ``attributes=``, as well as -``create`` (and all of their corresponding bang methods). For example, creating -a new person with associated address records can be done in a single -statement, like this: - -.. code-block:: ruby - - person = Person.create( - name: 'John Schmidt', - addresses_attributes: [ - { type: 'home', street: '1234 Street Ave.', city: 'Somewhere' }, - { type: 'work', street: 'Parkway Blvd.', city: 'Elsewehre' }, - ]) - - -Creating Records ----------------- - -You can create new nested records via nested attributes by omitting -an ``_id`` field: - -.. code-block:: ruby - - person = Person.first - person.update(addresses_attributes: [ - { type: 'prior', street: '221B Baker St', city: 'London' } ]) - -This will append the new record to the existing set; existing records will -not be changed. - - -Updating Records ----------------- - -If you specify an ``_id`` field for any of the nested records, the attributes -will be used to update the record with that id: - -.. code-block:: ruby - - person = Person.first - address = person.addresses.first - person.update(addresses_attributes: [ - { _id: address._id, city: 'Lisbon' } ]) - -Note that if there is no record with that id, a ``Mongoid::Errors::DocumentNotFound`` -exception will be raised. - - -Destroying Records ------------------- - -You can also destroy records this way, by specifying a special -``_destroy`` attribute. In order to use this, you must have passed -``allow_destroy: true`` with the ``accepts_nested_attributes_for`` -declaration: - -.. code-block:: ruby - - class Person - # ... - - accepts_nested_attributes_for :addresses, allow_destroy: true - end - - person = Person.first - address = person.addresses.first - person.update(addresses_attributes: [ - { _id: address._id, _destroy: true } ]) - -Note that, as with updates, if there is no record with that id, -a ``Mongoid::Errors::DocumentNotFound`` exception will be raised. - - -Combining Operations --------------------- - -Nested attributes allow you to combine all of these operations in -a single statement! Here's an example that creates an address, -updates another address, and destroys yet another address, all in -a single command: - -.. code-block:: ruby - - person = Person.first - person.update(addresses_attributes: [ - { type: 'alt', street: '1234 Somewhere St.', city: 'Cititon' }, - { _id: an_address_id, city: 'Changed City' }, - { _id: another_id, _destroy: true } ]) diff --git a/source/reference/queries.txt b/source/reference/queries.txt deleted file mode 100644 index 0ef52259..00000000 --- a/source/reference/queries.txt +++ /dev/null @@ -1,2480 +0,0 @@ -.. _queries: - -******* -Queries -******* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Mongoid provides a rich query DSL inspired by ActiveRecord. A trivial query -looks as follows: - -.. code-block:: ruby - - Band.where(name: "Depeche Mode") - -A more complex query utilizing various Mongoid features could be as follows: - -.. code-block:: ruby - - Band. - where(:founded.gte => "1980-01-01"). - in(name: [ "Tool", "Deftones" ]). - union. - in(name: [ "Melvins" ]) - -The query methods return ``Mongoid::Criteria`` objects, which are chainable -and lazily evaluated wrappers for MongoDB query language (MQL). -The queries are executed when their result sets are iterated. For example: - -.. code-block:: ruby - - # Construct a Criteria object: - - Band.where(name: 'Deftones') - # => #"Deftones"} - # options: {} - # class: Band - # embedded: false> - - # Evaluate the query and get matching documents: - - Band.where(name: 'Deftones').to_a - # => [#] - -Methods like ``first`` and ``last`` return the individual documents immediately. -Otherwise, iterating a Criteria object with methods like ``each`` or ``map`` -retrieves the documents from the server. ``to_a`` can be used to force -execution of a query that returns an array of documents, literally converting -a Criteria object to an Array. - -When a query method is called on a Criteria instance, the method returns a new -Criteria instance with the new conditions added to the existing conditions: - -.. code-block:: ruby - - scope = Band.where(:founded.gte => "1980-01-01") - # => #{"$gte"=>"1980-01-01"}} - # options: {} - # class: Band - # embedded: false> - - scope.where(:founded.lte => "2020-01-01") - # => #{"$gte"=>"1980-01-01", "$lte"=>"2020-01-01"}} - # options: {} - # class: Band - # embedded: false> - - scope - # => #{"$gte"=>"1980-01-01"}} - # options: {} - # class: Band - # embedded: false> - - -Condition Syntax -================ - -Mongoid supports three ways of specifying individual conditions: - -1. Field syntax. -2. MQL syntax. -3. Symbol operator syntax. - -All syntaxes support querying embedded documents using the dot notation. -All syntaxes respect field types, if the field being queried is defined in the -model class, and field aliases. - -The examples in this section use the following model definition: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :founded, type: Integer - field :m, as: :member_count, type: Integer - - embeds_one :manager - end - - class Manager - include Mongoid::Document - - embedded_in :band - - field :name, type: String - end - -Field Syntax ------------- - -The simplest querying syntax utilizes the basic Ruby hashes. Keys can be -symbols or strings, and correspond to field names in MongoDB documents: - -.. code-block:: ruby - - Band.where(name: "Depeche Mode") - # => #"Depeche Mode"} - # options: {} - # class: Band - # embedded: false> - - # Equivalent to: - - Band.where("name" => "Depeche Mode") - -MQL Syntax ----------- - -An MQL operator may be specified on any field using the hash syntax: - -.. code-block:: ruby - - Band.where(founded: {'$gt' => 1980}) - # => #{"$gt"=>1980}} - # options: {} - # class: Band - # embedded: false> - - # Equivalent to: - - Band.where('founded' => {'$gt' => 1980}) - -Symbol Operator Syntax ----------------------- - -MQL operators may be specified as methods on symbols for the respective field -name, as follows: - -.. code-block:: ruby - - Band.where(:founded.gt => 1980) - # => #{"$gt"=>1980}} - # options: {} - # class: Band - # embedded: false> - - -Fields -====== - -Querying on Defined Fields --------------------------- - -In order to query on a field, it is not necessary to add the field to -:ref:`the model class definition `. However, if a field is defined in -the model class, Mongoid will coerce query values to match defined field types -when constructing the query: - -.. code-block:: ruby - - Band.where(name: 2020, founded: "2020") - # => #"2020", "founded"=>2020} - # options: {} - # class: Band - # embedded: false> - -Querying for Raw Values ------------------------ - -If you'd like to bypass Mongoid's query type coercion behavior and query -directly for the raw-typed value in the database, wrap the query value in -``Mongoid::RawValue`` class. This can be useful when working with legacy data. - -.. code-block:: ruby - - Band.where(founded: Mongoid::RawValue("2020")) - # => #"2020"} - # options: {} - # class: Band - # embedded: false> - -Field Aliases -------------- - -Queries take into account :ref:`storage field names ` -and :ref:`field aliases `: - -.. code-block:: ruby - - Band.where(name: 'Astral Projection') - # => #"Astral Projection"} - # options: {} - # class: Band - # embedded: false> - -Since ``id`` and ``_id`` fields are aliases, either one can be used for queries: - -.. code-block:: ruby - - Band.where(id: '5ebdeddfe1b83265a376a760') - # => #BSON::ObjectId('5ebdeddfe1b83265a376a760')} - # options: {} - # class: Band - # embedded: false> - - -Embedded Documents -================== - -To match values of fields of embedded documents, use the dot notation: - -.. code-block:: ruby - - Band.where('manager.name' => 'Smith') - # => #"Smith"} - # options: {} - # class: Band - # embedded: false> - - Band.where(:'manager.name'.ne => 'Smith') - # => #{"$ne"=>"Smith"}} - # options: {} - # class: Band - # embedded: false> - -.. note:: - - Queries always return top-level model instances, even if all of the - conditions are referencing embedded documents. - - -.. _logical-operations: - -Logical Operations -================== - -Mongoid supports ``and``, ``or``, ``nor`` and ``not`` logical operations on -``Criteria`` objects. These methods take one or more hash of conditions -or another ``Criteria`` object as their arguments, with ``not`` additionally -having an argument-free version. - -.. code-block:: ruby - - # and with conditions - Band.where(label: 'Trust in Trance').and(name: 'Astral Projection') - - # or with scope - Band.where(label: 'Trust in Trance').or(Band.where(name: 'Astral Projection')) - - # not with conditions - Band.not(label: 'Trust in Trance', name: 'Astral Projection') - - # argument-less not - Band.not.where(label: 'Trust in Trance', name: 'Astral Projection') - -For backwards compatibility with earlier Mongoid versions, all of the logical -operation methods also accept arrays of parameters, which will be flattened -to obtain the criteria. Passing arrays to logical operations is deprecated and -may be removed in a future version of Mongoid. - -The following calls all produce the same query conditions: - -.. code-block:: ruby - - # Condition hashes passed to separate and invocations - Band.and(name: 'SUN Project').and(member_count: 2) - - # Multiple condition hashes in the same and invocation - Band.and({name: 'SUN Project'}, {member_count: 2}) - - # Multiple condition hashes in an array - deprecated - Band.and([{name: 'SUN Project'}, {member_count: 2}]) - - # Condition hash in where and a scope - Band.where(name: 'SUN Project').and(Band.where(member_count: 2)) - - # Condition hash in and and a scope - Band.and({name: 'SUN Project'}, Band.where(member_count: 2)) - - # Scope as an array element, nested arrays - deprecated - Band.and([Band.where(name: 'SUN Project'), [{member_count: 2}]]) - - # All produce: - # => #"SUN Project", "member_count"=>2} - # options: {} - # class: Band - # embedded: false> - - -Operator Combinations ---------------------- - -As of Mongoid 7.1, logical operators (``and``, ``or``, ``nor`` and ``not``) -have been changed to have the the same semantics as `those of ActiveRecord -`_. -To obtain the semantics of ``or`` as it behaved in Mongoid 7.0 and earlier, -use ``any_of`` which is described below. - -When conditions are specified on the same field multiple times, all -conditions are added to the criteria: - -.. code-block:: ruby - - Band.where(name: 1).where(name: 2).selector - # => {"name"=>"1", "$and"=>[{"name"=>"2"}]} - - Band.where(name: 1).or(name: 2).selector - # => {"$or"=>[{"name"=>"1"}, {"name"=>"2"}]} - -``any_of``, ``none_of``, ``nor`` and ``not`` behave similarly, with ``not`` producing -different query shapes as described below. - -When ``and``, ``or`` and ``nor`` logical operators are used, they -operate on the criteria built up to that point and its argument. -``where`` has the same meaning as ``and``: - -.. code-block:: ruby - - # or joins the two conditions - Band.where(name: 'Sun').or(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]} - - # or applies only to the first condition, the second condition is added - # to the top level as $and - Band.or(name: 'Sun').where(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"} - - # Same as previous example - where and and are aliases - Band.or(name: 'Sun').and(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}], "label"=>"Trust"} - - # Same operator can be stacked any number of times - Band.or(name: 'Sun').or(label: 'Trust').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}]} - - # The label: Foo condition is added to the top level as $and - Band.where(name: 'Sun').or(label: 'Trust').where(label: 'Foo').selector - # => {"$or"=>[{"name"=>"Sun"}, {"label"=>"Trust"}], "label"=>"Foo"} - - -``and`` Behavior ----------------- - -The ``and`` method will add new simple conditions to the top level of the -criteria, unless the receiving criteria already has a condition on the -respective fields, in which case the conditions will be combined with ``$and``. - -.. code-block:: ruby - - Band.where(label: 'Trust in Trance').and(name: 'Astral Projection').selector - # => {"label"=>"Trust in Trance Records", "name"=>"Astral Projection"} - - Band.where(name: /Best/).and(name: 'Astral Projection').selector - # => {"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]} - -As of Mongoid 7.1, specifying multiple criteria on the same field with ``and`` -combines all criteria so specified, whereas in previous versions of Mongoid -conditions on a field sometimes replaced previously specified conditions on -the same field, depending on which form of ``and`` was used. - - -``or``/``nor`` Behavior -_---------------------- - -``or`` and ``nor`` produce ``$or`` and ``$nor`` MongoDB operators, respectively, -using the receiver and all of the arguments as operands. For example: - -.. code-block:: ruby - - Band.where(name: /Best/).or(name: 'Astral Projection') - # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}]} - - Band.where(name: /Best/).and(name: 'Astral Projection'). - or(Band.where(label: /Records/)).and(label: 'Trust').selector - # => {"$or"=>[{"name"=>/Best/, "$and"=>[{"name"=>"Astral Projection"}]}, {"label"=>/Records/}], "label"=>"Trust"} - -If the only condition on the receiver is another ``or``/``nor``, the new -conditions are added to the existing list: - -.. code-block:: ruby - - Band.where(name: /Best/).or(name: 'Astral Projection'). - or(Band.where(label: /Records/)).selector - # => {"$or"=>[{"name"=>/Best/}, {"name"=>"Astral Projection"}, {"label"=>/Records/}]} - -Use ``any_of`` to add a disjunction to a Criteria object while maintaining -all of the conditions built up so far as they are. - - -.. _any-of: - -``any_of`` Behavior -------------------- - -``any_of`` adds a disjunction built from its arguments to the existing -conditions in the criteria. For example: - -.. code-block:: ruby - - Band.where(label: /Trust/).any_of({name: 'Astral Projection'}, {name: /Best/}) - # => {"label"=>/Trust/, "$or"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]} - -The conditions are hoisted to the top level if possible: - -.. code-block:: ruby - - Band.where(label: /Trust/).any_of({name: 'Astral Projection'}) - # => {"label"=>/Trust/, "name"=>"Astral Projection"} - - -.. _none-of: - -``none_of`` Behavior --------------------- - -``none_of`` adds a negated disjunction ("nor") built from its arguments to -the existing conditions in the criteria. For example: - -.. code-block:: ruby - - Band.where(label: /Trust/).none_of({name: 'Astral Projection'}, {name: /Best/}) - # => {"label"=>/Trust/, "$nor"=>[{"name"=>"Astral Projection"}, {"name"=>/Best/}]} - - -``not`` Behavior ----------------- - -``not`` method can be called without arguments, in which case it will negate -the next condition that is specified. ``not`` can also be called with one -or more hash conditions or ``Criteria`` objects, which will all be negated and -added to the criteria. - -.. code-block:: ruby - - # not negates subsequent where - Band.not.where(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - - # The second where is added as $and - Band.not.where(name: 'Best').where(label: /Records/).selector - # => {"name"=>{"$ne"=>"Best"}, "label"=>/Records/} - - # not negates its argument - Band.not(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - -.. note:: - - ``$not`` in MongoDB server cannot be used with a string argument. - Mongoid uses ``$ne`` operator to achieve such a negation: - - .. code-block:: ruby - - # String negation - uses $ne - Band.not.where(name: 'Best').selector - # => {"name"=>{"$ne"=>"Best"}} - - # Regexp negation - uses $not - Band.not.where(name: /Best/).selector - # => {"name"=>{"$not"=>/Best/}} - -Similarly to ``and``, ``not`` will negate individual conditions for simple -field criteria. For complex conditions and when a field already has a condition -defined on it, since MongoDB server only supports the ``$not`` operator on -a per-field basis rather than globally, Mongoid emulates ``$not`` by using -an ``{'$and' => [{'$nor' => ...}]}`` construct: - -.. code-block:: ruby - - # Simple condition - Band.not(name: /Best/).selector - # => {"name"=>{"$not"=>/Best/}} - - # Complex conditions - Band.where(name: /Best/).not(name: 'Astral Projection').selector - # => {"name"=>/Best/, "$and"=>[{"$nor"=>[{"name"=>"Astral Projection"}]}]} - - # Symbol operator syntax - Band.not(:name.ne => 'Astral Projection') - # => #[{"$nor"=>[{"name"=>{"$ne"=>"Astral Projection"}}]}]} - # options: {} - # class: Band - # embedded: false> - -If using ``not`` with arrays or regular expressions, please note the -caveats/limitations of ``$not`` `stated in the MongoDB server documentation -`_. - - -Incremental Query Construction -============================== - -By default, when conditions are added to a query, Mongoid considers each -condition complete and independent from any other conditions potentially -present in the query. For example, calling ``in`` twice adds two separate -``$in`` conditions: - -.. code-block:: ruby - - Band.in(name: ['a']).in(name: ['b']) - => #{"$in"=>["a"]}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} - options: {} - class: Band - embedded: false> - -Some operator methods support building the condition incrementally. In this -case, when an condition on a field which uses one of the supported operators -is being added, if there already is a condition on the same field using the -same operator, the operator expressions are combined according to the -specified *merge strategy*. - -.. _merge-strategies: - -Merge Strategies ----------------- - -Mongoid provides three merge strategies: - -- **Override**: the new operator instance replaces any existing conditions on - the same field using the same operator. -- **Intersect**: if there already is a condition using the same operator on the - same field, the values of the existing condition are intersected with the - values of the new condition and the result is stored as the operator value. -- **Union**: if there already is a condition using the same operator on the - same field, the values of the new condition are added to the values of the - existing condition and the result is stored as the operator value. - -The following snippet demonstrates all of the strategies, using ``in`` as the -example operator: - -.. code-block:: ruby - - Band.in(name: ['a']).override.in(name: ['b']) - => #{"$in"=>["b"]}} - options: {} - class: Band - embedded: false> - - Band.in(name: ['a', 'b']).intersect.in(name: ['b', 'c']) - => #{"$in"=>["b"]}} - options: {} - class: Band - embedded: false> - - Band.in(name: ['a']).union.in(name: ['b']) - => #{"$in"=>["a", "b"]}} - options: {} - class: Band - embedded: false> - -The strategy is requested by calling ``override``, ``intersect`` or ``union`` -on a ``Criteria`` instance. The requested strategy applies to the next -condition method called on the query. If the next condition method called does -not support merge strategies, the strategy is reset, as shown in the following -example: - -.. code-block:: ruby - - Band.in(name: ['a']).union.ne(name: 'c').in(name: ['b']) - => #{"$in"=>["a"], "$ne"=>"c"}, "$and"=>[{"name"=>{"$in"=>["b"]}}]} - options: {} - class: Band - embedded: false> - -Since ``ne`` does not support merge strategies, the ``union`` strategy was -ignored and reset and when ``in`` was invoked the second time there was no -strategy active. - -.. warning:: - - Merge strategies currently assume the previous condition(s) have been added - to the top level of the query, however this is not always the case - (conditions may be nested under an ``$and`` clause). Using merge strategies - with complex criteria may cause incorrect queries to be constructed. - This misbehavior is `intended to be fixed in the future - `_. - - -Supported Operator Methods --------------------------- - -The following operator methods support merge strategies: - -- ``all`` -- ``in`` -- ``nin`` - -The set of methods may be expanded in future releases of Mongoid. For -future compatibility, only invoke a strategy method when the next method call -is an operator that supports merge strategies. - -Note that the merge strategies are currently only applied when conditions are -added through the designated methods. In the following example merge strategy -is not applied because the second condition is added via ``where``, not via -``in``: - -.. code-block:: ruby - - Band.in(foo: ['a']).union.where(foo: {'$in' => 'b'}) - => #{"$in"=>["a"]}, "$and"=>[{"foo"=>{"$in"=>"b"}}]} - options: {} - class: Band - embedded: false> - -This behavior may change in a future release of Mongoid and should not be -relied upon. - -In contrast, it does not matter how the existing query was built when a -merge strategy-supporting operator method is invoked. In the following -example, the first condition was added through ``where`` but the strategy -mechanism still applies: - -.. code-block:: ruby - - Band.where(foo: {'$in' => ['a']}).union.in(foo: ['b']) - => #{"$in"=>["a", "b"]}} - options: {} - class: Band - embedded: false> - -Operator Value Expansion ------------------------- - -Operator methods that support merge strategies all take ``Array`` as their value -type. Mongoid expands ``Array``-compatible types, such as a ``Range``, -when they are used with these operator methods: - -.. code-block:: ruby - - Band.in(year: 1950..1960) - => #{"$in"=>[1950, 1951, 1952, 1953, 1954, 1955, 1956, 1957, 1958, 1959, 1960]}} - options: {} - class: Band - embedded: false> - -Additionally, Mongoid has historically wrapped non-``Array`` values in arrays, -as the following example demonstrates: - -.. code-block:: ruby - - Band.in(year: 1950) - => #{"$in"=>[1950]}} - options: {} - class: Band - embedded: false> - - -Query Methods -============= - -elem_match ----------- - -This matcher finds documents with array fields where one of the array values -matches all of the conditions. For example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tours, type: Array - end - - aerosmith = Band.create!(name: 'Aerosmith', tours: [ - {city: 'London', year: 1995}, - {city: 'New York', year: 1999}, - ]) - - Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith] - -``elem_match`` also works with embedded associations: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - embeds_many :tours - end - - class Tour - include Mongoid::Document - field :city, type: String - field :year, type: Integer - embedded_in :band - end - - dm = Band.create!(name: 'Depeche Mode') - aerosmith = Band.create!(name: 'Aerosmith') - Tour.create!(band: aerosmith, city: 'London', year: 1995) - Tour.create!(band: aerosmith, city: 'New York', year: 1999) - - Band.elem_match(tours: {city: 'London'}).to_a # => [aerosmith] - -``elem_match`` does not work with non-embedded associations because MongoDB -does not have joins - the conditions would be added to the collection -that is the source of a non-embedded association rather than the collection -of the association's target. - -``elem_match`` can also be used with recursively embedded associations, -as the following example shows: - -.. code-block:: ruby - - class Tag - include Mongoid::Document - field :name, type: String - recursively_embeds_many - end - - root = Tag.create!(name: 'root') - sub1 = Tag.new(name: 'sub1', child_tags: [Tag.new(name: 'subsub1')]) - root.child_tags << sub1 - root.child_tags << Tag.new(name: 'sub2') - root.save! - - Tag.elem_match(child_tags: {name: 'sub1'}).to_a # => [root] - - root.child_tags.elem_match(child_tags: {name: 'subsub1'}).to_a # => [sub1] - - -.. _projection: - -Projection -========== - -Mongoid provides two projection operators: ``only`` and ``without``. - - -.. _only: - -``only`` --------- - -The ``only`` method retrieves only the specified fields from the database. This -operation is sometimes called "projection". - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :label, type: String - - embeds_many :tours - end - - class Tour - include Mongoid::Document - - field :city, type: String - field :year, type: Integer - - embedded_in :band - end - - band = Band.only(:name).first - -Attempting to reference attributes which have not been loaded results in -``Mongoid::Errors::AttributeNotLoaded``. - -.. code-block:: ruby - - band.label - #=> raises Mongoid::Errors::AttributeNotLoaded - -Even though Mongoid currently allows writing to attributes that have not -been loaded, such writes will not be persisted -(`MONGOID-4701 `_) and -should therefore be avoided. - -``only`` can also be used with embedded associations: - -.. code-block:: ruby - - band = Band.only(:name, 'tours.year').last - # => # - - band.tours.first - # => # - -.. note:: - - Server versions 4.2 and lower allowed projecting both an association and - the association's fields in the same query, as follows: - - .. code-block:: ruby - - band = Band.only(:tours, 'tours.year').last - - The most recent projection specification overrides the earlier one. - For example, the above query was equivalent to: - - .. code-block:: ruby - - band = Band.only('tours.year').last - - Server versions 4.4 and higher prohibit specifying an association and its - fields in projection in the same query. - -``only`` can be specified with referenced associations (has_one, has_many, -has_and_belongs_to_many) but is currently ignored for referenced associations - -all fields of referenced associations will be loaded -(`MONGOID-4704 `_). - -Note that if a document has ``has_one`` or ``has_and_belongs_to_many`` associations, -the fields with foreign keys must be included in the list of attributes -loaded with ``only`` for those associations to be loaded. For example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - - has_and_belongs_to_many :managers - end - - class Manager - include Mongoid::Document - - has_and_belongs_to_many :bands - end - - band = Band.create!(name: 'Astral Projection') - band.managers << Manager.new - - Band.where(name: 'Astral Projection').only(:name).first.managers - # => [] - - Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers - # => [#] - - -.. _without: - -``without`` ------------ - -The opposite of ``only``, ``without`` causes the specified fields to be omitted: - -.. code-block:: ruby - - Band.without(:name) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - -Because Mongoid requires the ``_id`` field for various operations, it (as well -as its ``id`` alias) cannot be omitted via ``without``: - -.. code-block:: ruby - - Band.without(:name, :id) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - - Band.without(:name, :_id) - # => - # #{"name"=>0}} - # class: Band - # embedded: false> - - -.. _ordering: - -Ordering -======== - -Mongoid provides the ``order`` method on ``Criteria`` objects and its alias, -``order_by``, to specify the ordering of documents. These methods take a -hash indicating which fields to order the documents by, and whether to use -ascending or descending order for each field. - -.. code-block:: ruby - - Band.order(name: 1) - # => #{"name"=>1}} - # class: Band - # embedded: false> - - Band.order_by(name: -1, description: 1) - # => #{"name"=>-1, "description"=>1}} - # class: Band - # embedded: false> - - Band.order_by(name: :desc, description: 'asc') - # => #{"name"=>-1, "description"=>1}} - # class: Band - # embedded: false> - -The direction may be specified as integers ``1`` and ``-1`` for ascending -and descending, respectively, or as symbols ``:asc`` and ``:desc``, or as -strings ``"asc"`` and ``"desc"``. - -Alternatively, ``order`` accepts an array of two-element arrays specifying -the ordering. Field names and directions may be strings or symbols. - -.. code-block:: ruby - - Band.order([['name', 'desc'], ['description', 'asc']]) - - Band.order([[:name, :desc], [:description, :asc]]) - -Another way of providing the order is to use ``#asc`` and ``#desc`` methods -on symbols, as follows: - -.. code-block:: ruby - - Band.order(:name.desc, :description.asc) - -The arguments can be provided as a string using SQL syntax: - -.. code-block:: ruby - - Band.order('name desc, description asc') - -Finally, there are ``asc`` and ``desc`` methods that can be used instead of -``order``/``order_by``: - -.. code-block:: ruby - - Band.asc('name').desc('description') - # => #{"name"=>1, "description"=>-1}} - class: Band - embedded: false> - -``order`` calls can be chained, in which case the oldest calls define the -most significant criteria and the newest calls define the least significant -ones (since in Ruby hashes maintain the order of their keys): - -.. code-block:: ruby - - Band.order('name desc').order('description asc') - # => #{"name"=>-1, "description"=>1}} - class: Band - embedded: false> - -This can sometimes lead to surprising results if there are scopes, including -the default scope, that use ``order``/``order_by``. For example, in the -following snippet bands are ordered by name first because the order in the -default scope takes precedence over the order given in the query, due to -the default scope being evaluated first: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :year, type: Integer - - default_scope -> { order(name: :asc) } - end - - Band.order(year: :desc) - # => #{"name"=>1, "year"=>-1}} - class: Band - embedded: false> - - -Pagination -========== - -Mongoid provides the pagination operators ``limit``, ``skip``, and ``batch_size`` on ``Criteria``. - -.. _limit: - -``limit`` ---------- - -``limit`` sets the total number of documents to be returned by a query: - -.. code-block:: ruby - - Band.limit(5) - # => - # #5} - # class: Band - # embedded: false> - -.. _skip: - -``skip`` --------- - -``skip`` (alias: ``offset``) sets the number of query results to skip -before returning documents. The ``limit`` value, if specified, will be applied -after documents are skipped. When performing pagination, ``skip`` is recommended -to be combined with :ref:`ordering ` to ensure consistent results. - -.. code-block:: ruby - - Band.skip(10) - # => - # #10} - # class: Band - # embedded: false> - -.. _batch-size: - -``batch_size`` --------------- - -When executing large queries, or when iterating over query results with an enumerator method such as -``Criteria#each``, Mongoid automatically uses the `MongoDB getMore command -`_ to load results in batches. -The default ``batch_size`` is 1000, however you may set it explicitly: - -.. code-block:: ruby - - Band.batch_size(500) - # => - # #500} - # class: Band - # embedded: false> - - -Finding By ``_id`` -================== - -Mongoid provides the ``find`` method on ``Criteria`` objects to find documents -by their ``_id`` values: - -.. code-block:: ruby - - Band.find('5f0e41d92c97a64a26aabd10') - # => # - -The ``find`` method performs type conversion, if necessary, of the argument -to the type declared in the model being queried for the ``_id`` field. -By default, the ``_id`` type is ``BSON::ObjectId``, thus the query above -is equivalent to: - -.. code-block:: ruby - - Band.find(BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')) - # => # - -.. note:: - - When querying collections directly using the driver, type conversion is not - automatically performed: - -.. code-block:: ruby - - Band.collection.find(_id: BSON::ObjectId.from_string('5f0e41d92c97a64a26aabd10')).first - # => {"_id"=>BSON::ObjectId('5f0e41d92c97a64a26aabd10'), "name"=>"Juno Reactor"} - - Band.collection.find(_id: '5f0e41d92c97a64a26aabd10').first - # => nil - -The ``find`` method can accept multiple arguments, or an array of arguments. -In either case each of the arguments or array elements is taken to be an ``_id`` -value, and documents with all of the specified ``_id`` values are returned in -an array: - -.. code-block:: ruby - - Band.find('5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e') - # => [#, - #] - - Band.find(['5f0e41d92c97a64a26aabd10', '5f0e41b02c97a64a26aabd0e']) - # => [#, - #] - -If the same ``_id`` value is given more than once, the corresponding document -is only returned once: - -.. code-block:: ruby - - Band.find('5f0e41b02c97a64a26aabd0e', '5f0e41b02c97a64a26aabd0e') - # => [#] - -The documents returned are *not* ordered, and may be returned in a different -order from the order of provided ``_id`` values, as illustrated in the above -examples. - -If any of the ``_id`` values are not found in the database, the behavior of -``find`` depends on the value of the ``raise_not_found_error`` configuration -option. If the option is set to ``true``, ``find`` raises -``Mongoid::Errors::DocumentNotFound`` if any of the ``_id``\s are not found. -If the option is set to ``false`` and ``find`` is given a single ``_id`` to -find and there is no matching document, ``find`` returns ``nil``. If the -option is set to ``false`` and ``find`` is given an array of ids to find -and some are not found, the return value is an array of documents that were -found (which could be empty if no documents were found at all). - - -.. _additional-query-methods: - -Additional Query Methods -======================== - -Mongoid also has some helpful methods on criteria. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Criteria#count`` - - *Get the total number of documents matching a filter, or the total - number of documents in a collection. Note this will always hit - the database for the count.* - - *As of Mongoid 7.2, the* ``count`` *method uses the* - ``count_documents`` *driver helper to obtain the accurate count. - previously the* ``count`` *driver helper was used which used - collection metadata and was thus not necessarily accurate (but - may have returned the result faster). Use* ``estimated_count`` - *method to obtain an approximate number of documents in the collection - quickly.* - - - - .. code-block:: ruby - - Band.count - Band.where(name: "Photek").count - - * - ``Criteria#estimated_count`` - - *Get an approximate number of documents in the collection using the - collection metadata. The* ``estimated_count`` *method does not accept - query conditions; if any are given, it will raise* - ``Mongoid::Errors::InvalidEstimatedCountCriteria``. - *If a model defines a default scope,* ``estimated_count`` *must be - called on the unscoped model*. - - - - .. code-block:: ruby - - Band.count - Band.where(name: "Photek").count - - class Contract - include Mongoid::Document - - field :active, type: Boolean - - default_scope -> { where(active: true) } - end - - Contract.estimated_count - # => raises Mongoid::Errors::InvalidEstimatedCountCriteria - - Contract.unscoped.estimated_count - # => 0 - - * - ``Criteria#distinct`` - - *Get a list of distinct values for a single field. Note this will always hit - the database for the distinct values.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.distinct(:name) - Band.where(:fans.gt => 100000). - distinct(:name) - - Band.distinct('cities.name') - - # Assuming an aliased field: - class Manager - include Mongoid::Document - embedded_in :band - field :name, as: :n - end - - # Expands out to "managers.name" in the query: - Band.distinct('managers.n') - - * - ``Criteria#each`` - - *Iterate over all matching documents in the criteria.* - - - - .. code-block:: ruby - - Band.where(members: 1).each do |band| - p band.name - end - - * - ``Criteria#exists?`` - - *Determine if any matching documents exist. Will return true if there - are 1 or more.* - - ``#exists?`` *now takes a number of argument types:* - - - ``Hash``: *A hash of conditions.* - - ``Object``: *An _id to search for.* - - ``false``/``nil``: *Always returns false.* - - - - .. code-block:: ruby - - Band.exists? - Band.where(name: "Photek").exists? - Band.exists?(name: "Photek") - Band.exists?(BSON::ObjectId('6320d96a3282a48cfce9e72c')) - Band.exists?('6320d96a3282a48cfce9e72c') - Band.exists?(false) - Band.exists?(nil) - - * - ``Criteria#fifth`` - - *Get the fifth document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fifth - - * - ``Criteria#fifth!`` - - *Get the fifth document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fifth! - - * - ``Criteria#find_by`` - - *Find a document by the provided attributes. If not found, - raise an error or return nil depending on the value of the* - ``raise_not_found_error`` *configuration option.* - - - - .. code-block:: ruby - - Band.find_by(name: "Photek") - - Band.find_by(name: "Tool") do |band| - band.impressions += 1 - end - - * - ``Criteria#find_or_create_by`` - - *Find a document by the provided attributes, and if not found - create and return a newly persisted one. Note that attributes provided in the arguments to - this method will override any set in ``create_with``*. - - - - .. code-block:: ruby - - Band.find_or_create_by(name: "Photek") - Band.where(:likes.gt => 10).find_or_create_by(name: "Photek") - - ``find_or_create_by`` can be used on any scope, but in this case - the criteria given by the scope and by ``find_or_create_by`` are - combined. The following creates three bands: - - .. code-block:: ruby - - Band.find_or_create_by(name: "Photek") - Band.where(name: "Photek").find_or_create_by(name: "Aerosmith") - # creates Aerosmith again because there is no band whose name - # is Photek and Aerosmith at the same time - Band.where(name: "Photek").find_or_create_by(name: "Aerosmith") - - * - ``Criteria#find_or_initialize_by`` - - *Find a document by the provided attributes, and if not found - return a new one.* - - - - .. code-block:: ruby - - Band.find_or_initialize_by(name: "Photek") - Band.where(:likes.gt => 10).find_or_initialize_by(name: "Photek") - - * - ``Criteria#first|last`` - - *Finds a single document given the provided criteria. Get a list of - documents by passing in a limit argument. This method automatically adds - a sort on _id. This can cause performance issues, so if the sort is - undesirable, Criteria#take can be used instead.* - - - - .. code-block:: ruby - - Band.first - Band.where(:members.with_size => 3).first - Band.where(:members.with_size => 3).last - Band.first(2) - - * - ``Criteria#first!|last!`` - - *Finds a single document given the provided criteria, or raises an error - if none are found. This method automatically adds a sort on _id if no - sort is given. This can cause performance issues, so if the sort is - undesirable, Criteria#take! can be used instead.* - - - - .. code-block:: ruby - - Band.first! - Band.where(:members.with_size => 3).first! - Band.where(:members.with_size => 3).last! - - * - ``Criteria#first_or_create`` - - *Find the first document by the provided attributes, and if not found - create and return a newly persisted one.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_create - - * - ``Criteria#first_or_create!`` - - *Find the first document by the provided attributes, and if not found - create and return a newly persisted one using* ``create!``. - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_create! - - * - ``Criteria#first_or_initialize`` - - *Find the first document by the provided attributes, and if not found - return a new one.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").first_or_initialize - - * - ``Criteria#for_js`` - - *Find documents for a provided JavaScript expression, optionally with - the specified variables added to the evaluation scope. The scope - argument is supported in MongoDB 4.2 and lower.* - *Prefer* :manual:`$expr ` *over* ``for_js``. - - - - .. code-block:: ruby - - # All MongoDB versions - Band.for_js("this.name = 'Tool'") - - # MongoDB 4.2 and lower - Band.for_js("this.name = param", param: "Tool") - - * - ``Criteria#fourth`` - - *Get the fourth document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fourth - - * - ``Criteria#fourth!`` - - *Get the fourth document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.fourth! - - * - ``Criteria#length|size`` - - *Same as count but caches subsequent calls to the database* - - - - .. code-block:: ruby - - Band.length - Band.where(name: "FKA Twigs").size - - * - ``Criteria#pick`` - - *Get the values from one document for the provided fields. - Returns nil for unset fields and for non-existent fields.* - - *This method does not apply a sort to the documents, so it - will not necessarily return the values from the first document.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.pick(:name) - - Band.all.pick('cities.name') - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.pick('managers.n') - - - * - ``Criteria#pluck`` - - *Get all the values for the provided field. - Returns nil for unset fields and for non-existent fields.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.pluck(:name) - #=> ["Daft Punk", "Aphex Twin", "Ween"] - - Band.all.pluck('address.city') - #=> ["Paris", "Limerick", "New Hope"] - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.pluck('managers.n') - #=> [ ["Berry Gordy", "Tommy Mottola"], [], ["Quincy Jones"] ] - - # Accepts multiple field arguments, in which case - # the result will be returned as an Array of Arrays. - Band.all.pluck(:name, :likes) - #=> [ ["Daft Punk", 342], ["Aphex Twin", 98], ["Ween", 227] ] - - * - ``Criteria#read`` - - *Sets the read preference for the criteria.* - - - - .. code-block:: ruby - - Band.all.read(mode: :primary) - - * - ``Criteria#second`` - - *Get the second document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second - - * - ``Criteria#second!`` - - *Get the second document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second! - - * - ``Criteria#second_to_last`` - - *Get the second to last document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second_to_last - - * - ``Criteria#second_to_last!`` - - *Get the second to last document for the given criteria, or raise an - error if none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.second_to_last! - - * - ``Criteria#take`` - - *Get a list of n documents from the database or just one if no parameter - is provided.* - - *This method does not apply a sort to the documents, so it can return - different document(s) than #first and #last.* - - - - .. code-block:: ruby - - Band.take - Band.take(5) - - * - ``Criteria#take!`` - - *Get a document from the database or raise an error if none exist.* - - *This method does not apply a sort to the documents, so it can return - different document(s) than #first and #last.* - - - - .. code-block:: ruby - - Band.take! - - * - ``Criteria#tally`` - - *Get a mapping of values to counts for the provided field.* - - *This method accepts the dot notation, thus permitting referencing - fields in embedded associations.* - - *This method respects :ref:`field aliases `, - including those defined in embedded documents.* - - - - .. code-block:: ruby - - Band.all.tally(:name) - - Band.all.tally('cities.name') - - # Using the earlier definition of Manager, - # expands out to "managers.name" in the query: - Band.all.tally('managers.n') - - * - ``Criteria#third`` - - *Get the third document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third - - * - ``Criteria#third!`` - - *Get the third document for the given criteria, or raise an error if - none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third! - - * - ``Criteria#third_to_last`` - - *Get the third to last document for the given criteria.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third_to_last - - * - ``Criteria#third_to_last!`` - - *Get the third to last document for the given criteria, or raise an - error if none exist.* - - *This method automatically adds a sort on _id if no sort is given.* - - - - .. code-block:: ruby - - Band.third_to_last! - - -Eager Loading -============= - -Mongoid provides a facility to eager load documents -from associations to prevent the n+1 issue when -iterating over documents with association access. Eager loading is supported on -all associations with the exception of polymorphic ``belongs_to`` -associations. - -.. code-block:: ruby - - class Band - include Mongoid::Document - has_many :albums - end - - class Album - include Mongoid::Document - belongs_to :band - end - - Band.includes(:albums).each do |band| - p band.albums.first.name # Does not hit the database again. - end - - -Regular Expressions -=================== - -MongoDB, and Mongoid, allow querying documents by regular expressions. - -Given the following model definitions: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :description, type: String - end - - Band.create!(name: 'Sun Project', description: "Sun\nProject") - -... we can query using simple Ruby regular expressions in a natural way: - -.. code-block:: ruby - - Band.where(name: /project/i).first - # => # - -It is also possible to query using PCRE syntax by constructing -``BSON::Regexp::Raw`` objects explicitly: - -.. code-block:: ruby - - Band.where(description: /\AProject/).first - # => # - - Band.where(description: BSON::Regexp::Raw.new('^Project')).first - # => nil - - Band.where(description: BSON::Regexp::Raw.new('^Project', 'm')).first - # => # - - -Conditions On Fields -==================== - -When a condition uses a field defined in the model, the value being specified -in the condition is converted according to the rules of the field, if any. -For example, consider the following model definition that contains a ``Time`` -field, a ``Date`` field and an implicit ``Object`` field, and also -intentionally does not define a field called ``deregistered_at``: - -.. code-block:: ruby - - class Voter - include Mongoid::Document - - field :born_on, type: Date - field :registered_at, type: Time - field :voted_at - end - -Queries on ``born_on`` and ``registered_at`` fields using ``Date`` and ``Time`` -values, respectively, are straightforward: - -.. code-block:: ruby - - Voter.where(born_on: Date.today).selector - # => {"born_on"=>2020-12-18 00:00:00 UTC} - - Voter.where(registered_at: Time.now).selector - # => {"registered_at"=>2020-12-19 04:33:36.939788067 UTC} - -But, note the differences in behavior when providing a ``Date`` instance -in all possible scenarios: - -.. code-block:: ruby - - Voter.where(born_on: Date.today).selector - # => {"born_on"=>2020-12-18 00:00:00 UTC} - - Voter.where(registered_at: Date.today).selector - # => {"registered_at"=>2020-12-18 00:00:00 -0500} - - Voter.where(voted_at: Date.today).selector - # => {"voted_at"=>Fri, 18 Dec 2020} - - Voter.where(deregistered_at: Date.today).selector - # => {"deregistered_at"=>2020-12-18 00:00:00 UTC} - -When using the ``registered_at`` field which is of type ``Time``, the date -was interpreted to be in local time (as per the :ref:`configured time zone -`). When using the ``born_on`` field which is of type ``Date``, -the date was interpreted to be in UTC. When using the ``voted_at`` field -which was defined without a type (hence implicitly as an ``Object``), -the date was used unmodified in the constructed query. When using a -nonexistent field ``deregistered_at`` the date was interpreted to be in UTC -and converted to a time, matching the behavior of querying a ``Date`` field. - - -Scoping -======= - -Scopes provide a convenient way to reuse common criteria with more -business domain style syntax. - - -.. _named-scopes: - -Named Scopes ------------- - -Named scopes are simply criteria defined at class load that are referenced -by a provided name. Just like normal criteria, they are lazy and chainable. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :country, type: String - field :genres, type: Array - - scope :english, ->{ where(country: "England") } - scope :rock, ->{ where(:genres.in => [ "rock" ]) } - end - - Band.english.rock # Get the English rock bands. - -Named scopes can take procs and blocks for accepting parameters or -extending functionality. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :country, type: String - field :active, type: Boolean, default: true - - scope :named, ->(name){ where(name: name) } - scope :active, ->{ - where(active: true) do - def deutsch - tap do |scope| - scope.selector.store("origin" => "Deutschland") - end - end - end - } - end - - Band.named("Depeche Mode") # Find Depeche Mode. - Band.active.deutsch # Find active German bands. - -By default, Mongoid allows defining a scope that would shadow an existing -class method, as the following example shows: - -.. code-block:: ruby - - class Product - include Mongoid::Document - - def self.fresh - true - end - - scope :fresh, ->{ where(fresh: true) } - end - -To have Mongoid raise an error when a scope would overwrite an existing class -method, set the ``scope_overwrite_exception`` :ref:`configuration option -` to ``true``. - - -Default Scopes --------------- - -Default scopes can be useful when you find yourself applying the same -criteria to most queries, and wish to specify these criteria as the default. -Default scopes are procs that return criteria objects. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean - - default_scope ->{ where(active: true) } - end - - Band.each do |band| - # All bands here are active. - end - -Specifying a default scope also initializes the fields of new models to -the values given in the default scope, if the values are simple literals: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean - field :num_tours, type: Integer - - default_scope ->{ where(active: true, num_tours: {'$gt' => 1}) } - end - - # active is set, num_tours is not set - Band.new # => # - -Note that if a default value is provided both in the field definition and -in the default scope, the value in the default scope takes precedence: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean, default: true - - default_scope ->{ where(active: false) } - end - - Band.new # => # - -Because a default scope initializes fields in new models as just described, -defining a default scope with a dotted key and a simple literal value, while -possible, is not recommended: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tags, type: Hash - - default_scope ->{ where('tags.foo' => 'bar') } - end - - Band.create! - # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"} - Band.create!(tags: { 'foo' => 'bar' }) - # => Created document: {"_id"=>BSON::ObjectId('632de4ad3282a404bee1877c'), "tags.foo"=>"bar", "tags"=>{"foo"=>"bar"}} - Band.all.to_a - # => [ #"bar"}> ] - -Mongoid 8 allows dotted keys to be used in Mongoid, and when creating a document, -the scope is added as a dotted key in the attributes: - -.. code-block:: ruby - - Band.new.attribute - # => {"_id"=>BSON::ObjectId('632de97d3282a404bee1877d'), "tags.foo"=>"bar"} - -Whereas when querying, Mongoid looks for an embedded document: - -.. code-block:: ruby - - Band.create! - # => Created document: {"_id"=>BSON::ObjectId('632de48f3282a404bee1877b'), "tags.foo"=>"bar"} - Band.where - # => #"bar"} - options: {} - class: Band - embedded: false> - # This looks for something like: { tags: { "foo" => "bar" } } - Band.count - # => 0 - -A workaround is to define the default scope as a complex query: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :tags, type: Hash - - default_scope ->{ where('tags.foo' => {'$eq' => 'bar'}) } - end - - Band.create!(tags: { hello: 'world' }) - Band.create!(tags: { foo: 'bar' }) - # does not add a "tags.foo" dotted attribute - Band.count - # => 1 - -You can tell Mongoid not to apply the default scope by using -``unscoped``, which can be inline or take a block. - -.. code-block:: ruby - - Band.unscoped.where(name: "Depeche Mode") - Band.unscoped do - Band.where(name: "Depeche Mode") - end - -You can also tell Mongoid to explicitly apply the default scope -again later to always ensure it's there. - -.. code-block:: ruby - - Band.unscoped.where(name: "Depeche Mode").scoped - -If you are using a default scope on a model that is part of an association, -you must reload the association to have scoping reapplied. -This is important to note if you change a value of a document in the association -that would affect its visibility within the scoped association. - -.. code-block:: ruby - - class Label - include Mongoid::Document - embeds_many :bands - end - - class Band - include Mongoid::Document - field :active, default: true - embedded_in :label - default_scope ->{ where(active: true) } - end - - label.bands.push(band) - label.bands # [ band ] - band.update_attribute(:active, false) - label.bands # [ band ] Must reload. - label.reload.bands # [] - -.. note:: - - After the default scope is applied, it is no longer distinguished from - other query conditions. This can lead to surprising behavior when using - ``or`` and ``nor`` operators in particular: - - .. code-block:: ruby - - class Band - include Mongoid::Document - - field :name - field :active - field :touring - - default_scope ->{ where(active: true) } - end - - Band.where(name: 'Infected Mushroom') - # => - # #true, "name"=>"Infected Mushroom"} - # options: {} - # class: Band - # embedded: false> - - Band.where(name: 'Infected Mushroom').or(touring: true) - # => - # #[{"active"=>true, "name"=>"Infected Mushroom"}, {"touring"=>true}]} - # options: {} - # class: Band - # embedded: false> - - Band.or(touring: true) - # => - # #[{"active"=>true}, {"touring"=>true}]} - # options: {} - # class: Band - # embedded: false> - - In the last example, you might expect the two conditions - (``active: true`` and ``touring: true``) to be combined with an ``$and``, - but because the ``Band`` class already has the scope applied to it, - it becomes one of the disjunction branches of the ``or``. - - -Runtime Default Scope Override ------------------------------- - -You can use the ``with_scope`` method to change the default scope in a block -at runtime: - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :country, type: String - field :genres, type: Array - - scope :english, ->{ where(country: "England") } - end - - criteria = Band.with_scope(Band.english) do - Band.all - end - - criteria - # => - # #"England"} - # options: {} - # class: Band - # embedded: false> - -.. note:: - - If with_scope calls are nested, when the nested with_scope block completes - Mongoid 7 sets the current scope to nil instead of the parent scope. - Mongoid 8 will set the current scope to the correct parent scope. - To get Mongoid 8 behavior in Mongoid 7.4 and higher, set the - ``Mongoid.broken_scoping`` global option to false. - - -Class Methods -------------- - -Class methods on models that return criteria objects are also -treated like scopes, and can be chained as well. - -.. code-block:: ruby - - class Band - include Mongoid::Document - field :name, type: String - field :active, type: Boolean, default: true - - def self.active - where(active: true) - end - end - - Band.active - - -Queries + Persistence -===================== - -Mongoid supports persistence operations off of criteria -in a light capacity for when you want to expressively perform multi -document inserts, updates, and deletion. - -.. warning:: - - Criteria ordering and pagination conditions, including ``order``, ``limit``, - ``offset``, and ``batch_size``, will be ignored on the following operations. - -.. list-table:: - :header-rows: 1 - :widths: 30 60 - - * - Operation - - Example - - * - ``Criteria#create`` - - *Create a newly persisted document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").create - - * - ``Criteria#create!`` - - *Create a newly persisted document and raise an exception on validation failure.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").create! - - * - ``Criteria#build|new`` - - *Create a new (unsaved) document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").build - Band.where(name: "Photek").new - - * - ``Criteria#update`` - - *Update attributes of the first matching document.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").update(label: "Mute") - - * - ``Criteria#update_all`` - - *Update attributes of all matching documents.* - - - - .. code-block:: ruby - - Band.where(members: 2).update_all(label: "Mute") - - * - ``Criteria#add_to_set`` - - *Perform an $addToSet on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").add_to_set(label: "Mute") - - * - ``Criteria#bit`` - - *Perform a $bit on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").bit(likes: { and: 14, or: 4 }) - - * - ``Criteria#inc`` - - *Perform an $inc on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").inc(likes: 123) - - * - ``Criteria#pop`` - - *Perform a $pop on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Photek").pop(members: -1) - Band.where(name: "Photek").pop(members: 1) - - * - ``Criteria#pull`` - - *Perform a $pull on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").pull(members: "Maynard") - - * - ``Criteria#pull_all`` - - *Perform a $pullAll on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool"). - pull_all(:members, [ "Maynard", "Danny" ]) - - * - ``Criteria#push`` - - *Perform a $push on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").push(members: "Maynard") - - * - ``Criteria#push_all`` - - *Perform a $push with $each on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool"). - push_all(members: [ "Maynard", "Danny" ]) - - * - ``Criteria#rename`` - - *Perform a $rename on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").rename(name: :title) - - * - ``Criteria#set`` - - *Perform a $set on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").set(likes: 10000) - - * - ``Criteria#unset`` - - *Perform a $unset on all matching documents.* - - - - .. code-block:: ruby - - Band.where(name: "Tool").unset(:likes) - - * - ``Criteria#delete`` - - *Deletes all matching documents in the database.* - - - - .. code-block:: ruby - - Band.where(label: "Mute").delete - - * - ``Criteria#destroy`` - - *Deletes all matching documents in the database while running callbacks for all. - This loads all documents into memory and can be an expensive operation.* - - - - .. code-block:: ruby - - Band.where(label: "Mute").destroy - - -.. _query-cache: - -Query Cache -=========== - -The Ruby MongoDB driver versions 2.14 and above provide query caching functionality. When enabled, the -query cache saves the results of previously executed find and aggregation -queries and reuses them in the future instead of performing the queries again, -thus increasing application performance and reducing database load. - -Please review the `driver query cache documentation -`_ -for details about the driver's query cache behavior. - -The rest of this section assumes that driver 2.14.0 or later is being used. - - -Enabling Query Cache --------------------- - -The query cache may be enabled by using the driver's namespace or Mongoid's -namespace. - - -.. _enabling-query-cache-automatically: - -Enabling Query Cache Automatically ----------------------------------- - -The MongoDB Ruby Driver provides middleware to automatically enable the query cache for -Rack web requests and ActiveJob job runs. Please see the :ref:`Query Cache Rack Middleware -` section on the configuration page for instructions. - -Note that the Query Cache Middleware does not apply to code executed outside web requests -and/or jobs. - - -.. _enabling-query-cache-manually: - -Enabling Query Cache Manually ------------------------------ - -To enable the Query Cache manually for a code segment, use: - -.. code-block:: ruby - - Mongo::QueryCache.cache do - # ... - end - -The Query Cache can also be explicitly enabled and disabled, although we -recommend to use the block form described above: - -.. code-block:: ruby - - begin - Mongo::QueryCache.enabled = true - # ... - ensure - Mongo::QueryCache.enabled = false - end - - -.. _query-cache-first-method: - -Caching the Result of ``#first`` --------------------------------- - -Calling the ``first`` method on a model class imposes an ascending sort by -the ``_id`` field on the underlying query. This may produce unexpected behavior -with query caching. - -For example, when calling ``all`` on a model class and then ``first``, -one would expect the second query to use the cached results from the first. -However, because of the sort imposed on the second query, both methods -will query the database and separately cache their results. - -.. code-block:: ruby - - Band.all.to_a - #=> Queries the database and caches the results - - Band.first - #=> Queries the database again because of the sort - -To use the cached results, call ``all.to_a.first`` on the model class. - - -.. _load-async: - -Asynchronous Queries -==================== - -Mongoid allows running database queries asynchronously in the background. -This can be beneficial when there is a need to get documents from different -collections. - -In order to schedule an asynchronous query call the ``load_async`` method on a -``Criteria``: - -.. code-block:: ruby - - class PagesController < ApplicationController - def index - @active_bands = Band.where(active: true).load_async - @best_events = Event.best.load_async - @public_articles = Article.where(public: true).load_async - end - end - -In the above example three queries will be scheduled for asynchronous execution. -Results of the queries can be later accessed as usual: - -.. code-block:: html - -
    - <%- @active_bands.each do -%> -
  • <%= band.name %>
  • - <%- end -%> -
- -Even if a query is scheduled for asynchronous execution, it might be executed -synchronously on the caller's thread. There are three possible scenarios depending -on when the query results are being accessed: - -#. If the scheduled asynchronous task has been already executed, the results are returned. -#. If the task has been started, but not finished yet, the caller's thread blocks until the task is finished. -#. If the task has not been started yet, it is removed from the execution queue, and the query is executed synchronously on the caller's thread. - -.. note:: - - Even though ``load_async`` method returns a ``Criteria`` object, you should not - do any operations on this object except accessing query results. The query is - scheduled for execution immediately after calling ``load_async``, therefore - later changes to the ``Criteria`` object may not be applied. - - -Configuring asynchronous query execution ----------------------------------------- - -Asynchronous queries are disabled by default. When asynchronous queries are -disabled, ``load_async`` will execute the query immediately on the current thread, -blocking as necessary. Therefore, calling ``load_async`` on criteria in this case -is roughly the equivalent of calling ``to_a`` to force query execution. - -In order to enable asynchronous query execution, the following config options -must be set: - -.. code-block:: yaml - - development: - ... - options: - # Execute asynchronous queries using a global thread pool. - async_query_executor: :global_thread_pool - # Number of threads in the pool. The default is 4. - # global_executor_concurrency: 4 diff --git a/source/reference/sessions.txt b/source/reference/sessions.txt deleted file mode 100644 index 4faf35c0..00000000 --- a/source/reference/sessions.txt +++ /dev/null @@ -1,56 +0,0 @@ -.. _sessions: - -******** -Sessions -******** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -You can use sessions with Mongoid in a similar way that you would execute a transaction in ActiveRecord. -Namely, you can call a method, ``#with_session`` on a model class or on an instance of a model and execute -some operations in a block. All operations in the block will be executed in the context of single session. -Please see the MongoDB Ruby driver documentation for what session options are available. - -Please note the following limitations of sessions: - -- Sessions cannot be shared across threads; sessions are not thread-safe. This is consistent with the Ruby driver's support for sessions. - -- Sessions cannot be nested. You cannot called ``#with_session`` on a model class or a model instance within the block passed to the ``#with_session`` method on another model class or model instance. - -- All model classes and instances used within the session block must use the same driver client. For example, if you have specified different ``storage_options`` for another model used in the block than that of the model class or instance on which ``#with_session`` is called, you will get an error. - -Using a Session via Model#with_session -====================================== - -Call ``#with_session`` on a model class and pass it session options to execute a block in the context -of a session. - -.. code-block:: ruby - - Person.with_session(causal_consistency: true) do - Person.create! - person = Person.first - person.name = "Emily" - person.save - end - - -Using a Session via model#with_session -====================================== - -Call ``#with_session`` on a model instance and pass it session options to execute a block in the context -of a session. - -.. code-block:: ruby - - person.with_session(causal_consistency: true) do - person.username = 'Emily' - person.save - person.posts << Post.create! - end diff --git a/source/reference/sharding.txt b/source/reference/sharding.txt deleted file mode 100644 index 6fd41522..00000000 --- a/source/reference/sharding.txt +++ /dev/null @@ -1,146 +0,0 @@ -.. _sharding: - -********************** -Sharding Configuration -********************** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -Mongoid can assist with setting up collection sharding in sharded environments. - - -.. _shard-keys: - -Declaring Shard Keys -==================== - -Shard keys can be declared on models using the ``shard_key`` macro: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - field :ssn - - shard_key ssn: 1 - - # The collection must also have an index that starts with the shard key. - index ssn: 1 - end - -Note that in order to shard a collection, the collection must have an index -that starts with the shard key. Mongoid provides :ref:`index management -` functionality, which the examples here take -advantage of. - -Mongoid supports two syntaxes for declaring shard keys. The standard syntax -follows the format of MongoDB `shardCollection shell helper -`_ -and allows specifying ranged and hashed shard keys, compound shard keys and -collection sharding options: - -.. code-block:: ruby - - shard_key ssn: 1 - - shard_key ssn: 1, country: 1 - - shard_key ssn: :hashed - - shard_key {ssn: :hashed}, unique: true - -The alternative is the shorthand syntax, in which only the keys are given. -This syntax only supports ranged shard keys and does not allow options to -be specified: - -.. code-block:: ruby - - shard_key :ssn - - shard_key :ssn, :country - -``shard_key`` macro can take the name of a ``belongs_to`` association in -place of a field name, in which case Mongoid will use the foreign key -configured in the association as the field name: - -.. code-block:: ruby - - class Person - include Mongoid::Document - - belongs_to :country - - # Shards by country_id. - shard_key country: 1 - - # The collection must also have an index that starts with the shard key. - index country: 1 - end - -The shard key may also reference a field in an embedded document, by using -the "." character to delimit the field names: - -.. code-block:: ruby - - shard_key "location.x" => 1, "location.y" => 1 - - shard_key "location.x", "location.y" - -.. note:: - - Because the "." character is used to delimit fields in embedded documents, - Mongoid does not currently support shard key fields that themselves - literally contain the "." character. - -.. note:: - - If a model declares a shard key, Mongoid expects the respective collection - to be sharded with the specified shard key. When reloading models, Mongoid - will provide the shard key in addition to the ``id`` field value to the - ``find`` command to improve query performance, especially on `geographically - distributed sharded clusters `_. - If the collection is not sharded with the specified shard key, queries - may produce incorrect results. - - -.. _sharding-management: - -Sharding Management Rake Tasks -============================== - -To shard collections in the database according to the shard keys defined in -the models, run the ``db:mongoid:shard_collections`` Rake task. -If necessary, run the ``db:mongoid:create_indexes`` Rake task prior to -sharding collections: - -.. code-block:: bash - - rake db:mongoid:create_indexes - rake db:mongoid:shard_collections - -.. note:: - - Like with index management rake tasks, sharding management rake tasks - generally do not stop and fail when they encounter the problem with a - particular model class. Instead they log the problem (to the configured - Mongoid logger) at an appropriate level and continue with the next model. - When Mongoid is used in a Rails application, this means the results of - the rake task execution will generally be found in the per-environment - log file like ``log/development.log``. - -.. note:: - - When performing schema-related operations in a sharded cluster, such as - sharding collections as described in this document, or creating or dropping - collections or databases, cluster nodes may end up with out of date local - configuration-related cache data. Execute the `flushRouterConfig - `_ - command on each ``mongos`` node to clear these caches. diff --git a/source/reference/text-search.txt b/source/reference/text-search.txt deleted file mode 100644 index 9624affb..00000000 --- a/source/reference/text-search.txt +++ /dev/null @@ -1,85 +0,0 @@ -.. _mongoid-text-search: -.. _text-search: - -*********** -Text Search -*********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - - -MongoDB provides :manual:`text indexes ` -to support text search queries on string content. Text indexes -can include any field whose value is a string or an array of -string elements. - -.. note:: - - MongoDB Atlas also provides - `Atlas Search `_ - which is a more powerful and flexible text search solution. - The rest of this section discusses text indexes and not Atlas Search. - -To perform text search with Mongoid, follow these steps: - -1. Define a text index on a model. -2. Create the text index on the server. -3. Build a text search query. - - -Defining Text Search Index --------------------------- - -Index definition through Mongoid is described in detail on the :ref:`indexes -` page. Text search indexes are described in detail -under `text indexes `_ -in the MongoDB manual. Below is an example definition of a Band model with -a text index utilizing the description field: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :description, type: String - - index description: 'text' - end - -Note that the index type (``text``) must be given as a string, not as a symbol. - - -Creating Text Index -------------------- - -To create the index, invoke the ``db:mongoid:create_indexes`` Rake task: - -.. code-block:: ruby - - bundle exec rake db:mongoid:create_indexes - - -Querying Using Text Index -------------------------- - -To find bands whose description contains "ounces" or its variations, use the -`$text operator `_: - -.. code-block:: ruby - - Band.where('$text' => {'$search' => 'ounces'}).to_a - # => [#] - -Note that the description contains the word "ounce" even though the search -query was "ounces". - -Note also that when performing text search, the name of the field is not -explicitly specified - ``$text`` operator searches all fields indexed with -the text index. diff --git a/source/reference/transactions.txt b/source/reference/transactions.txt deleted file mode 100644 index ecf2465e..00000000 --- a/source/reference/transactions.txt +++ /dev/null @@ -1,232 +0,0 @@ -.. _transactions: - -************ -Transactions -************ - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Version 4.0 of the MongoDB server introduces -`multi-document transactions `_. -(Updates to multiple fields within a single document are atomic in all -versions of MongoDB). Transactions require a non-standalone MongoDB topology -and Ruby driver version 2.6 or higher. A higher level transaction API requires -Mongoid version 9.0 or higher, while a lower level API requires Mongoid -version 6.4 or higher. - -Using Transactions -================== - -Higher Level API ----------------- - -A transaction can be started by calling the ``transaction`` method on an instance -of a Mongoid document class, on a Mongoid document class, on or ``Mongoid`` module: - -.. code-block:: ruby - - Band.transaction do - Band.create(title: 'Led Zeppelin') - end - - band = Band.create(title: 'Deep Purple') - band.transaction do - band.active = false - band.save! - end - - Mongoid.transaction do - band.destroy - end - -When the ``transaction`` method is called, Mongoid does the following: - -* creates a session on a client that is used by the receiver of the - ``transaction`` method call; -* starts a transaction on the session; -* executes the given block; -* commits the transaction if no exception raised in the block; - - * calls ``after_commit`` callbacks for all objects modified inside the transaction -* aborts the transaction if an exception is raised in the block; - - * calls ``after_rollback`` callbacks for all objects modified inside the transaction -* closes the session - -.. note:: - - Since a transaction is tied to a particular client, _only_ operations on - the same client will be in scope of the transaction. Therefore it - is recommended that only objects that use the same client are used inside the - ``transaction`` method block. - - .. code-block:: ruby - - class Author - include Mongoid::Document - store_in client: :encrypted_client - end - - class User - include Mongoid::Document - store_in client: :encrypted_client - end - - class Article - include Mongoid::Document - # This class uses the :default client - end - - # Transaction is started on the :encrypted_client - Author.transaction do - # This operation uses the same client, so it is in the transaction - Author.create! - # This operation also uses the same client, so it is in the transaction - User.create! - # This operation uses a different client, so it is NOT in the transaction - Article.create! - end - -.. note:: - When ``transaction`` method is called on ``Mongoid`` module, the transaction - is created using the ``:default`` client. - -Aborting Transaction -~~~~~~~~~~~~~~~~~~~~ - -Any exception raised inside the ``transaction`` method block aborts the -transaction. Normally the raised exception passed on, except for the -``Mongoid::Errors::Rollback``. This error should be raised if you want to -explicitly abort the transaction without passing on an exception. - -Callbacks -~~~~~~~~~ - -Transaction API introduces two new callbacks - ``after_commit`` and ``after_rollback``. - -``after_commit`` callback is triggered for an object that was created, saved, -or destroyed: - -* after transaction is committed if the object was modified inside the transaction; -* after the object was persisted if the object was modified outside a transaction. - -.. note:: - In any case ``after_commit`` callback is triggered only after all other callbacks - were executed successfully. Therefore, if the object is modified without a - transaction, it is possible that the object was persisted, but ``after_commit`` - callback was not triggered (for example, an exception raised in ``after_save`` - callback). - -``after_rollback`` callback is triggered for an object that was created, saved, -or destroyed inside a transaction if the transaction was aborted. ``after_rollback`` -is never triggered without a transaction. - - -Lower Level API ---------------- - -In order to start a transaction, the application must have a :ref:`session `. - -A transaction can be started by calling the ``start_transaction`` method on a session, which can be -obtained by calling the ``with_session`` method on either a model class or instance: - -.. code-block:: ruby - - class Person - include Mongoid::Document - end - - Person.with_session do |session| - session.start_transaction - end - - person = Person.new - person.with_session do |session| - session.start_transaction - end - -It is also possible to specify read concern, write concern and read preference -when starting a transaction: - -.. code-block:: ruby - - Person.with_session do |session| - session.start_transaction( - read_concern: {level: :majority}, - write_concern: {w: 3}, - read: {mode: :primary}) - end - -A transaction may be committed or aborted. The corresponding methods to do so are -``commit_transaction`` and ``abort_transaction``, again on the session instance: - -.. code-block:: ruby - - Person.with_session do |session| - session.commit_transaction - end - - Person.with_session do |session| - session.abort_transaction - end - -If a session ends with an open transaction, -`the transaction is aborted `_. - -The transaction commit `can be retried `_ -if it fails. Here is the Ruby code to do so: - -.. code-block:: ruby - - begin - session.commit_transaction - rescue Mongo::Error => e - if e.label?(Mongo::Error::UNKNOWN_TRANSACTION_COMMIT_RESULT_LABEL) - retry - else - raise - end - end - -Note that in order to perform operations within the transaction, operations must use the same client -that the session was initiated on. By default, all operations will be done on the default client: - -.. code-block:: ruby - - class Person - include Mongoid::Document - end - - class Post - include Mongoid::Document - end - - Person.with_session do |s| - s.start_transaction - Person.create! - Person.create! - Post.create! - s.commit_transaction - end - -To explicitly use a different client, use the ``with`` method: - -.. code-block:: ruby - - Post.with(client: :other) do - Person.with(client: :other) do - Person.with_session do |s| - s.start_transaction - Person.create! - Person.create! - Post.create! - s.commit_transaction - end - end - end diff --git a/source/reference/validation.txt b/source/reference/validation.txt deleted file mode 100644 index 4ec578ad..00000000 --- a/source/reference/validation.txt +++ /dev/null @@ -1,67 +0,0 @@ -.. _mongoid-validation: -.. _validation: - -********** -Validation -********** - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid includes ``ActiveModel::Validations`` to supply the basic -validation plus an additional associated and uniqueness validator. - -See the `Active Record Validations -`_ -Rails guide and `ActiveModel::Validations -`_ -documentation for more information. - -Mongoid behaves slightly differently to Active Record when using ``#valid?`` -on already persisted data. Active Record's ``#valid?`` will run all -validations whereas Mongoid's ``#valid?`` will only run validations on -documents that are in memory as an optimization. - - -``validates_uniqueness_of`` and ``:conditions`` Option -======================================================= - -The ``:conditions`` option to ``validates_uniqueness_of`` can be used to -provide additional conditions to add to the database query looking for -identical documents. This option does not influence when the validation -is executed because it is not considered when Mongoid retrieves the present -value of the respective field from the model. Consider the following example: - -.. code-block:: ruby - - class Band - include Mongoid::Document - - field :name, type: String - field :year, type: Integer - - validates_uniqueness_of :name, conditions: -> { where(:year.gte => 2000) } - end - - # OK - Band.create!(name: "Sun Project", year: 2000) - - # Fails validation because there is a band with the "Sun Project" name - # and year 2000 in the database, even though the model being created now - # does not have a year. - Band.create!(name: "Sun Project") - - -Read preference with ``validates_uniqueness_of`` -================================================ - -In order to validate the uniqueness of an attribute, Mongoid must check that -the value for that attribute does not already exist in the database. If Mongoid -queries a secondary member of the replica set, there is a possibility that it -is reading stale data. Because of this, the queries used to check a -``validates_uniqueness_of`` validation always use read preference ``primary``. diff --git a/source/schema-configuration.txt b/source/schema-configuration.txt deleted file mode 100644 index 091c4bcf..00000000 --- a/source/schema-configuration.txt +++ /dev/null @@ -1,31 +0,0 @@ -.. _schema-configuration: - -******************** -Schema Configuration -******************** - -.. default-domain:: mongodb - -.. toctree:: - :titlesonly: - - reference/fields - reference/inheritance - reference/associations - reference/validation - reference/collection-configuration - reference/indexes - reference/sharding - -Overview --------- - -See the following sections to learn how to configure a schema with Mongoid: - -- :ref:`Field Definition ` -- :ref:`Inheritance ` -- :ref:`Associations ` -- :ref:`Validation ` -- :ref:`Collection Configuration ` -- :ref:`Index Management ` -- :ref:`Sharding Management ` \ No newline at end of file diff --git a/source/tutorials.txt b/source/tutorials.txt deleted file mode 100644 index 2a564253..00000000 --- a/source/tutorials.txt +++ /dev/null @@ -1,17 +0,0 @@ -.. _tutorials: - -********* -Tutorials -********* - -.. toctree:: - :titlesonly: - - tutorials/common-errors - -Overview --------- - -See the following sections to learn more about working with Mongoid: - -- :ref:`Common Errors ` \ No newline at end of file diff --git a/source/tutorials/common-errors.txt b/source/tutorials/common-errors.txt deleted file mode 100644 index 226e3b0e..00000000 --- a/source/tutorials/common-errors.txt +++ /dev/null @@ -1,51 +0,0 @@ -.. _common-errors: - -************* -Common Errors -************* - -.. default-domain:: mongodb - -.. contents:: On this page - :local: - :backlinks: none - :depth: 2 - :class: singlecol - -Mongoid/Moped Authentication Error: failed with error 13 -======================================================== - -If you are encountering the following error: - -.. code-block:: ruby - - Moped::Errors::OperationFailure: The operation: #1, :w=>1} - @fields=nil> - failed with error 13: "not authorized for insert on mongose_development.people" - - See https://github.com/mongodb/mongo/blob/master/docs/errors.md - for details about this error. - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/operation/read.rb:50:in `block in execute' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `[]' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:594:in `block (2 levels) in flush' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `map' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:593:in `block in flush' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/bundler/gems/moped-10abbf3eac37/lib/moped/node.rb:617:in `block in logging' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications.rb:164:in `block in instrument' - from /.rbenv/versions/2.1.4/lib/ruby/gems/2.1.0/gems/activesupport-4.2.0/lib/active_support/notifications/instrumenter.rb:20:in `instrumen - -This error is caused by Moped, a Ruby driver that is no longer in use by -Mongoid. Upgrading to Mongoid 5+ should fix this issue. - -You can find more information about this issue here: -`MONGOID-4067 `_. - diff --git a/source/whats-new.txt b/source/whats-new.txt index e6e52baa..fda72e88 100644 --- a/source/whats-new.txt +++ b/source/whats-new.txt @@ -116,7 +116,7 @@ The deprecated class ``{+odm+}::Errors::InvalidStorageParent`` has been removed. ``around_*`` Callbacks for Embedded Documents are Ignored ---------------------------------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {+odm+} v8.x and older allow user to define ``around_*`` callbacks for embedded documents. Starting in v9.0, these callbacks are ignored and will not be executed. @@ -142,7 +142,7 @@ Removal of Deprecated Options **Breaking change:** The following config options are removed in {+odm+} v9.0. Please ensure you have removed all references to these from your app. -If you were using ``config.load_defaults 8.1`` prior to upgrading, you will not +If you were using ``config.load_defaults 8.1`` before upgrading, you will not experience any behavior change. Refer to earlier release notes for the meaning of each option. @@ -205,7 +205,7 @@ the ``touch`` method. band.changes # => {} Sandbox Mode for Rails Console ------------------------------- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {+odm+} now supports Rails console sandbox mode. If the Rails console started with ``--sandbox`` flag, {+odm+} starts a transaction on the ``:default`` client @@ -545,7 +545,7 @@ Consider the following code: record = Model.with(collection: 'other_collection') { Model.first } record.update(field: 'value') -Prior to {+odm+} v9.0, this could would silently fail to execute the update, +Before {+odm+} v9.0, the preceding code silently fails to execute the update, because the storage options (here, the specification of an alternate collection for the model) would not be remembered by the record. Thus, the record would be loaded from ``other_collection``, but when updated, would attempt