$project
聚合阶段可将输入文档根据请求的字段输出到管道的下个阶段,输出的字段可以是输入文档中的字段,也可以是新的计算字段。
{ $project: { <specification(s)> } }
$project
聚合接受一个文档参数,可以指定包含的字段,抑制_id
字段,添加新的字段以及重置已有字段的值等。亦或者,也可以指定排除的字段。
$project
的参数字段可以是下面的形式:
参数字段形式 | 描述 |
---|---|
<字段>:<1或true> |
指定包含的字段,非0整数也被视为true |
_id:<0或false> |
指定抑制_id 字段,也就是_id 字段不会被传递到下个阶段。也可以使用REMOVE 变量和条件排除字段 |
<字段>:<表达式> |
添加一个新字段或者重置已有字段的值。如果表达式的值为$$REMOVE ,则字段被排除 |
<字段>:<0或false> |
排除一个指定的字段。可以使用REMOVE 变量和条件排除字段,如果排除_id 以外的字段,则不能同时使用其他形式,但是REMOVE 变量条件排除字段除外 |
_id
字段默认输出,如果要包含其他字段,必须明确的指定。$project
将忽略该字段,输出文档中也不会包含该字段。_id
字段缺省情况下输出文档包含_id
字段,如果不想让_id
字段出现在输出文档中必须显示的指定抑制_id
字段。
如果指定了排除字段,没有被指定的字段默认输出到下个阶段。
{ $project: { "" : 0, "" : 0, ... } } // 将返回所有没有被指定的字段
如果指定了排除_id
字段以外的其他字段,则不能再使用其他形式来指定字段,也就是说这种情况下不能再指定包含其他字段,也不能指定新的字段或重置已有字段。但可以使用REMOVE
变量条件排除字段。
可以使用REMOVE
变量根据表达式条件抑制一个字段。
**注意:**也可以使用$addFields
来为文档添加字段。
要添加一个字段或重置已有字段的值,可以指定字段名并使用表达式指定值
要将字段值直接设置为数字或布尔字面量,而不是将字段设置为解析为字面量的表达式,可使用$literal
操作符。否则,$project
将把数字或布尔字面量视为包含或排除字段的标志。
通过指定一个新字段并将其值设置为现有字段的字段路径,可以重命名一个字段。
$project
阶段支持使用方括号[]
直接创建新的数组字段,如果指定的数组字段在文档中不存在,则操作会将空值替换为该字段的值。
当重塑、添加或重置内嵌文档的字段时,可以使用点号或嵌套字段:
点号形式:
"contact.address.country": <1 or 0 or 表达式>
嵌套字段形式:
contact: { address: { country: <1 or 0 or 表达式> } }
当使用嵌套字段时,不能在嵌套文档内部使用点号指定字段,如下面的就是错误的方式:
contact: { "address.country": <1 or 0 or 表达式> }
不能在同一投影中同时指定嵌入文档和嵌入文档中的字段。下面的$project
阶段有路径冲突错误,因为试图同时重塑内嵌文档contact
和contact.address.country
字段:
{ $project: { contact: 1, "contact.address.country": 1 } }
这种错误与父文档和内嵌字段的顺序无关,下面的$project
是同样的错误:
{ $project: { "contact.address.country": 1, contact: 1 } }
$project
阶段的位置当使用$project
截断式,通常情况下它应该放在管道的最后阶段,用它来指定返回给客户端的字段。
没有必要为了减少传递到后续管道的字段数量而把$project
阶段放在管道的开始或中间,这也不太可能提高性能,数据库会自动执行这种优化。
$project
制定一个空文档会返回错误。$project
阶段中不能使用数组下标。有一个book
集合包含下面的文档:
{
"_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
字段,如果要在输出文档中排除_id
字段,可以在参数中给_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"
}
下面的$peoject
阶段排除掉了lastModified
字段:
db.books.aggregate( [ { $project : { "lastModified": 0 } } ] )
结果输出:
"_id" : 1,
"title": "abc123",
"isbn": "0001122223334",
"author": { "last": "zzz", "first": "aaa" },
"copies": 5
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,
}
可以在聚合表达式中使用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"
}
下面的$proejct
阶段使用RREMOVE
变量排除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" } }
bookmarks
有下面的文档:
{ "_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
的title
字段,可以使用点号或嵌套字段来指定:
用点号指定:
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
、last
和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
}
一个coll
集合有下面的文档:
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "x" : 1, "y" : 1 }
下面的操作将字段x
、y
重塑为myArray
字段的数组元素:
db.coll.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )
操作返回下面的结果:
{ "_id" : ObjectId("55ad167f320c6be244eb3b95"), "myArray" : [ 1, 1 ] }
不能在$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": [] } ]