MongoDB聚合框架:仅项目匹配数组的元素

时间:2014-12-12 15:16:22

标签: mongodb aggregation-framework

我有一个"班级"文件为:

{
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()的项目运算符中使用此数组。

3 个答案:

答案 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”的文档。