Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
13 changes: 6 additions & 7 deletions snooty.toml
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
name = "mongoid"
title = "Mongoid"

intersphinx = [ "https://www.mongodb.com/docs/manual/objects.inv",
"https://www.mongodb.com/docs/atlas/objects.inv"
]
intersphinx = [
"https://www.mongodb.com/docs/manual/objects.inv",
"https://www.mongodb.com/docs/atlas/objects.inv",
]

toc_landing_pages = [
"/quick-start-rails",
"/quick-start-sinatra"
]
toc_landing_pages = ["/quick-start-rails", "/quick-start-sinatra"]

[constants]
rails-6-version = 6.0
Expand All @@ -22,3 +20,4 @@ quickstart-sinatra-app-name = "my-sinatra-app"
quickstart-rails-app-name = "my-rails-app"
feedback-widget-title = "Feedback"
server-manual = "Server manual"
api-root = "https://www.mongodb.com/docs/mongoid/master/api/Mongoid"
233 changes: 233 additions & 0 deletions source/aggregation.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
.. _mongoid-aggregation:

====================================
Transform Your Data with Aggregation
====================================

.. facet::
:name: genre
:values: reference

.. meta::
:keywords: code example, transform, pipeline

.. contents:: On this page
:local:
:backlinks: none
:depth: 2
:class: singlecol

Overview
--------

In this guide, you can learn how to use {+odm+} to perform **aggregation
operations**.

Aggregation operations process data in your MongoDB collections and return
computed results. The MongoDB Aggregation framework, which is part of the Query
API, is modeled on the concept of data processing pipelines. Documents enter a
pipeline that contains one or more stages, and this pipeline transforms the
documents into an aggregated result.

Aggregation operations function similarly to car factories with assembly
lines. The assembly lines have stations with specialized tools to
perform specific tasks. For example, when building a car, the assembly
line begins with the frame. Then, as the car frame moves through the
assembly line, each station assembles a separate part. The result is a
transformed final product, the finished car.

The assembly line represents the *aggregation pipeline*, the individual
stations represent the *aggregation stages*, the specialized tools
represent the *expression operators*, and the finished product
represents the *aggregated result*.

Compare Aggregation and Find Operations
---------------------------------------

The following table lists the different tasks you can perform with find
operations, compared to what you can achieve with aggregation
operations. The aggregation framework provides expanded functionality
that allows you to transform and manipulate your data.

.. list-table::
:header-rows: 1
:widths: 50 50

* - Find Operations
- Aggregation Operations

* - | Select *certain* documents to return
| Select *which* fields to return
| Sort the results
| Limit the results
| Count the results
- | Select *certain* documents to return
| Select *which* fields to return
| Sort the results
| Limit the results
| Count the results
| Rename fields
| Compute new fields
| Summarize data
| Connect and merge data sets

{+odm+} Builders
----------------

You can construct an aggregation pipeline by using {+odm+}'s high-level
domain-specific language (DSL). The DSL supports the following aggregation
pipeline operators:

.. list-table::
:header-rows: 1
:widths: 50 50

* - Operator
- Method Name

* - :manual:`$group <reference/operator/aggregation/group/>`
- ``group()``

* - :manual:`$project <reference/operator/aggregation/project/>`
- ``project()``

* - :manual:`$unwind <reference/operator/aggregation/unwind/>`
- ``unwind()``

To create an aggregation pipeline by using one of the preceding operators, call
the corresponding method on an instance of ``Criteria``. Calling the method adds
the aggregation operation to the ``pipeline`` atrritbure of the ``Criteria``
instance. To run the aggregation pipeline, pass the ``pipeline`` attribute value
to the ``Collection#aggregate()`` method.

Example
~~~~~~~

Consider a database that contains a collection with documents that are modeled by
the following classes:

.. code-block:: ruby

class Tour
include Mongoid::Document

embeds_many :participants

field :name, type: String
field :states, type: Array
end

class Participant
include Mongoid::Document

embedded_in :tour

field :name, type: String
end

In this example, the ``Tour`` model represents the name of a tour and the states
it travels through, and the ``Participant`` model represents the name of a
person participating in the tour.

The following example creates an aggregation pipeline that outputs the states a
participant has visited by using the following
aggregation operations:

- ``match()``, which find documents in which the ``participants.name`` field
value is ``"Serenity"``
- ``unwind()``, which deconstructs the ``states`` array field and outputs a
document for each element in the array
- ``group()``, which groups the documents by the value of their ``states`` field
- ``project()``, which prompts the pipeline to return only the ``_id`` and
``states`` fields

.. io-code-block::

.. input:: /includes/aggregation/builder-dsl.rb
:language: ruby

.. output::

[{"states":["OR","WA","CA"]}]

Aggregation with the Ruby Driver
--------------------------------

You can use the {+ruby-driver+} to run aggregation operations that do not have
corresponding builder methods by using the
``Collection#aggregate()`` method and passing in an array of aggregation
operations. Using the {+ruby-driver+} to perform the aggregation returns
raw ``BSON::Document`` objects rather than ``Mongoid::Document`` model
instances.

Example
~~~~~~~

Consider a database that contains a collection with documents that are modeled
by the following classes:

.. 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

The following example creates an aggregation pipeline to retrieve all bands that
have toured since ``2000`` and have at least ``1`` award:

.. io-code-block::

.. input:: /includes/aggregation/ruby-aggregation.rb
:language: ruby

.. output::

[
{"_id": "...", "name": "Deftones" },
{"_id": "...", "name": "Tool"},
...
]

.. tip::

The preceding example projects only the ``_id`` field of the output
documents. It then uses the projected results to find the documents and return
them as ``Mongoid::Document`` model instances. This optional step is not
required to run an aggregation pipeline.

Additional Information
----------------------

To view a full list of aggregation operators, see :manual:`Aggregation
Operators. </reference/operator/aggregation/>`

To learn about assembling an aggregation pipeline and view examples, see
:manual:`Aggregation Pipeline. </core/aggregation-pipeline/>`

To learn more about creating pipeline stages, see :manual:`Aggregation
Stages. </reference/operator/aggregation-pipeline/>`

API Documentation
~~~~~~~~~~~~~~~~~

To learn more about any of the methods discussed in this
guide, see the following API documentation:

- `group() <{+api-root+}/Criteria/Queryable/Aggregable.html#group-instance_method>`__
- `project() <{+api-root+}/Criteria/Queryable/Aggregable.html#project-instance_method>`__
- `unwind() <{+api-root+}/Criteria/Queryable/Aggregable.html#unwind-instance_method>`__
6 changes: 6 additions & 0 deletions source/includes/aggregation/builder-dsl.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
criteria = Tour.where('participant.name' => 'Serenity').
unwind(:states).
group(_id: 'states', :states.add_to_set => '$states').
project(_id: 0, states: 1)

@states = Tour.collection.aggregate(criteria.pipeline).to_json
21 changes: 21 additions & 0 deletions source/includes/aggregation/ruby-aggregation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
band_ids = Band.collection.aggregate([
{ '$lookup' => {
from: 'tours',
localField: '_id',
foreignField: 'band_id',
as: 'tours',
} },
{ '$lookup' => {
from: 'awards',
localField: '_id',
foreignField: 'band_id',
as: 'awards',
} },
{ '$match' => {
'tours.year' => {'$gte' => 2000},
'awards._id' => {'$exists' => true},
} },
{'$project' => {_id: 1}},
])

bands = Band.find(band_ids.to_a)
1 change: 1 addition & 0 deletions source/index.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ MongoDB in Ruby. To work with {+odm+} from the command line using
tutorials
schema-configuration
working-with-data
/aggregation
API <https://mongodb.com/docs/mongoid/master/api/>
release-notes
contributing
Expand Down
Loading