Skip to content

Commit 12bacc6

Browse files
p-mongop
andauthored
MONGOID-5369 Move Aggregation Pipeline docs to their own page (#5320)
Co-authored-by: Oleg Pudeyev <[email protected]>
1 parent 4cdc2bd commit 12bacc6

File tree

2 files changed

+190
-177
lines changed

2 files changed

+190
-177
lines changed

docs/reference/aggregation.txt

Lines changed: 190 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,190 @@
1+
.. _aggregation-pipeline:
2+
3+
********************
4+
Aggregation Pipeline
5+
********************
6+
7+
.. default-domain:: mongodb
8+
9+
.. contents:: On this page
10+
:local:
11+
:backlinks: none
12+
:depth: 2
13+
:class: singlecol
14+
15+
16+
Mongoid exposes MongoDB's `aggregation pipeline
17+
<https://mongodb.com/docs/manual/core/aggregation-pipeline/>`_ for queries
18+
involving multiple referenced associations at the same time. Given the
19+
following model definitions with referenced associations:
20+
21+
.. code-block:: ruby
22+
23+
class Band
24+
include Mongoid::Document
25+
has_many :tours
26+
has_many :awards
27+
field :name, type: String
28+
end
29+
30+
class Tour
31+
include Mongoid::Document
32+
belongs_to :band
33+
field :year, type: Integer
34+
end
35+
36+
class Award
37+
include Mongoid::Document
38+
belongs_to :band
39+
field :name, type: String
40+
end
41+
42+
To retrieve bands that toured since 2000 and have at least one award, one
43+
could do the following:
44+
45+
.. code-block:: ruby
46+
47+
band_ids = Band.collection.aggregate([
48+
{'$lookup' => {
49+
from: 'tours',
50+
localField: '_id',
51+
foreignField: 'band_id',
52+
as: 'tours',
53+
}},
54+
{'$lookup' => {
55+
from: 'awards',
56+
localField: '_id',
57+
foreignField: 'band_id',
58+
as: 'awards',
59+
}},
60+
{'$match' => {
61+
'tours.year' => {'$gte' => 2000},
62+
'awards._id' => {'$exists' => true},
63+
}},
64+
{'$project' => {_id: 1}},
65+
])
66+
bands = Band.find(band_ids)
67+
68+
Note that the aggregation pipeline, since it is implemented by the Ruby driver
69+
for MongoDB and not Mongoid, returns raw ``BSON::Document`` objects rather than
70+
``Mongoid::Document`` model instances. The above example projects only
71+
the ``_id`` field which is then used to load full models. An alternative is
72+
to not perform such a projection and work with raw fields, which would eliminate
73+
having to send the list of document ids to Mongoid in the second query
74+
(which could be large).
75+
76+
77+
.. _aggregation-pipeline-builder-dsl:
78+
79+
Builder DSL
80+
-----------
81+
82+
Mongoid provides limited support for constructing the aggregation pipeline
83+
itself using a high-level DSL. The following aggregation pipeline operators
84+
are supported:
85+
86+
- `$group <https://mongodb.com/docs/manual/reference/operator/aggregation/group/>`_
87+
- `$project <https://mongodb.com/docs/manual/reference/operator/aggregation/project/>`_
88+
- `$unwind <https://mongodb.com/docs/manual/reference/operator/aggregation/unwind/>`_
89+
90+
To construct a pipeline, call the corresponding aggregation pipeline methods
91+
on a ``Criteria`` instance. Aggregation pipeline operations are added to the
92+
``pipeline`` attribute of the ``Criteria`` instance. To execute the pipeline,
93+
pass the ``pipeline`` attribute value to ``Collection#aggragegate`` method.
94+
95+
For example, given the following models:
96+
97+
.. code-block:: ruby
98+
99+
class Tour
100+
include Mongoid::Document
101+
102+
embeds_many :participants
103+
104+
field :name, type: String
105+
field :states, type: Array
106+
end
107+
108+
class Participant
109+
include Mongoid::Document
110+
111+
embedded_in :tour
112+
113+
field :name, type: String
114+
end
115+
116+
We could find out which states a participant visited:
117+
118+
.. code-block:: ruby
119+
120+
criteria = Tour.where('participants.name' => 'Serenity',).
121+
unwind(:states).
122+
group(_id: 'states', :states.add_to_set => '$states').
123+
project(_id: 0, states: 1)
124+
125+
pp criteria.pipeline
126+
# => [{"$match"=>{"participants.name"=>"Serenity"}},
127+
# {"$unwind"=>"$states"},
128+
# {"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}},
129+
# {"$project"=>{"_id"=>0, "states"=>1}}]
130+
131+
Tour.collection.aggregate(criteria.pipeline).to_a
132+
133+
134+
group
135+
`````
136+
137+
The ``group`` method adds a `$group aggregation pipeline stage
138+
<https://mongodb.com/docs/manual/reference/operator/aggregation/group/>`_.
139+
140+
The field expressions support Mongoid symbol-operator syntax:
141+
142+
.. code-block:: ruby
143+
144+
criteria = Tour.all.group(_id: 'states', :states.add_to_set => '$states')
145+
criteria.pipeline
146+
# => [{"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}}]
147+
148+
Alternatively, standard MongoDB aggregation pipeline syntax may be used:
149+
150+
.. code-block:: ruby
151+
152+
criteria = Tour.all.group(_id: 'states', states: {'$addToSet' => '$states'})
153+
154+
155+
project
156+
```````
157+
158+
The ``project`` method adds a `$project aggregation pipeline stage
159+
<https://mongodb.com/docs/manual/reference/operator/aggregation/project/>`_.
160+
161+
The argument should be a Hash specifying the projection:
162+
163+
.. code-block:: ruby
164+
165+
criteria = Tour.all.project(_id: 0, states: 1)
166+
criteria.pipeline
167+
# => [{"$project"=>{"_id"=>0, "states"=>1}}]
168+
169+
170+
.. _unwind-dsl:
171+
172+
unwind
173+
``````
174+
175+
The ``unwind`` method adds an `$unwind aggregation pipeline stage
176+
<https://mongodb.com/docs/manual/reference/operator/aggregation/unwind/>`_.
177+
178+
The argument can be a field name, specifiable as a symbol or a string, or
179+
a Hash or a ``BSON::Document`` instance:
180+
181+
.. code-block:: ruby
182+
183+
criteria = Tour.all.unwind(:states)
184+
criteria = Tour.all.unwind('states')
185+
criteria.pipeline
186+
# => [{"$unwind"=>"$states"}]
187+
188+
criteria = Tour.all.unwind(path: '$states')
189+
criteria.pipeline
190+
# => [{"$unwind"=>{:path=>"$states"}}]

docs/reference/associations.txt

Lines changed: 0 additions & 177 deletions
Original file line numberDiff line numberDiff line change
@@ -1637,180 +1637,3 @@ with, and is useful for developers of extensions to Mongoid.
16371637
- Returns the name of the field to set the polymorphic type.
16381638
* - ``Association#validate?``
16391639
- Returns whether the association has an associated validation.
1640-
1641-
1642-
.. _aggregation-pipeline:
1643-
1644-
Aggregation Pipeline
1645-
====================
1646-
1647-
Mongoid exposes MongoDB's aggregation pipeline for queries involving multiple
1648-
referenced associations at the same time. Given the same setup as before with
1649-
referenced associations:
1650-
1651-
.. code-block:: ruby
1652-
1653-
class Band
1654-
include Mongoid::Document
1655-
has_many :tours
1656-
has_many :awards
1657-
field :name, type: String
1658-
end
1659-
1660-
class Tour
1661-
include Mongoid::Document
1662-
belongs_to :band
1663-
field :year, type: Integer
1664-
end
1665-
1666-
class Award
1667-
include Mongoid::Document
1668-
belongs_to :band
1669-
field :name, type: String
1670-
end
1671-
1672-
To retrieve bands that toured since 2000 and have at least one award, one
1673-
could do the following:
1674-
1675-
.. code-block:: ruby
1676-
1677-
band_ids = Band.collection.aggregate([
1678-
{'$lookup' => {
1679-
from: 'tours',
1680-
localField: '_id',
1681-
foreignField: 'band_id',
1682-
as: 'tours',
1683-
}},
1684-
{'$lookup' => {
1685-
from: 'awards',
1686-
localField: '_id',
1687-
foreignField: 'band_id',
1688-
as: 'awards',
1689-
}},
1690-
{'$match' => {
1691-
'tours.year' => {'$gte' => 2000},
1692-
'awards._id' => {'$exists' => true},
1693-
}},
1694-
{'$project' => {_id: 1}},
1695-
])
1696-
bands = Band.find(band_ids)
1697-
1698-
Note that the aggregation pipeline, since it is implemented by the Ruby driver
1699-
for MongoDB and not Mongoid, returns raw ``BSON::Document`` objects rather than
1700-
``Mongoid::Document`` model instances. The above example projects only
1701-
the ``_id`` field which is then used to load full models. An alternative is
1702-
to not perform such a projection and work with raw fields, which would eliminate
1703-
having to send the list of document ids to Mongoid in the second query
1704-
(which could be large).
1705-
1706-
.. _aggregation-pipeline-builder-dsl:
1707-
1708-
Builder DSL
1709-
-----------
1710-
1711-
Mongoid provides limited support for constructing the aggregation pipeline
1712-
itself using a high-level DSL. The following aggregation pipeline operators
1713-
are supported:
1714-
1715-
- `$group <https://mongodb.com/docs/manual/reference/operator/aggregation/group/>`_
1716-
- `$project <https://mongodb.com/docs/manual/reference/operator/aggregation/project/>`_
1717-
- `$unwind <https://mongodb.com/docs/manual/reference/operator/aggregation/unwind/>`_
1718-
1719-
To construct a pipeline, call the corresponding aggregation pipeline methods
1720-
on a ``Criteria`` instance. Aggregation pipeline operations are added to the
1721-
``pipeline`` attribute of the ``Criteria`` instance. To execute the pipeline,
1722-
pass the ``pipeline`` attribute value to ``Collection#aggragegate`` method.
1723-
1724-
For example, given the following models:
1725-
1726-
.. code-block:: ruby
1727-
1728-
class Tour
1729-
include Mongoid::Document
1730-
1731-
embeds_many :participants
1732-
1733-
field :name, type: String
1734-
field :states, type: Array
1735-
end
1736-
1737-
class Participant
1738-
include Mongoid::Document
1739-
1740-
embedded_in :tour
1741-
1742-
field :name, type: String
1743-
end
1744-
1745-
We could find out which states a participant visited:
1746-
1747-
.. code-block:: ruby
1748-
1749-
criteria = Tour.where('participants.name' => 'Serenity',).
1750-
unwind(:states).
1751-
group(_id: 'states', :states.add_to_set => '$states').
1752-
project(_id: 0, states: 1)
1753-
1754-
pp criteria.pipeline
1755-
# => [{"$match"=>{"participants.name"=>"Serenity"}},
1756-
# {"$unwind"=>"$states"},
1757-
# {"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}},
1758-
# {"$project"=>{"_id"=>0, "states"=>1}}]
1759-
1760-
Tour.collection.aggregate(criteria.pipeline).to_a
1761-
1762-
group
1763-
`````
1764-
1765-
The ``group`` method adds a `$group aggregation pipeline stage
1766-
<https://mongodb.com/docs/manual/reference/operator/aggregation/group/>`_.
1767-
1768-
The field expressions support Mongoid symbol-operator syntax:
1769-
1770-
.. code-block:: ruby
1771-
1772-
criteria = Tour.all.group(_id: 'states', :states.add_to_set => '$states')
1773-
criteria.pipeline
1774-
# => [{"$group"=>{"_id"=>"states", "states"=>{"$addToSet"=>"$states"}}}]
1775-
1776-
Alternatively, standard MongoDB aggregation pipeline syntax may be used:
1777-
1778-
.. code-block:: ruby
1779-
1780-
criteria = Tour.all.group(_id: 'states', states: {'$addToSet' => '$states'})
1781-
1782-
project
1783-
```````
1784-
1785-
The ``project`` method adds a `$project aggregation pipeline stage
1786-
<https://mongodb.com/docs/manual/reference/operator/aggregation/project/>`_.
1787-
1788-
The argument should be a Hash specifying the projection:
1789-
1790-
.. code-block:: ruby
1791-
1792-
criteria = Tour.all.project(_id: 0, states: 1)
1793-
criteria.pipeline
1794-
# => [{"$project"=>{"_id"=>0, "states"=>1}}]
1795-
1796-
.. _unwind-dsl:
1797-
1798-
unwind
1799-
``````
1800-
1801-
The ``unwind`` method adds an `$unwind aggregation pipeline stage
1802-
<https://mongodb.com/docs/manual/reference/operator/aggregation/unwind/>`_.
1803-
1804-
The argument can be a field name, specifiable as a symbol or a string, or
1805-
a Hash or a ``BSON::Document`` instance:
1806-
1807-
.. code-block:: ruby
1808-
1809-
criteria = Tour.all.unwind(:states)
1810-
criteria = Tour.all.unwind('states')
1811-
criteria.pipeline
1812-
# => [{"$unwind"=>"$states"}]
1813-
1814-
criteria = Tour.all.unwind(path: '$states')
1815-
criteria.pipeline
1816-
# => [{"$unwind"=>{:path=>"$states"}}]

0 commit comments

Comments
 (0)