我有一个"班级"文件为:
{
className: "AAA",
students: [
{name:"An", age:"13"},
{name:"Hao", age:"13"},
{name:"John", age:"14"},
{name:"Hung", age:"12"}
]
}
我希望得到姓名为#34; An"的学生,只获得数组中的匹配元素"学生"。我可以用函数find()作为:
>db.class.find({"students.name":"An"}, {"students.$":true})
{
"_id" : ObjectId("548b01815a06570735b946c1"),
"students" : [
{
"name" : "An",
"age" : "13"
}
]}
很好,但是当我使用Aggregation执行相同操作时,会出现错误:
db.class.aggregate([
{$match:{"students.name":'An'}},
{$project:{"students.$":true}}
])
错误是:
uncaught exception: aggregate failed: {
"errmsg" : "exception: FieldPath field names may not start with '$'.",
"code" : 16410,
"ok" : 0
}
为什么呢?我无法使用" $"对于aggregate()的$ project运算符中的数组,可以在find()的项目运算符中使用此数组。
答案 0 :(得分:3)
来自docs:
在find()方法或findOne()的投影文档中使用$ 当您只需要选中一个特定数组元素时的方法 文档。
位置运算符$
不能用于聚合管道投影阶段。在那里不被承认。
这是有道理的,因为当您使用查找查询执行投影时,查询的投影部分的输入是与查询匹配的单个文档。即使在投影期间,匹配的上下文也是已知的。因此,对于与查询匹配的每个文档,然后在找到下一个匹配项之前应用投影运算符。
db.class.find({"students.name":"An"}, {"students.$":true})
如果:
db.class.aggregate([
{$match:{"students.name":'An'}},
{$project:{"students.$":true}}
])
aggregation管道是一组阶段。每个阶段都完全没有意识到,并且与之前或下一个阶段无关。一组文档在传递到管道中的下一个阶段之前完全传递一个阶段。在这种情况下,第一阶段是$match
阶段,所有文档都根据匹配条件进行过滤。投影阶段的输入现在是设置的文档,这些文档已作为匹配阶段的一部分进行过滤。
因此,投影阶段的位置操作符没有意义,因为在当前阶段,它不知道字段已被过滤的基础。因此,$
运算符不允许作为字段路径的一部分。
为什么以下工作?
db.class.aggregate([
{ $match: { "students.name": "An" },
{ $unwind: "$students" },
{ $project: { "students": 1 } }
])
如您所见,投影阶段将一组文档作为输入,并投影所需的字段。它独立于前一阶段和下一阶段。
答案 1 :(得分:1)
尝试在管道中使用展开运算符:http://docs.mongodb.org/manual/reference/operator/aggregation/unwind/#pipe._S_unwind
您的汇总看起来像
db.class.aggregate([
{ $match: { "students.name": "An" },
{ $unwind: "$students" },
{ $project: { "students": 1 } }
])
答案 2 :(得分:0)
您可以使用$ filter根据指定条件选择要返回的数组子集。
db.class.aggregate([
{
$match:{
"className: "AAA"
}
},
{
$project: {
$filter: {
input: "$students",
as: "stu",
cond: { $eq: [ "$$stu.name", "An" ] }
}
}
])
下面的示例将学生数组过滤为仅包含名称等于“ An”的文档。