diff --git a/source/reference/operator/positional.txt b/source/reference/operator/positional.txt index f9028790ac3..08815de5b87 100644 --- a/source/reference/operator/positional.txt +++ b/source/reference/operator/positional.txt @@ -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() - ` 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:`$ + ` projection operator. - .. code-block:: javascript + When used with the :method:`update() ` + method, + + - the positional :operator:`$` operator acts as a placeholder for + the **first** element that matches the :ref:`query document + `, and - db.collection.update( { }, { : { "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( { : value ... }, { : { ".$" : value } } ) - Consider the following collection ``students`` with the following documents: + Consider a collection ``students`` with the following documents: .. code-block:: javascript @@ -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 `. The positional :operator:`$` operator facilitates updates to arrays that contain embedded documents. Use the positional :operator:`$` @@ -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() `, :operator:`$set` and :operator:`$unset` diff --git a/source/reference/projection/elemMatch.txt b/source/reference/projection/elemMatch.txt index ad367ba0ed4..4d668100fbb 100644 --- a/source/reference/projection/elemMatch.txt +++ b/source/reference/projection/elemMatch.txt @@ -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() ` 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() ` - 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() ` 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 diff --git a/source/reference/projection/positional.txt b/source/reference/projection/positional.txt new file mode 100644 index 00000000000..6474a59e989 --- /dev/null +++ b/source/reference/projection/positional.txt @@ -0,0 +1,152 @@ +=============== +\$ (projection) +=============== + +.. default-domain:: mongodb + +.. operator:: $ + + The positional :projection:`$` operator limits the contents of the + ```` field that is included in the query results to contain + the **first** matching element. To specify an array element to + update, see the :doc:`positional $ operator for updates + `. + + Used in the :term:`projection` document of the + :method:`~db.collection.find()` method or the + :method:`~db.collection.findOne()` method: + + - The :projection:`$` projection operator limits the content of the + ```` field to the **first** element that matches the + :ref:`query document `. + + - The ```` field **must** appear in the :ref:`query document + ` + + .. code-block:: javascript + + db.collection.find( { : ... }, + { ".$": 1 } ) + db.collection.find( { : ...}, + { ".$": 1 } ) + + The ```` can be documents that contains :ref:`query operator + expressions `. + + - Only **one** positional :projection:`$` operator can appear in the + projection document. + + - Only **one** array field can appear in the :ref:`query document + `; i.e. the following query is + **incorrect**: + + .. code-block:: javascript + + db.collection.find( { : , : }, + { ".$": 1 } ) + + .. example:: + + A collection ``students`` contains the following documents: + + .. 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 ] } + + In the following query, the projection ``{ "grades.$": 1 }`` + returns only the first element greater than or equal to ``85`` + for the ``grades`` field. + + .. code-block:: javascript + + db.students.find( { semester: 1, grades: { $gte: 85 } }, + { "grades.$": 1 } ) + + The operation returns the following documents: + + .. code-block:: javascript + + { "_id" : 1, "grades" : [ 87 ] } + { "_id" : 2, "grades" : [ 90 ] } + { "_id" : 3, "grades" : [ 85 ] } + + 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. + + .. 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. + + 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. + + .. example:: + + 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``: + + .. code-block:: javascript + + { "_id" : 7, semester: 3, "grades" : [ { grade: 80, mean: 75, std: 8 }, + { grade: 85, mean: 90, std: 5 }, + { grade: 90, mean: 85, std: 3 } ] } + + { "_id" : 8, semester: 3, "grades" : [ { grade: 92, mean: 88, std: 8 }, + { grade: 78, mean: 90, std: 5 }, + { grade: 88, mean: 85, std: 3 } ] } + + 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: + + .. code-block:: javascript + + db.students.find( { "grades.mean": { $gt: 70 } }, + { "grades.$": 1 } + ).sort( { "grades.grade": 1 } ) + + 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 + + { "_id" : 8, "grades" : [ { "grade" : 92, "mean" : 88, "std" : 8 } ] } + { "_id" : 7, "grades" : [ { "grade" : 80, "mean" : 75, "std" : 8 } ] } + + .. note:: + + 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/elemMatch/` operator, e.g.: + + .. code-block:: javascript + + db.students.find( { grades: { $elemMatch: { + mean: { $gt: 70 }, + grade: { $gt:90 } + } } }, + { "grades.$": 1 } ) + +.. seealso:: + :projection:`$elemMatch (projection) <$elemMatch>`