Skip to content

DOCS-368 Elemmatch and positional projection operators #652

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Feb 15, 2013
Merged
Show file tree
Hide file tree
Changes from all 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
45 changes: 26 additions & 19 deletions source/reference/operator/positional.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,25 @@

The positional :operator:`$` operator identifies an element in an
``array`` field to update without explicitly specifying the position
of the element in the array. The positional :operator:`$` operator,
when used with the :method:`update()
<db.collection.update()>` method and acts as a placeholder for the
**first match** of the update ``query selector``:
of the element in the array. To project, or return, an array element
from a read operation, see the :doc:`$
</reference/projection/positional>` projection operator.

.. code-block:: javascript
When used with the :method:`update() <db.collection.update()>`
method,

- the positional :operator:`$` operator acts as a placeholder for
the **first** element that matches the :ref:`query document
<mongodb-query-document>`, and

db.collection.update( { <query selector> }, { <update operator>: { "array.$" : value } } )
- the ``array`` field **must** appear as part of the ``query
document``.

.. code-block:: javascript

The ``array`` field **must** appear as part of the ``query selector``.
db.collection.update( { <array>: value ... }, { <update operator>: { "<array>.$" : value } } )

Consider the following collection ``students`` with the following documents:
Consider a collection ``students`` with the following documents:

.. code-block:: javascript

Expand All @@ -38,7 +45,8 @@
db.students.update( { _id: 1, grades: 80 }, { $set: { "grades.$" : 82 } } )

Remember that the positional :operator:`$` operator acts as a
placeholder for the **first match** of the update ``query selector``.
placeholder for the **first match** of the update :ref:`query
document <mongodb-query-document>`.

The positional :operator:`$` operator facilitates updates to arrays
that contain embedded documents. Use the positional :operator:`$`
Expand Down Expand Up @@ -66,18 +74,17 @@

db.students.update( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } } )

Consider the following behaviors when using the positional
:operator:`$` operator:

- Do not use the positional operator :operator:`$` with
:term:`upsert` operations because, inserts will use the ``$`` as a field name
in the inserted document.
.. note::

- Do not use the positional operator :operator:`$` with
:term:`upsert` operations because inserts will use the ``$`` as
a field name in the inserted document.

- When used with the :operator:`$unset` operator, the positional
:operator:`$` operator does not remove the matching element from
the array but rather sets it to ``null``.
- When used with the :operator:`$unset` operator, the positional
:operator:`$` operator does not remove the matching element
from the array but rather sets it to ``null``.

.. seealso::

:method:`update() <db.collection.update()>`, :operator:`$set` and
:operator:`$unset`
169 changes: 139 additions & 30 deletions source/reference/projection/elemMatch.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,49 +10,158 @@ $elemMatch (projection)

.. versionadded:: 2.2

Use the :projection:`$elemMatch` projection operator to limit the
response of a query to a single matching element of an
array. Consider the following:
The :projection:`$elemMatch` projection operator limits the contents
of an array field that is included in the query results to contain
only the array element that matches the :projection:`$elemMatch`
condition.

.. note::

- The elements of the array are documents.

- If multiple elements match the :projection:`$elemMatch`
condition, the operator returns the **first** matching element
in the array.

- The :projection:`$elemMatch` projection operator is similar to
the positional :projection:`$` projection operator.

The examples on the :projection:`$elemMatch` projection operator
assumes a collection ``school`` with the following documents:

.. code-block:: javascript

{
_id: 1,
zipcode: 63109,
students: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
}
{
_id: 2,
zipcode: 63110,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}

{
_id: 3,
zipcode: 63109,
students: [
{ name: "ajax", school: 100, age: 7 },
{ name: "achilles", school: 100, age: 8 },
]
}

{
_id: 4,
zipcode: 63109,
students: [
{ name: "barney", school: 102, age: 7 },
]
}

.. example::

Given the following document fragment:
The following :method:`find() <db.collection.find()>` operation
queries for all documents where the value of the ``zipcode``
field is ``63109``. The :projection:`$elemMatch` projection
returns only the **first** matching element of the ``students``
array where the ``school`` field has a value of ``102``:

.. code-block:: javascript

{
_id: ObjectId(),
zipcode: 63109,
dependents: [
{ name: "john", school: 102, age: 10 },
{ name: "jess", school: 102, age: 11 },
{ name: "jeff", school: 108, age: 15 }
]
}
db.schools.find( { zipcode: 63109 },
{ students: { $elemMatch: { school: 102 } } } )

Consider the following :method:`find() <db.collection.find()>`
operation:
The operation returns the following documents:

.. code-block:: javascript

var projection = { _id: 0, dependents: { $elemMatch: { school: 102 }}};
db.students.find( { zipcode: 63109 }, projection);
{ "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 } ] }
{ "_id" : 3 }
{ "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }

The query would return all documents where the value of the
``zipcode`` field is ``63109``, while the projection excludes
the ``_id`` field and only includes the first matching element of
the ``dependents`` array where the ``school`` element has a value of
``102``. The documents would take the following form:
- For the document with ``_id`` equal to ``1``, the ``students``
array contains multiple elements with the ``school`` field
equal to ``102``. However, the :projection:`$elemMatch`
projection returns only the first matching element from the
array.

- The document with ``_id`` equal to ``3`` does not contain the
``students`` field in the result since no element in its
``students`` array matched the :projection:`$elemMatch`
condition.

The :projection:`$elemMatch` projection can specify criteria on multiple
fields:

.. example::

The following :method:`find() <db.collection.find()>` operation
queries for all documents where the value of the ``zipcode``
field is ``63109``. The projection includes the **first**
matching element of the ``students`` array where the ``school``
field has a value of ``102`` **and** the ``age`` field is greater
than ``10``:

.. code-block:: javascript

db.schools.find( { zipcode: 63109 },
{ students: { $elemMatch: { school: 102, age: { $gt: 10} } } } )

The operation returns the three documents that have ``zipcode`` equal to ``63109``:

.. code-block:: javascript

{ "_id" : 1, "students" : [ { "name" : "jess", "school" : 102, "age" : 11 } ] }
{ "_id" : 3 }
{ "_id" : 4 }

Documents with ``_id`` equal to ``3`` and ``_id`` equal to ``4``
do not contain the ``students`` field since no element matched
the :projection:`$elemMatch` criteria.

When the :method:`~db.collection.find()` method includes a
:method:`~cursor.sort()`, the :method:`~db.collection.find()` method
applies the :method:`~cursor.sort()` to order the matching documents
**before** it applies the projection.

If an array field contains multiple documents with the same field
name and the :method:`~db.collection.find()` method includes a
:method:`~cursor.sort()` on that repeating field, the returned
documents may not reflect the sort order because the
:method:`~cursor.sort()` was applied to the elements of the array
before the :projection:`elemMatch` projection.

.. example::

The following query includes a :method:`~cursor.sort()` to order
by descending ``students.age`` field:

.. code-block:: javascript

db.schools.find(
{ zipcode: 63109 },
{ students: { $elemMatch: { school: 102 } } }
).sort( { "students.age": -1 } )

The operation applies the :method:`~cursor.sort()` to order the
documents that have the field ``zipcode`` equal to ``63109`` and
then applies the projection. The operation returns the three
documents in the following order:

.. code-block:: javascript

{
dependents: [
{ name: "john", school: 102, age: 10 }
]
}
{ "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 } ] }
{ "_id" : 3 }
{ "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }

.. note::
.. seealso::

The :projection:`$elemMatch` projection will only match one array
element per source document.
:projection:`$ (projection) <$>` operator
Loading