Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions snooty.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,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"
34 changes: 34 additions & 0 deletions source/includes/interact-data/modify-results.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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
Band.skip(3)

# Equivalent
Band.offset(3)
# end-skip

# start-batch
Band.batch_size(500)
# end-batch
11 changes: 7 additions & 4 deletions source/interact-data.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ Interact with Data
.. meta::
:keywords: ruby framework, odm, crud, query

.. TODO
.. toctree::
:caption: Interact with Data
/interact-data/specify-query
.. toctree::
:caption: Interact with Data

/interact-data/modify-results

In this section, you can learn how to use {+odm+} to interact with your
MongoDB data.

- :ref:`mongoid-data-modify-results`: Learn how to modify the way that
{+odm+} returns results from queries.

.. - :ref:`mongoid-data-specify-query`: Learn how to construct
.. queries to match specific documents in a MongoDB collection.
281 changes: 281 additions & 0 deletions source/interact-data/modify-results.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
.. _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 to you 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 might also use the ``Manager`` model,
which represents a person who manages a given band, or a ``Tour`` model, which
represents live performances by a certain band or musical group.

.. _mongoid-data-projection:

Return Specified Fields
-----------------------

In MongoDB, the process of specifying fields to include or exclude from
results is called *projection*. {+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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: might be my lack of ruby experience, but i'm still not sure i understand this paragraph. will ruby people get it?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It means that even if you project only one field of an embedded document, the whole thing will load when you access it from the result. I can reword


.. 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 has ``has_one`` or ``has_and_belongs_to_many``
associations, you must include the fields with foreign keys in the list
of attributes loaded when using ``only()`` for those associations to be
loaded.

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 sort the order that {+odm+} returns documents by using the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
You can sort the order that {+odm+} returns documents by using the
You can sort 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 in the following ways:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: Love the code examples for each sort variation, but seems useful to give a little more context. E.g.: "The following examples show different ways to sort by the name and year fields."

It also took me a minute to understand these examples. Seems like you can choose the sort order by passing a number or a symbol or a string? I wonder if making that point explicitly would be helpful, and possibly organizing the info in a table or something. I assume users should also be picking just one of these methods and sticking with it, to keep their code consistent. Could be a good tip to add.


- Integers ``1`` (ascending) and ``-1`` (descending)
- Example: ``Band.order(name: 1, year: -1)``

- Symbols ``:asc`` and ``:desc``
- Example: ``Band.order(name: :asc)``

- 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]])``

- ``asc`` and ``desc`` methods on symbols
- Example: ``Band.order(:name.asc, :year.desc)``

- SQL syntax
- Example: ``Band.order('name desc')``

You can also use the ``asc()`` and ``desc()`` methods instead of using
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: I'd consider making order, order_by, and asc/desc their own subsections

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ill move the asc desc into a note

``order()``:

.. code-block:: ruby

Band.asc('name').desc('year')

If you chain sort specifications, the first call defines the most
significant criteria and the newest call defines the least significant
one.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: does most/least significant mean the order in which ruby will try to sort the fields by (sort the most significant, then second most, etc.)? I just haven't heard these terms before.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ill reword


.. 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, as the
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I: as

Suggested change
the scope sort takes precedence over the sort specified in a query, as the
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 operators that you can use on ``Criteria`` objects. The
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Q: operators vs methods?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They are interchangeable

following sections describe how to use these operators.

Limit Number of Results
~~~~~~~~~~~~~~~~~~~~~~~

You can limit the number of results that {+odm+} returns by using the
``limit()`` method.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: slight ambiguity between you using the limit() method and mongoid using it

Suggested change
You can limit the number of results that {+odm+} returns by using the
``limit()`` method.
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, it
is applied after documents are skipped.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

S: might be useful to have an example or admonition for this, actually! probably a common question.

Suggested change
method, or its alias ``offset()``. If you chain a ``limit()`` call, it
is applied after documents are skipped.
method, or its alias ``offset()``. If you chain a ``limit()`` call
to the ``skip()`` method, the limit
is applied after documents are skipped.


.. tip::

When performing pagination, use ``skip()`` on :ref:`sorted results <mongoid-data-sort>` 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 </reference/command/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
Loading