Skip to content

Positional operator #1542

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

Closed
wants to merge 3 commits into from
Closed
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
171 changes: 94 additions & 77 deletions source/reference/operator/projection/positional.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

.. default-domain:: mongodb

Defintion
---------

.. projection:: $

The positional :projection:`$` operator limits the contents of the
Expand Down Expand Up @@ -44,109 +47,123 @@

db.collection.find( { <array>: <value>, <someOtherArray>: <value2> },
{ "<array>.$": 1 } )
Behavior
--------

Array Field Limitation
~~~~~~~~~~~~~~~~~~~~~~

Since only **one** array field can appear in the query document,
if the array contains documents, to specify criteria on multiple
fields of these documents, use the
:query:`$elemMatch` operator. For example:

.. code-block:: javascript

db.students.find( { grades: { $elemMatch: {
mean: { $gt: 70 },
grade: { $gt:90 }
} } },
{ "grades.$": 1 } )

Sorts and the Positional Operator
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

.. example::
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 positional :projection:`$`
projection operator.

A collection ``students`` contains the following documents:
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 sort was
applied to the elements of the array before the :projection:`$`
projection operator.

.. code-block:: javascript

{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] }
{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] }
{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] }
{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] }
{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] }
{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }
Examples
--------

In the following query, the projection ``{ "grades.$": 1 }``
returns only the first element greater than or equal to ``85``
for the ``grades`` field.
Project Array Values
~~~~~~~~~~~~~~~~~~~~

.. code-block:: javascript
A collection ``students`` contains the following documents:

db.students.find( { semester: 1, grades: { $gte: 85 } },
{ "grades.$": 1 } )
.. code-block:: javascript

The operation returns the following documents:
{ "_id" : 1, "semester" : 1, "grades" : [ 70, 87, 90 ] }
{ "_id" : 2, "semester" : 1, "grades" : [ 90, 88, 92 ] }
{ "_id" : 3, "semester" : 1, "grades" : [ 85, 100, 90 ] }
{ "_id" : 4, "semester" : 2, "grades" : [ 79, 85, 80 ] }
{ "_id" : 5, "semester" : 2, "grades" : [ 88, 88, 92 ] }
{ "_id" : 6, "semester" : 2, "grades" : [ 95, 90, 96 ] }

.. code-block:: javascript
In the following query, the projection ``{ "grades.$": 1 }``
returns only the first element greater than or equal to ``85``
for the ``grades`` field.

{ "_id" : 1, "grades" : [ 87 ] }
{ "_id" : 2, "grades" : [ 90 ] }
{ "_id" : 3, "grades" : [ 85 ] }
.. code-block:: javascript

Although the array field ``grades`` may contain multiple elements
that are greater than or equal to ``85``, the :projection:`$`
projection operator returns only the first matching element from the
array.
db.students.find( { semester: 1, grades: { $gte: 85 } },
{ "grades.$": 1 } )

.. important::
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 positional :projection:`$`
projection operator.
The operation returns the following documents:

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 sort was
applied to the elements of the array before the :projection:`$`
projection operator.
.. code-block:: javascript

.. example::
{ "_id" : 1, "grades" : [ 87 ] }
{ "_id" : 2, "grades" : [ 90 ] }
{ "_id" : 3, "grades" : [ 85 ] }

A ``students`` collection contains the following documents
where the ``grades`` field is an array of documents; each document
contain the three field names ``grade``, ``mean``, and ``std``:
Although the array field ``grades`` may contain multiple elements
that are greater than or equal to ``85``, the :projection:`$`
projection operator returns only the first matching element from the
array.

.. code-block:: javascript
Project Array Documents
~~~~~~~~~~~~~~~~~~~~~~~

{ "_id" : 7, semester: 3, "grades" : [ { grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 } ] }
A ``students`` collection contains the following documents
where the ``grades`` field is an array of documents; each document
contain the three field names ``grade``, ``mean``, and ``std``:

{ "_id" : 8, semester: 3, "grades" : [ { grade: 92, mean: 88, std: 8 },
{ grade: 78, mean: 90, std: 5 },
{ grade: 88, mean: 85, std: 3 } ] }
.. code-block:: javascript

In the following query, the projection ``{ "grades.$": 1 }``
returns only the first element with the ``mean`` greater
than ``70`` for the ``grades`` field. The query also includes a
:method:`~cursor.sort()` to order by ascending ``grades.grade``
field:
{ "_id" : 7, semester: 3, "grades" : [ { grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 } ] }

.. code-block:: javascript
{ "_id" : 8, semester: 3, "grades" : [ { grade: 92, mean: 88, std: 8 },
{ grade: 78, mean: 90, std: 5 },
{ grade: 88, mean: 85, std: 3 } ] }

db.students.find( { "grades.mean": { $gt: 70 } },
{ "grades.$": 1 }
).sort( { "grades.grade": 1 } )
In the following query, the projection ``{ "grades.$": 1 }``
returns only the first element with the ``mean`` greater
than ``70`` for the ``grades`` field. The query also includes a
:method:`~cursor.sort()` to order by ascending ``grades.grade``
field:

The :method:`~db.collection.find()` method sorts the matching
documents **before** it applies the :projection:`$` projection
operator on the ``grades`` array. Thus, the results with the
projected array elements do not reflect the ascending
``grades.grade`` sort order:
.. code-block:: javascript

.. code-block:: javascript
db.students.find( { "grades.mean": { $gt: 70 } },
{ "grades.$": 1 }
).sort( { "grades.grade": 1 } )

{ "_id" : 8, "grades" : [ { "grade" : 92, "mean" : 88, "std" : 8 } ] }
{ "_id" : 7, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 8 } ] }
The :method:`~db.collection.find()` method sorts the matching
documents **before** it applies the :projection:`$` projection
operator on the ``grades`` array. Thus, the results with the
projected array elements do not reflect the ascending
``grades.grade`` sort order:

.. note::
.. code-block:: javascript

Since only **one** array field can appear in the query document,
if the array contains documents, to specify criteria on multiple
fields of these documents, use the
:doc:`/reference/operator/query/elemMatch/` operator, e.g.:
{ "_id" : 8, "grades" : [ { "grade" : 92, "mean" : 88, "std" : 8 } ] }
{ "_id" : 7, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 8 } ] }

.. code-block:: javascript

db.students.find( { grades: { $elemMatch: {
mean: { $gt: 70 },
grade: { $gt:90 }
} } },
{ "grades.$": 1 } )
Further Reading
---------------

.. seealso::
:projection:`$elemMatch (projection) <$elemMatch>`
:projection:`$elemMatch (projection) <$elemMatch>`
113 changes: 73 additions & 40 deletions source/reference/operator/update/positional.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

.. default-domain:: mongodb

Definition
----------

.. update:: $

*Syntax*: ``{ "<array>.$" : value }``
Expand All @@ -28,63 +31,93 @@

db.collection.update( { <array>: value ... }, { <update operator>: { "<array>.$" : value } } )

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

.. code-block:: javascript
Upserts
~~~~~~~

{ "_id" : 1, "grades" : [ 80, 85, 90 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }
Do not use the positional operator :operator:`$` with
:term:`upsert` operations because inserts will use the ``$`` as
a field name in the inserted document.

To update ``80`` to ``82`` in the ``grades`` array in the
first document, use the positional :operator:`$` operator if
you do not know the position of the element in the array:
Unsets
~~~~~~

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

db.students.update( { _id: 1, grades: 80 }, { $set: { "grades.$" : 82 } } )
Negations
~~~~~~~~~

Remember that the positional :operator:`$` operator acts as a
placeholder for the **first match** of the update :ref:`query
document <read-operations-query-document>`.
.. see SERVER-6982

The positional :operator:`$` operator facilitates updates to arrays
that contain embedded documents. Use the positional :operator:`$`
operator to access the fields in the embedded documents with the
:ref:`dot notation <document-dot-notation>` on the
:operator:`$` operator.
If the query matches the array using a negation operator, such as
:query:`$ne`, :query:`$not`, or :query:`$nin`, then you cannot use the
positional operator to update values from this array.

.. code-block:: javascript
However, if the negated portion of the query is inside of an
:query:`$elemMatch` expression, then you *can* use the positional
operator to update this field.

db.collection.update( { <query selector> }, { <update operator>: { "array.$.field" : value } } )
Examples
--------

Consider the following document in the ``students`` collection whose
``grades`` field value is an array of embedded documents:
Update Values in an Array
~~~~~~~~~~~~~~~~~~~~~~~~~

.. code-block:: javascript
Consider a collection ``students`` with the following documents:

{ "_id" : 4, "grades" : [ { grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 } ] }
.. code-block:: javascript

Use the positional :operator:`$` operator to update the value of the
``std`` field in the embedded document with the ``grade`` of ``85``:
{ "_id" : 1, "grades" : [ 80, 85, 90 ] }
{ "_id" : 2, "grades" : [ 88, 90, 92 ] }
{ "_id" : 3, "grades" : [ 85, 100, 90 ] }

.. code-block:: javascript
To update ``80`` to ``82`` in the ``grades`` array in the
first document, use the positional :operator:`$` operator if
you do not know the position of the element in the array:

.. code-block:: javascript

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 :ref:`query
document <read-operations-query-document>`.

Update Documents in an Array
~~~~~~~~~~~~~~~~~~~~~~~~~~~~

The positional :operator:`$` operator facilitates updates to arrays
that contain embedded documents. Use the positional :operator:`$`
operator to access the fields in the embedded documents with the
:ref:`dot notation <document-dot-notation>` on the
:operator:`$` operator.

.. code-block:: javascript

db.collection.update( { <query selector> }, { <update operator>: { "array.$.field" : value } } )

Consider the following document in the ``students`` collection whose
``grades`` field value is an array of embedded documents:

.. code-block:: javascript

db.students.update( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } } )
{ "_id" : 4, "grades" : [ { grade: 80, mean: 75, std: 8 },
{ grade: 85, mean: 90, std: 5 },
{ grade: 90, mean: 85, std: 3 } ] }

.. note::
Use the positional :operator:`$` operator to update the value of the
``std`` field in the embedded document with the ``grade`` of ``85``:

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

- When used with the :update:`$unset` operator, the positional
:operator:`$` operator does not remove the matching element
from the array but rather sets it to ``null``.
db.students.update( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } } )

.. seealso::
Further Reading
---------------

:method:`~db.collection.update()`, :update:`$set` and
:update:`$unset`
:method:`~db.collection.update()`, :update:`$set` and
:update:`$unset`