$ (projection)

定义

$运算符<array>将查询结果中的内容限制为仅包含与查询文档匹配的第一个元素。

当您在选定文档中只需要一个特定的数组元素时,请在find()方法或findOne()方法的投影文档中使用$。

请参阅聚合运算符,$filter以返回仅包含与指定条件匹配的那些元素的数组。

使用注意事项

$运算符和$elemMatch运算符均基于条件从数组中投影第一个匹配元素。

$运算符根据查询语句中的某些条件从集合中的每个文档中投影第一个匹配的数组元素。

$elemMatch投影运算符采用显式条件参数。这样,您可以根据查询中没有的条件进行投影,或者如果您需要基于数组的嵌入式文档中的多个字段进行投影。

db.collection.find()在视图不支持$投影操作。

特性

使用要求

给定形式:

1
2
3
4
db.collection.find( { <array>: <value> ... },
                    { "<array>.$": 1 } )
db.collection.find( { <array.field>: <value> ...},
                    { "<array>.$": 1 } )

<array>领域的限制必须出现在查询文档,并且<value>可以是包含文档的查询运算符表达式。

数组字段限制

在处理数组投影时,MongoDB需要满足以下条件:

  • 投影文档中只能出现一个$运算符。
  • 在查询文档中应仅出现一个数组字段,该字段受投影运算符$限制。查询文档中的其他数组字段可能导致未定义的特性。
  • 查询文档应该只包含在数组字段单一条件被投影。多个条件可能在内部相互覆盖并导致不确定的特性。

在这些要求下,以下查询不正确:

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

要在该数组内的文档的多个字段上指定条件,请使用$elemMatch查询运算符。以下查询返回grades数组中的第一个文档,该数组的mean大于70,grade大于90。

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

如果需要单独的条件来选择文档以及在这些文档中选择字段,则必须使用运算符$elemMatch

排序和位置运算符

当find()方法包括时 sort(),该find() 方法在应用位置$投影算符之前应用sort()来对匹配的文档进行排序。

如果数组字段包含具有相同字段名的多个文档,并且该find()方法sort()在该重复字段上包含 ,则返回的文档可能无法反映排序顺序,因为排序是在$ 投影运算符之前应用于数组的元素。

例子

投影数组值

集合students包含以下文档:

1
2
3
4
5
6
{ "_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 ] }

在以下查询中,投影{ "grades.$": 1 }仅返回grades大于或等于85该字段的第一个元素。

1
2
db.students.find( { semester: 1, grades: { $gte: 85 } },
                  { "grades.$": 1 } )

该操作返回以下文档:

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

尽管数组字段grades可能包含多个大于或等于的元素85,但投影运算符$仅返回数组中的第一个匹配元素。

投影数组文档

students集合包含下列文件,其中grades字段是文档的数组; 每个文档包含三个字段名称grade,mean以及std:

1
2
3
4
5
6
7
{ "_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 } ] }

在下面的查询中,投影{ "grades.$": 1 }仅返回第一个平均分数字段大于70的元素:

1
2
3
4
db.students.find(
   { "grades.mean": { $gt: 70 } },
   { "grades.$": 1 }
)

该操作返回以下文档:

1
2
{ "_id" : 7, "grades" : [  {  "grade" : 80,  "mean" : 75,  "std" : 8 } ] }
{ "_id" : 8, "grades" : [  {  "grade" : 92,  "mean" : 88,  "std" : 8 } ] }

$elemMatch (projection)

$elemMatch运算符将查询结果中<array>字段的内容限制为仅包含与$elemMatch条件匹配的第一个元素。

使用注意事项

$运算符和$ elemMatch运算符均基于条件从数组中投影第一个匹配元素。

$运算符根据查询语句中的某些条件从集合中的每个文档中投影第一个匹配的数组元素。

$elemMatch投影运算符采用显式条件参数。这样,您可以根据查询中没有的条件进行投影,或者如果您需要基于数组的嵌入式文档中的多个字段进行投影。

db.collection.find()在视图不支持$投影操作。

您不能在$elemMatch中指定$text查询表达式。

例子

假定schools包含以下文档的集合:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
{
 _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 },
              { name: "ruth", school: 102, age: 16 },
           ]
}

邮政编码搜索

以下find()操作查询该zipcode 字段的值为63109的所有文档。在$elemMatch投影仅返回第一所述的匹配元件students ,其中阵列school字段具有值102:

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

该操作返回以下文档,这些文档zipcode 等于63109并students使用投影该数组 $elemMatch:

1
2
3
{ "_id" : 1, "students" : [ { "name" : "john", "school" : 102, "age" : 10 } ] }
{ "_id" : 3 }
{ "_id" : 4, "students" : [ { "name" : "barney", "school" : 102, "age" : 7 } ] }
  • 对于_id等于1的文档,students 数组包含school字段等于102的多个元素。但是$elemMatch投影仅返回数组中的第一个匹配元素。
  • _id等于3的文档在结果中不包含该 students字段,因为其students数组中没有元素 与$elemMatch 条件匹配。

多个字段$elemMatch

$elemMatch投影可以在多个字段上指定条件:

以下find()操作查询该zipcode 字段的值为63109的所有文档。投影包含数组的第一个匹配元素,students的school字段的值为102,并且age字段大于10:

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

该操作返回zipcode等于63109的三个文档:

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

_id等于的文档3不包含该students字段,因为没有数组元素符合$elemMatch条件。

$meta

$meta投影运算符为每个匹配的文档返回与查询关联的元数据(例如“textScore”)。

https://docs.mongodb.com/manual/reference/operator/projection/meta/

$slice (projection)

$slice运算符控制查询返回的数组的项数。有关在使用$push进行更新期间限制数组大小的信息,请改为使用$slice修饰符。

db.collection.find()在视图操作不支持$slice投影操作。

考虑以下原型查询:

1
db.collection.find( { field: value }, { array: {$slice: count } } );

此操作选择collection一个名为的字段标识的文档,该字段field保存value并返回由count存储在该array字段中的数组的value所指定的元素数。如果count值大于array查询中的元素数,则返回数组的所有元素。

$slice接受多种格式的参数,包括负值和数组。考虑以下示例:

1
db.posts.find( {}, { comments: { $slice: 5 } } )

在这里,$slice选择comments字段中数组的前五个项目。

1
db.posts.find( {}, { comments: { $slice: -5 } } )

此操作返回数组中的最后五个项目。

以下示例将数组指定为$slice的参数。数组采用[skip,limit]的形式,其中第一个值表示要跳过的数组中的项目数,第二个值表示要返回的项目数。

1
db.posts.find( {}, { comments: { $slice: [ 20, 10 ] } } )

在这里,在跳过该数组的前20个项目之后,查询将仅返回10个项目。

1
db.posts.find( {}, { comments: { $slice: [ -20, 10 ] } } )

此操作还返回10个项目,从数组最后一个项目开始的第20个项目开始。