diff --git a/snooty.toml b/snooty.toml index 5ae4acc9..1b299d10 100644 --- a/snooty.toml +++ b/snooty.toml @@ -23,3 +23,4 @@ quickstart-sinatra-app-name = "my-sinatra-app" quickstart-rails-app-name = "my-rails-app" feedback-widget-title = "Feedback" server-manual = "Server manual" +api = "https://www.mongodb.com/docs/mongoid/master/api" \ No newline at end of file diff --git a/source/includes/interact-data/modify-results.rb b/source/includes/interact-data/modify-results.rb new file mode 100644 index 00000000..f2da3cfe --- /dev/null +++ b/source/includes/interact-data/modify-results.rb @@ -0,0 +1,40 @@ +# start-only +Band.where(members: 4).only(:name) +# end-only + +# start-only-embed +bands = Band.only(:name, 'tours.year') +# end-only-embed + +# start-only-embed-association +# Returns null +Band.where(name: 'Astral Projection').only(:name).first.managers + +# Returns the first Manager object +Band.where(name: 'Astral Projection').only(:name, :manager_ids).first.managers +# end-only-embed-association + +# start-without +Band.where(members: 4).without(:year) +# end-without + +# start-limit +Band.limit(5) +# end-limit + +# start-skip-limit +Band.skip(2).limit(5) +# Skips the first two results and returns +# the following five results +# end-skip-limit + +# start-skip +Band.skip(3) + +# Equivalent +Band.offset(3) +# end-skip + +# start-batch +Band.batch_size(500) +# end-batch diff --git a/source/interact-data.txt b/source/interact-data.txt index 83247dcb..442d855e 100644 --- a/source/interact-data.txt +++ b/source/interact-data.txt @@ -15,9 +15,13 @@ Interact with Data :caption: Interact with Data /interact-data/specify-query + /interact-data/modify-results In this section, you can learn how to use {+odm+} to interact with your MongoDB data. - :ref:`mongoid-data-specify-query`: Learn how to construct queries to match specific documents in a MongoDB collection. + +- :ref:`mongoid-data-modify-results`: Learn how to modify the way that + {+odm+} returns results from queries. diff --git a/source/interact-data/modify-results.txt b/source/interact-data/modify-results.txt new file mode 100644 index 00000000..cc87951c --- /dev/null +++ b/source/interact-data/modify-results.txt @@ -0,0 +1,304 @@ +.. _mongoid-data-modify-results: + +==================== +Modify Query Results +==================== + +.. facet:: + :name: genre + :values: reference + +.. meta:: + :keywords: ruby framework, odm, crud, print results, code example + +.. contents:: On this page + :local: + :backlinks: none + :depth: 2 + :class: singlecol + +Overview +-------- + +In this guide, you can learn how to customize the way that {+odm+} +returns results from queries. MongoDB allows you to perform the +following actions to modify the way that results appear: + +- :ref:`mongoid-data-projection` + +- :ref:`mongoid-data-sort` + +- :ref:`mongoid-data-skip-limit` + +Sample Data +~~~~~~~~~~~ + +The examples in this guide use the ``Band`` model, which represents a +band or musical group. The definition of the ``Band`` model might be +different for each section to demonstrate different query +functionalities. Some sections also use the ``Manager`` model, +which represents a person who manages a given band, or the ``Tour`` +model, which represents live performances by a given band. + +.. _mongoid-data-projection: + +Return Specified Fields +----------------------- + +In MongoDB, *projection* is the process of specifying fields to include +or exclude from results. {+odm+} provides the following operators +to project fields: + +- ``only()``: Specifies fields to include +- ``without()``: Specifies fields to exclude + +Include Fields +~~~~~~~~~~~~~~ + +The ``only()`` method retrieves only the specified fields from the +database. + +The following code returns only the ``name`` field from documents in +which the value of the ``members`` field is ``4``: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only + :end-before: end-only + :language: ruby + :dedent: + +.. note:: _id Field + + In MongoDB, the ``_id`` field is included in results even if you do + not explicitly include it. + +If you attempt to reference attributes that have not been loaded, +{+odm+} raises a ``Mongoid::Errors::AttributeNotLoaded`` error. + +You can also use the ``only()`` method to include fields from embedded +documents. + +Consider that the ``Band`` model embeds multiple ``Tour`` objects. You can +project fields from the ``Tour`` model such as ``year``, as shown in the +following code: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only-embed + :end-before: end-only-embed + :language: ruby + :dedent: + +Then, you can access the embedded fields from the returned documents: + +.. code-block:: ruby + + # Returns the first Tour object from + # the first Band in the results + bands.first.tours.first + +You can pass fields of referenced associations to the ``only()`` method, +but the projection is ignored when loading the embedded objects. {+odm+} +loads all fields of the referenced associations. For example, when you +access the embedded ``Tour`` object as shown in the preceding code, +{+odm+} returns the complete object, not just the ``year`` field. + +.. note:: + + If you are connected to a deployment running MongoDB 4.4 or later, + you cannot specify an association and its fields in a projection in + the same query. + +If a document contains ``has_one`` or ``has_and_belongs_to_many`` +associations, and you want {+odm+} to load those associations when +you call the ``only()`` method, you must include the fields with foreign +keys in the list of attributes. + +In the following example, the ``Band`` and ``Manager`` models have a +``has_and_belongs_to_many`` association: + +.. 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 + +The following code demonstrates how {+odm+} can load the associated +``Manager`` objects if you include the ``manager_ids`` field: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-only-embed-association + :end-before: end-only-embed-association + :language: ruby + :dedent: + +Exclude Fields +~~~~~~~~~~~~~~ + +You can explicitly exclude fields from results by using the +``without()`` method. + +The following code excludes the ``year`` field from returned ``Band`` +objects: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-without + :end-before: end-without + :language: ruby + :dedent: + +.. important:: _id Field + + {+odm+} requires the ``_id`` field for various operations, so you + *cannot* exclude the ``_id`` field or the ``id`` alias from results. + If you pass ``_id`` or ``id`` to the ``without()`` method, {+odm+} + ignores it. + +.. _mongoid-data-sort: + +Sort Results +------------ + +You can specify the order in which {+odm+} returns documents by using the +``order()`` and ``order_by()`` methods. + +These methods accept a hash that indicates which fields to order the +documents by, and whether to use an ascending or descending order for +each field. + +You can specify the sort direction by using integers, symbols, or +strings. We recommend using the same sorting syntax throughout your +application for consistency. The following list provides each syntax and +shows how to sort on the ``name`` and ``year`` fields: + +- Integers ``1`` (ascending) and ``-1`` (descending) + + - Example: ``Band.order(name: 1, year: -1)`` + +- Symbols ``:asc`` and ``:desc`` + + - Example: ``Band.order(name: :asc, year: :desc)`` + +- Strings ``"asc"`` and ``"desc"`` + + - Example: ``Band.order_by(name: "asc", year: "desc")`` + +The ``order()`` method also accepts the following sort specifications: + +- Array of two-element arrays: + + - Strings + + - Example: ``Band.order([['name', 'asc'], ['year', 'desc']])`` + + - Symbols + + - Example: ``Band.order([[:name, :asc], [:year, :desc]])`` + +- ``asc`` and ``desc`` methods on symbols + + - Example: ``Band.order(:name.asc, :year.desc)`` + +- SQL syntax + + - Example: ``Band.order('name asc', 'year desc')`` + +.. tip:: + + Instead of using ``order()`` or ``order_by()``, you can also use the + ``asc()`` and ``desc()`` methods to specify sort orders: + + .. code-block:: ruby + + Band.asc('name').desc('year') + +When you chain sort specifications, the first call defines the first +sorting order and the newest call defines the last sorting order after +the previous sorts have been applied. + +.. TODO update link in the following note for scope + +.. note:: Sorting in Scopes + + If you define a scope on your model that includes a sort specification, + the scope sort takes precedence over the sort specified in a query, + because the default scope is evaluated first. + +.. _mongoid-data-skip-limit: + +Paginate Results +---------------- + +{+odm+} provides the ``limit()``, ``skip()``, and ``batch_size()`` +pagination methods that you can use on ``Criteria`` objects. The +following sections describe how to use these operators. + +Limit Number of Results +~~~~~~~~~~~~~~~~~~~~~~~ + +You can use the ``limit()`` method to limit the number of results that +{+odm+} returns. + +The following code retrieves a maximum of ``5`` documents: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-limit + :end-before: end-limit + :language: ruby + :dedent: + +Skip Results +~~~~~~~~~~~~ + +You can skip a specified number of results by using the ``skip()`` +method, or its alias ``offset()``. + +If you chain a ``limit()`` call to ``skip()``, the limit is applied +after documents are skipped, as demonstrated in the following example: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-skip-limit + :end-before: end-skip-limit + :language: ruby + :dedent: + +.. tip:: + + When performing pagination, use ``skip()`` on :ref:`sorted results ` + to ensure consistent results. + +The following code skips the first ``3`` documents when returning results: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-skip + :end-before: end-skip + :language: ruby + :dedent: + +Generate Batches of Results +~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When executing large queries and when iterating over query results by using +an enumerator method such as ``Criteria#each()``, {+odm+} automatically +uses the MongoDB :manual:`getMore ` command +to load results in batches. The default batch size is ``1000``, but +you can set a different value by using the ``batch_size()`` method. + +The following code sets the batch size to ``500``: + +.. literalinclude:: /includes/interact-data/modify-results.rb + :start-after: start-batch + :end-before: end-batch + :language: ruby + :dedent: + +Additional Information +---------------------- + +.. TODO: add links to the bottom of this page