Definition 定义

将带有请求字段的文档传递给 pipeline 中的下一个 stage。指定的字段可以是输入文档中的现有字段新计算的字段

$project stage 具有以下原型形式:

  1. { $project: { <specificatino(s)> } }

$project 接收一个文档,该文档可以指定包含字段禁止 _id 字段添加新字段以及重设现有字段的值。或者,你也可以指定排除字段

$project 规范有以下形式:

Form Description
<field>: <1 or true> 指定包含一个字段。非零(non-zero)的整数也被视为 true
_id: <0 or false> 指定禁止 _id 字段。

要有条件地排除一个字段,请使用 REMOVE 变量来代替。详情参阅 Exclude Fields Conditionally(有条件地排除字段)
<field>: <expression> 添加新字段或重置现有字段的值。

如果表达式评估为 $$REMOVE,输出中就会排除该字段。详情参阅 Exclude Fields Conditionally(有条件地排除字段)
<field>: <0 or false> 指定排除一个字段。

要有条件地排除一个字段,请使用 REMOVE 变量来代替。详情参阅 Exclude Fields Conditionally(有条件地排除字段)

如果您指定排除 _id 以外的字段,您**不能**使用任何其他 $project 规范形式。这一限制并不适用于使用 REMOVE 变量有条件地排除一个字段。

也可以参阅 $unset stage,以排除字段。

Considerations 注意事项

Include Existing Fields 包含现有字段

  • 默认情况下,_id 字段被包含在输出文档中。要在输出文档中包含输入文档中的任何其他字段,你必须在 $project 中明确指定包含。
  • 如果你指定包含一个在文档中不存在的字段,$project 会忽略该字段的包含,并且不将该字段添加到文档中。

    Suppress the _id Field 抑制/禁止 _id 字段

    默认情况下,_id 字段被包含在输出文档中。要从输出文档中排除 _id 字段,你必须在 $project 中明确指定抑制/禁止 _id 字段。

    Exclude Fields 排除字段

    如果你指定排除一个或多个字段,那么所有其他的字段将在输出文档中返回。
    1. { $project: { "<field1>": 0, "<field2>": 0, ... } } // 返回除指定字段之外的所有字段
    如果您指定排除 _id 以外的字段,您不能采用任何其他 $project 规范形式:即如果您排除字段,您也不能指定包含字段,重置现有字段的值,或添加新字段。这一限制并不适用于使用 REMOVE 变量的有条件排除字段。

也可参阅 $unset stage,以排除字段。

Exclude Fields Conditionally 有条件地排除字段

你可以在 aggregation expression 中使用变量 REMOVE 来有条件地抑制/禁止一个字段。有关示例,请参阅 Conditionally Exclude Fields(有条件地排除字段)

Add New Fields or Reset Existing Fields 添加新字段或重置现有字段

NOTE MongoDB还提供了 $addFields 来向文档添加新字段。

要添加一个新的字段或重置一个现有字段的值,指定字段名并将其值设置为某个 expression 。关于 expressions 的更多信息,参阅 Expressions

Literal Values 字面值

要将一个字段的值直接设置为数字或布尔值,而不是将字段设置为可解析为字段的 expression,请使用 $literal operator。否则,$project 会将数字或布尔字段视为包括或排除该字段的标志。

Field Rename 重命名字段

通过指定一个新字段并将其值设置为现有字段的 field path,你可以有效地重命名一个字段。

New Array Fields 新的数组字段

$project stage 支持使用方括号 [] 来直接创建新的数组字段。如果你指定的数组字段在文档中不存在,该 operation 会将 null 替换为该字段的值。有关示例,请参阅 Project New Array Fields(project 新的数组字段)

你不能在 $project stage 使用数组索引。参阅 Array Indexes are Unsupported(数组索引不被支持)

Embedded Document Fields 嵌入式文档字段

当 projecting 或 adding/resetting 一个嵌入式文档中的字段时,你可以使用点符号,如:

  1. "contact.address.country": <1 or 0 or expression>

或者你可以嵌套字段:

  1. contact: { address: { country: <1 or 0 or expression> } }

当嵌套字段时,你不能在嵌入的文档中使用点符号来指定字段,例如,contact: { "address.country": <1 or 0 or expression> } 是无效(invalid)的。

Path Collision Error in Embedded Fields 嵌入字段中的路径冲突错误

你不能在同一个 projection 中同时指定一个嵌入文档和该嵌入文档中的一个字段。

下面的 $project stage 失败了,因为它试图同时 project 嵌入式 contact 文档和 contact.address.country 字段,所以出现了 Path collision error(路径碰撞错误)

  1. { $project: { contact: 1, "contact.address.country": 1 } }

无论指定父文档和嵌入字段的顺序如何,该错误都会发生。下面的 $project 失败了,出现同样的错误:

  1. { $project: { "contact.address.country": 1, contact: 1 } }

Restrictions 限制

如果 $project 规范是一个空文档,将返回一个错误。

你不能在 $project stage 使用数组索引。参阅 Array Indexes are Unsupported(数组索引不被支持)

Example 例子

Include Specific Fields in Output Documents 在输出文档中包含特定字段

考虑一个包含以下文档的 books collection:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5
  7. }

以下 $project stage 在其输出文档中仅包含 _idtitleauthor 字段:

  1. db.books.aggregate( [
  2. { $project: { title: 1, author: 1 } }
  3. ] )

该 operation 的结果如以下文档:

  1. { "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }

Suppress _id Field in the Output Documents 在输出文档中抑制/禁止 _id 字段

默认情况下,_id 字段总是被包括在内。要从 $project stage 的输出文档中排除 _id 字段,请在 projection 文档中指定排除 _id 字段,将其设置为 0

考虑一个包含以下文档的 books collection:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5
  7. }

以下 $project stage 不包括 _id 字段,但在其输出文档中包含 titleauthor 字段:

  1. db.books.aggregate( [
  2. { $project: { _id: 0, title: 1, author: 1 } }
  3. ] )

该 operation 结果如以下文档:

  1. { "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }

Exclude Fields from Output Documents 从输出文档中排除字段

考虑一个包含以下文档的 books collection:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5,
  7. lastModified: "2016-07-28"
  8. }

下面的 $project stage 从输出中排除了 lastModified 字段:

  1. db.books.aggregate( [
  2. { $project: { "lastModified": 0 } }
  3. ] )

也可参阅 $unset stage,以排除字段。

Exclude Fields from Embedded Documents 从嵌入式文档中排除字段

考虑一个包含以下文档的 books collection:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5,
  7. lastModified: "2016-07-28"
  8. }

下面的 $project stage 从输出中排除了 author.firstlastModified 字段:

  1. db.books.aggregate( [
  2. { $project: { "author.first": 0, "lastModified": 0 } }
  3. ] )

或者,您可以将排除规范嵌套在文档中:

  1. db.books.aggregate( [
  2. {
  3. $project: {
  4. "author": { "first": 0 },
  5. "lastModified": 0
  6. }
  7. }
  8. ] )

这两种规范都会产生相同的输出:

  1. {
  2. "_id" : 1,
  3. "title" : "abc123",
  4. "isbn" : "0001122223334",
  5. "author" : {
  6. "last" : "zzz"
  7. },
  8. "copies" : 5,
  9. }

也可参阅 $unset stage,以排除字段。

Conditionally Exclude Fields 有条件地排除字段

你可以在 aggregation expressions 中使用变量 REMOVE 来有条件地抑制/禁止一个字段。

考虑一个包含以下文档的 books collection:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5,
  7. lastModified: "2016-07-28"
  8. }
  9. {
  10. "_id" : 2,
  11. title: "Baked Goods",
  12. isbn: "9999999999999",
  13. author: { last: "xyz", first: "abc", middle: "" },
  14. copies: 2,
  15. lastModified: "2017-07-21"
  16. }
  17. {
  18. "_id" : 3,
  19. title: "Ice Cream Cakes",
  20. isbn: "8888888888888",
  21. author: { last: "xyz", first: "abc", middle: "mmm" },
  22. copies: 5,
  23. lastModified: "2017-07-22"
  24. }

下面的 $project stage 使用 REMOVE 变量来排除 author.middle 字段,只有当它等于 "" 时:

  1. db.books.aggregate( [
  2. {
  3. $project: {
  4. title: 1,
  5. "author.first": 1,
  6. "author.last": 1,
  7. "author.middle": {
  8. $cond: {
  9. if: { $eq: [ "", "$author.middle" ] },
  10. then: "$$REMOVE",
  11. else: "$author.middle"
  12. }
  13. }
  14. }
  15. }
  16. ] )

该 aggregation operation 的结果输出如下:

  1. { "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }
  2. { "_id" : 2, "title" : "Baked Goods", "author" : { "last" : "xyz", "first" : "abc" } }
  3. { "_id" : 3, "title" : "Ice Cream Cakes", "author" : { "last" : "xyz", "first" : "abc", "middle" : "mmm" } }

Include Specific Fields from Embedded Documents 从嵌入式文档中包含指定指端

考虑一个包含以下文档的 bookmarks collection:

  1. { _id: 1, user: "1234", stop: { title: "book1", author: "xyz", page: 32 } }
  2. { _id: 2, user: "7890", stop: [ { title: "book2", author: "abc", page: 5 }, { title: "book3", author: "ijk", page: 100 } ] }

要在 stop 字段中只包括嵌入文档的 title 字段,你可以使用点符号:

  1. db.bookmarks.aggregate( [ { $project: { "stop.title": 1 } } ] )

或者,你可以将包含规范嵌套在一个文档中:

  1. db.bookmarks.aggregate( [
  2. {
  3. $project: {
  4. stop: { title: 1 }
  5. }
  6. }
  7. ] )

这两种规范的结果都是以下文档:

  1. { "_id" : 1, "stop" : { "title" : "book1" } }
  2. { "_id" : 2, "stop" : [ { "title" : "book2" }, { "title" : "book3" } ] }

Include Computed Fields 包含计算字段

考虑一个包含以下文档的 books colleciton:

  1. {
  2. "_id" : 1,
  3. title: "abc123",
  4. isbn: "0001122223334",
  5. author: { last: "zzz", first: "aaa" },
  6. copies: 5
  7. }

下面的 $project stage 添加了新的字段 isbnlastNamecopiesSold

  1. db.books.aggregate( [
  2. {
  3. $project: {
  4. title: 1,
  5. isbn: {
  6. prefix: { $substr: [ "$isbn", 0, 3 ] },
  7. group: { $substr: [ "$isbn", 3, 2 ] },
  8. publisher: { $substr: [ "$isbn", 5, 4 ] },
  9. title: { $substr: [ "$isbn", 9, 3 ] },
  10. checkDigit: { $substr: [ "$isbn", 12, 1] }
  11. },
  12. lastName: "$author.last",
  13. copiesSold: "$copies"
  14. }
  15. }
  16. ] )

该 operation 结果文档如下:

  1. {
  2. "_id" : 1,
  3. "title" : "abc123",
  4. "isbn" : {
  5. "prefix" : "000",
  6. "group" : "11",
  7. "publisher" : "2222",
  8. "title" : "333",
  9. "checkDigit" : "4"
  10. },
  11. "lastName" : "zzz",
  12. "copiesSold" : 5
  13. }

Project New Array Fields Project 新的数组字段

例如,如果一个 collection 包含以下文档:

  1. { "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }

以下 operation 将字段 xy project 到一个新字段 myArray 中的元素:

  1. db.collection.aggregate( [
  2. {
  3. $project: {
  4. myArray: [ "$x", "$y" ]
  5. }
  6. }
  7. ] )

该 operation 返回以下文档:

  1. { "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1 ] }

如果数组规范包含在文档中不存在的字段,则 operation 将 null 作为该字段的值。

例如,给定上述相同的文档,下面的操作将字段 xy 和一个不存在的字段 $someField project 为一个新字段 myArray 中的元素:

  1. db.collection.aggregate( [
  2. {
  3. $project: {
  4. myArray: [ "$x", "$y", "$someField" ]
  5. }
  6. }
  7. ] )

该 operation 返回以下文档:

  1. { "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1, null ] }

Array Indexes are Unspported

你不能在 $project stage 使用一个数组索引。本节展示了一个例子:

创建以下 pizzas collection:

  1. db.pizzas.insert( [
  2. { _id: 0, name: [ 'Pepperoni' ] },
  3. ] )

以下示例返回 pizza:

  1. db.pizzas.aggregate( [
  2. { $project: { x: '$name', _id: 0 } },
  3. ] )

pizza 在示例输出中返回:

  1. [ { x: [ 'Pepperoni' ] } ]

下面的例子使用一个数组索引($name.0)来尝试返回 pizza:

  1. db.pizzas.aggregate( [
  2. { $project: { x: '$name.0', _id: 0 } },
  3. ] )

示例输出中未返回 pizza:

  1. [ { x: [] } ]

TIP 参阅:

参考

https://docs.mongodb.com/manual/reference/operator/aggregation/project