Docs 菜单
Docs 主页
/
MongoDB Manual
/ / /

$project(聚合)

在此页面上

  • 定义
  • 兼容性
  • 语法
  • 行为
  • Considerations
  • 示例
$project

将带所请求字段的文档传递至管道中的下个阶段。指定的字段可以是输入文档中的已有字段或新计算的字段。

可以使用 $project 查找托管在以下环境中的部署:

  • MongoDB Atlas:用于云中 MongoDB 部署的完全托管服务

  • MongoDB Enterprise:基于订阅、自我管理的 MongoDB 版本

  • MongoDB Community:源代码可用、免费使用且可自行管理的 MongoDB 版本

$project 阶段具有以下原型形式:

{ $project: { <specification(s)> } }

$project 采用的文档可以指定包含字段、抑制 _id 字段、添加新字段以及重置现有字段的值。您也可以指定排除字段。

$project 规范有以下几种形式:

形式
说明
<field>: <1 or true>
指定包含字段。非零整数也被视为 true
_id: <0 or false>

指定抑制 _id 字段。

要有条件地排除字段,请使用 REMOVE 变量。有关详细信息,请参阅有条件地排除字段

<field>: <expression>

添加新字段或重置现有字段的值。

如果表达式的计算结果为 $$REMOVE,则该字段在输出中将被排除。有关详细信息,请参阅有条件地排除字段

<field>: <0 or false>

指定排除某个字段。

要有条件地排除字段,请使用 REMOVE 变量。有关详细信息,请参阅有条件地排除字段

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

另请参阅 $unset 阶段,以排除字段。

  • 默认情况下,_id 字段包含在输出文档中。要在输出文档中包含输入文档中的任何其他字段,必须在 $project 中明确指定包含该字段。

  • 如果您指定包含的字段在文档中并不存在,那么 $project 将忽略该字段包含,同时不会将该字段添加到文档中。

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

如果指定排除一个或多个字段,则所有其他字段均为在输出文档中返回。

{ $project: { "<field1>": 0, "<field2>": 0, ... } } // Return all but the specified fields

如果指定排除 _id 之外的字段,那么您不能使用任何其他 $project 规范形式:即,如果排除字段,那么您不能同时指定包含字段、重置现有字段的值或添加新字段。此限制不适用于使用 REMOVE 变量有条件地排除字段。

另请参阅 $unset 阶段,以排除字段。

您可以在聚合表达式中使用变量 REMOVE 来有条件地抑制字段。有关示例,请参阅有条件地排除字段

注意

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

要添加新字段或重置现有字段的值,请指定字段名称并将其值设置为某个表达式。 有关表达式的更多信息,请参阅表达式。

要将字段值直接设置为数值或布尔文字,而不是将该字段设置为可解析为文字的表达式,请使用 $literal 操作符。否则,$project 将数字或布尔文字视为包含或排除该字段的标志。

有效地重命名字段的方式:指定新字段并将其值设置为现有字段的字段路径。

$project 阶段支持使用方括号 [] 直接创建新的数组字段。如果指定文档中不存在的数组字段,此操作将替换 null 作为该字段的值。有关示例,请参阅投影新的数组字段

$project 阶段不能使用数组索引。有关详细信息,请参阅不支持数组索引。

在嵌入式文档中投影或添加/重置字段时,可以使用点符号,如

"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 阶段失败并出现 Path collision 错误,原因是它试图投影嵌入的 contact 文档和 contact.address.country 字段:

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

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

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

当您使用 $project 阶段时,它通常应该是管道中的最后一个阶段,用于指定要返回给客户端的字段。

在管道的开头或中间使用 $project 阶段来减少传递到后续管道阶段的字段数量不太可能提高性能,因为数据库会自动执行此优化。

如果 $project 阶段传递的是空文档,MongoDB 将返回错误。

例如,运行以下管道会产生错误:

db.myCollection.aggregate( [ {
$project: { }
} ] )

$project 阶段不能使用数组索引。有关详细信息,请参阅不支持数组索引。

使用以下文档创建 books 集合:

{
"_id" : 1,
title: "abc123",
isbn: "0001122223334",
author: { last: "zzz", first: "aaa" },
copies: 5
}

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

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

该操作生成以下文档:

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

默认情况下始终包含 _id 字段。如要从 $project 阶段的输出文档中排除 _id 字段,请在投影文档中将 _id 字段设置为 0,以指定排除该字段。

使用以下文档创建 books 集合:

{
"_id" : 1,
title: "abc123",
isbn: "0001122223334",
author: { last: "zzz", first: "aaa" },
copies: 5
}

以下 $project 阶段在其输出文档中排除 _id 字段,但包括 titleauthor 字段:

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.firstlastModified 字段:

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"
}

只有当 author.middle 字段等于 "" 时,以下 $project 阶段才使用 REMOVE 变量将该字段排除:

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 的示例,请参阅删除字段

请考虑包含以下文档的 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 阶段添加新字段 isbnlastNamecopiesSold

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 }

以下操作将字段 xy 作为元素投影到新字段 myArray 中:

db.collection.aggregate( [ { $project: { myArray: [ "$x", "$y" ] } } ] )

该操作将返回以下文档:

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

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

例如,给定与上面相同的文档,以下操作将字段 xy 和不存在的字段 $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' ] },
] )

以下示例返回披萨 (pizza):

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

在示例输出中返回比萨:

[ { x: [ 'Pepperoni' ] } ]

以下示例使用数组索引 ($name.0) 以尝试返回披萨:

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

在示例输出中不会返回比萨:

[ { x: [] } ]

后退

$planCacheStats