如何在聚合投影上使用$ elemMatch?

时间:2014-08-27 13:16:35

标签: mongodb projection

这是我的目标:

{ "_id" : ObjectId("53fdcb6796cb9b9aa86f05b9"), "list" : [ "a", "b" ], "complist" : [ { "a" : "a", "b" : "b" }, { "a" : "c", "b" : "d" } ] }

这就是我想要完成的事情:检查" list"包含一个元素,只获得字段" a"来自" complist"上的对象在阅读文档时,无论这些值如何。我正在构建一个论坛系统,这是一个返回论坛详细信息的查询。我需要在知道用户是否在论坛的白名单中时阅读论坛信息。

使用查找我可以使用查询

db.itens.find({},{list:{$elemMatch:{$in:["a"]}}})

仅获取与特定值匹配的第一个元素。这样我就可以检查返回的数组是否为空,我知道是否" list"包含我正在寻找的价值。我无法在查询中执行此操作,因为我想要文档,无论它包含我在" list"中查找的值。值。我需要文件并知道" list"有一定的价值。

使用聚合我可以使用查询

db.itens.aggregate({$project:{"complist.a":1}})

只读字段" a" complist中包含的对象。这将获得论坛的主题基本信息,我不想要线程的所有信息,只是一些事情。

但是当我尝试使用查询时

db.itens.aggregate({$project:{"complist.b":1,list:{$elemMatch:{$in:["a"]}}}})

尝试同时执行这两项操作,它会向我抛出一个错误,指出运算符$ elemMatch无效。

我在这里用$ elemMatch做错了吗?有没有更好的方法来实现这一目标?

7 个答案:

答案 0 :(得分:7)

出于某种原因,$ elemMatch在聚合中不起作用。您需要在Mongo 3.2中使用新的$ filter运算符。见https://docs.mongodb.org/manual/reference/operator/aggregation/filter/

答案 1 :(得分:6)

实际上,最简单的解决方案是只需$展开你的数组,然后$匹配相应的文档。您可以使用$ group和$ push再次关闭相应的文档。

答案 2 :(得分:3)

this question的答案可能会有所帮助。

db.collection_name.aggregate({
    "$match": {
        "complist": {
            "$elemMatch": {
                "a": "a"
            }
        }
    }
});

答案 3 :(得分:3)

很老的问题,但是从字面上看,没有一个好的答案。


TLDR

您不能在$elemMatch阶段使用$project。但是您可以使用$filter之类的其他聚合运算符来达到相同的结果。

db.itens.aggregate([
    {
        $project: {
            compList: {
               $filter: {
                input: "$complist",
                as: "item",
                cond: {$eq: ["$$item.a", 1]}
               }
            }
        }
    }
])

如果只希望数组中与条件$elemMatch类似的第一个项目,则可以合并$arrayElemAt


深入解释

首先让我们了解$elemMatch

$elemMatch是一个查询表达式,同时它的这个projection版本也存在,它是指查询投影而不是$project聚合阶段。

那又怎样?这和什么有什么关系? $project阶段具有某些输入结构,而我们要使用的输入结构是:

什么是有效的expression

表达式可以包括字段路径,文字,系统变量,表达式对象和表达式运算符。表达式可以嵌套。

因此我们想使用expression operator,但是正如您从文档中看到的$elemMatch所不包含的。因此,它不是在聚合$project阶段使用的有效表达式。

答案 4 :(得分:1)

虽然问题已经过去了,但这是我2017年11月的贡献。

我有类似的问题,并且连续两次匹配操作对我有效。下面的代码是我整个代码的一个子集,我更改了元素名称,所以它没有经过测试。无论如何,这应该指向正确的方向。

db.collection.aggregate([
    {
        "$match": {
            "_id": "ID1"
        }
    },
    {
        "$unwind": "$sub_collection"
    },
    {
        "$match": {
            "sub_collection.field_I_want_to_match": "value"
        }
    }
])

答案 5 :(得分:-3)

您可以将$eq运算符与$ elemMatch结合使用

db.items.find({}, {
  "complist.b": 1,
  "list": { $elemMatch: { $eq: "a" } }
})

答案 6 :(得分:-8)

好吧,你可以使用" array.field"在找到的投影块上。

 db.itens.find({},{"complist.b":1,list:{$elemMatch:{$in:["a"]}}})

做了我需要的。