$project(聚合)
定义
兼容性
您可以使用 $project
对以下环境中的托管部署进行操作
MongoDB Atlas:云中MongoDB部署的全托管服务
MongoDB Enterprise:基于订阅、自我管理的MongoDB版本
MongoDB Community:源代码可用的、免费使用且可自我管理的MongoDB版本
语法
$project
阶段具有以下原型形式
{ $project: { <specification(s)> } }
$project
接收一个文档,可以指定字段的包含、_id
字段的抑制、新字段的添加和现有字段值的重置。或者,您也可以指定字段的排除。
$project
规范具有以下形式
形式 | 描述 |
---|---|
<field>: <1 或 true> | 指定字段的包含。非零整数也被视为 true 。 |
_id: <0 或 false> | |
<field>: <expression> | 添加新字段或重置现有字段的值。 如果表达式计算结果为 |
<field>: <0 或 false> |
行为
包含字段
_id
字段
默认情况下,_id
字段包含在输出文档中。要排除输出文档中的 _id
字段,必须在 $project
. 中显式指定排除 _id
字段。
排除字段
如果您指定排除某个或某些字段,则所有其他字段都将返回到输出文档中。
{ $project: { "<field1>": 0, "<field2>": 0, ... } } // Return all but the specified fields
如果您指定排除除 _id
之外的字段,则不能使用任何其他 $project
规范形式:即如果排除字段,则不能同时指定包含字段、重置现有字段的值或添加新字段。此限制不适用于使用 REMOVE
变量条件排除字段的情况。
请参阅$unset
阶段以排除字段。
条件排除字段
添加新字段或重置现有字段
注意
MongoDB 还提供了 $addFields
操作符来向文档中添加新字段。
要添加新字段或重置现有字段的值,指定字段名称并将其值设置为某个表达式。有关表达式更多信息,请参阅 表达式运算符。
文字值
要将字段值直接设置为数值或布尔文字值,而不是将字段设置为解析为文字值的表达式,请使用 $literal
操作符。否则,$project
将数值或布尔文字值视为包含或排除字段的标志。
字段重命名
通过指定一个新字段并设置其值为现有字段的字段路径,可以有效地重命名字段。
新数组字段
在$project
阶段,支持使用方括号[]
直接创建新的数组字段。如果您指定了文档中不存在的数组字段,则操作将使用null
作为该字段的值。例如,请参阅创建新数组字段。
嵌套文档字段
当在嵌套文档中投影或添加/重置字段时,您可以使用点表示法,如下所示
"contact.address.country": <1 or 0 or expression>
或者您可以将字段嵌套
contact: { address: { country: <1 or 0 or expression> } }
当嵌套字段时,您不能在嵌套文档内部使用点表示法来指定字段,例如 contact: { "address.country": <1 or 0 or expression> }
是 无效的。
嵌套字段中的路径冲突错误
您不能在同一投影中同时指定嵌套文档以及该嵌套文档中的字段。
以下$project
阶段因尝试投影嵌套的contact
文档和contact.address.country
字段而失败,并出现Path collision
错误。
{ $project: { contact: 1, "contact.address.country": 1 } }
无论父文档和嵌套字段指定的顺序如何,都会发生错误。以下$project
也会因同样的错误而失败。
{ $project: { "contact.address.country": 1, contact: 1 } }
$project
阶段放置
当使用 $project
阶段时,它通常应该是您管道中的最后一个阶段,用于指定要返回给客户端的字段。
在管道的开始或中间使用 $project
阶段以减少传递给后续管道阶段字段的数量,不太可能提高性能,因为数据库会自动执行此优化。
注意事项
空规范
MongoDB 如果 $project
阶段传入空文档,将返回错误。
例如,运行以下管道将产生错误
db.myCollection.aggregate( [ { $project: { } } ] )
数组索引
示例
在输出文档中包含特定字段
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
以下 $project
阶段仅在其输出文档中包含 _id
、title
和 author
字段
db.books.aggregate( [ { $project : { title : 1 , author : 1 } } ] )
该操作的结果如下文档
{ "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }
在输出文档中抑制 _id
字段
默认情况下,_id
字段总是被包含。为了在 $project
阶段的输出文档中排除 _id
字段,在投影文档中将它设置为 0
以指定排除。
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
以下 $project
阶段排除了 _id
字段,但包含输出文档中的 title
和 author
字段。
db.books.aggregate( [ { $project : { _id: 0, title : 1 , author : 1 } } ] )
该操作的结果如下文档
{ "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } }
从输出文档中排除字段
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" }
以下 $project
阶段从输出中排除了 lastModified
字段。
db.books.aggregate( [ { $project : { "lastModified": 0 } } ] )
请参阅$unset
阶段以排除字段。
从嵌入式文档中排除字段
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" }
以下$project
阶段从输出中排除了author.first
和lastModified
字段
db.books.aggregate( [ { $project : { "author.first" : 0, "lastModified" : 0 } } ] )
或者,您可以在文档中嵌套排除规范
db.bookmarks.aggregate( [ { $project: { "author": { "first": 0}, "lastModified" : 0 } } ] )
这两种规范会产生相同的输出
{ "_id" : 1, "title" : "abc123", "isbn" : "0001122223334", "author" : { "last" : "zzz" }, "copies" : 5, }
请参阅$unset
阶段以排除字段。
条件排除字段
您可以在聚合表达式中使用变量REMOVE
来条件性地抑制字段。
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5, lastModified: "2016-07-28" } { "_id" : 2, title: "Baked Goods", isbn: "9999999999999", author: { last: "xyz", first: "abc", middle: "" }, copies: 2, lastModified: "2017-07-21" } { "_id" : 3, title: "Ice Cream Cakes", isbn: "8888888888888", author: { last: "xyz", first: "abc", middle: "mmm" }, copies: 5, lastModified: "2017-07-22" }
以下$project
阶段使用REMOVE
变量仅在它等于""
时排除author.middle
字段
db.books.aggregate( [ { $project: { title: 1, "author.first": 1, "author.last" : 1, "author.middle": { $cond: { if: { $eq: [ "", "$author.middle" ] }, then: "$$REMOVE", else: "$author.middle" } } } } ] )
聚合操作的结果如下
{ "_id" : 1, "title" : "abc123", "author" : { "last" : "zzz", "first" : "aaa" } } { "_id" : 2, "title" : "Baked Goods", "author" : { "last" : "xyz", "first" : "abc" } } { "_id" : 3, "title" : "Ice Cream Cakes", "author" : { "last" : "xyz", "first" : "abc", "middle" : "mmm" } }
提示
与$addFields比较
您可以使用$addFields
或$project
阶段来删除文档字段。最佳方法取决于您的管道和您想要保留多少原始文档。
要查看在$addFields
阶段使用$$REMOVE
的示例,请参阅删除字段。
从嵌入文档中包含特定字段
考虑一个包含以下文档的 书签
集合
{ _id: 1, user: "1234", stop: { title: "book1", author: "xyz", page: 32 } } { _id: 2, user: "7890", stop: [ { title: "book2", author: "abc", page: 5 }, { title: "book3", author: "ijk", page: 100 } ] }
要仅将嵌入文档中的 标题
字段包含在 stop
字段中,您可以使用 点符号
db.bookmarks.aggregate( [ { $project: { "stop.title": 1 } } ] )
或者,您可以在文档中嵌套包含指定
db.bookmarks.aggregate( [ { $project: { stop: { title: 1 } } } ] )
这两种指定都产生以下文档
{ "_id" : 1, "stop" : { "title" : "book1" } } { "_id" : 2, "stop" : [ { "title" : "book2" }, { "title" : "book3" } ] }
包含计算字段
考虑一个包含以下文档的 books
集合
{ "_id" : 1, title: "abc123", isbn: "0001122223334", author: { last: "zzz", first: "aaa" }, copies: 5 }
以下 $project
阶段添加了新的字段 isbn
、lastName
和 copiesSold
db.books.aggregate( [ { $project: { title: 1, isbn: { prefix: { $substr: [ "$isbn", 0, 3 ] }, group: { $substr: [ "$isbn", 3, 2 ] }, publisher: { $substr: [ "$isbn", 5, 4 ] }, title: { $substr: [ "$isbn", 9, 3 ] }, checkDigit: { $substr: [ "$isbn", 12, 1] } }, lastName: "$author.last", copiesSold: "$copies" } } ] )
该操作的结果如下文档
{ "_id" : 1, "title" : "abc123", "isbn" : { "prefix" : "000", "group" : "11", "publisher" : "2222", "title" : "333", "checkDigit" : "4" }, "lastName" : "zzz", "copiesSold" : 5 }
投影新数组字段
例如,如果一个集合包含以下文档
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }
以下操作将字段 x
和 y
投影为新字段 myArray
的元素
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )
操作返回以下文档
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1 ] }
如果数组指定中包含文档中不存在的字段,则将 null
作为该字段的值进行替换。
例如,给定上述相同的文档,以下操作将字段 x
、y
和一个不存在的字段 $someField
投影为新字段 myArray
的元素
db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y", "$someField" ] } } ] )
操作返回以下文档
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1, null ] }
数组索引不受支持
在$project
阶段,您不能使用数组索引。本节将展示一个示例。
创建以下pizzas
集合
db.pizzas.insert( [ { _id: 0, name: [ 'Pepperoni' ] }, ] )
以下示例返回披萨
db.pizzas.aggregate( [ { $project: { x: '$name', _id: 0 } }, ] )
示例输出中返回了披萨
[ { x: [ 'Pepperoni' ] } ]
以下示例使用数组索引($name.0
)尝试返回披萨
db.pizzas.aggregate( [ { $project: { x: '$name.0', _id: 0 } }, ] )
示例输出中没有返回披萨
[ { x: [] } ]