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

Ste*_*ynx 13 projection mongodb

这是我的目标:

{ "_id" : ObjectId("53fdcb6796cb9b9aa86f05b9"), "list" : [ "a", "b" ], "complist" : [ { "a" : "a", "b" : "b" }, { "a" : "c", "b" : "d" } ] }
Run Code Online (Sandbox Code Playgroud)

这就是我想要完成的事情:检查"list"是否包含某个元素,并在读取文档时从"complist"上的对象中仅获取字段"a",而不管这些值是什么.我正在构建一个论坛系统,这是将返回论坛详细信息的查询.我需要在知道用户是否在论坛的白名单中时阅读论坛信息.

有了查找,我可以使用查询

db.itens.find({},{list:{$elemMatch:{$in:["a"]}}})
Run Code Online (Sandbox Code Playgroud)

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

使用聚合我可以使用查询

db.itens.aggregate({$project:{"complist.a":1}})
Run Code Online (Sandbox Code Playgroud)

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

但是当我尝试使用查询时

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

尝试做两件事,它会抛出一个错误,说操作符$ elemMatch无效.

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

Tom*_*ert 13

很老的问题,但实际上没有一个建议的答案是好的。


TLDR :

您不能在$project阶段使用$elemMatch。但是您可以使用其他聚合运算符(如$filter )获得相同的结果。

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

如果您只想要数组中与$elemMatch类似的条件匹配的第一项,您可以合并$arrayElemAt


深入解释

首先让我们了解$elemMatch

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

所以呢?这有什么关系?好吧,$project阶段具有特定的输入结构,而我们要使用的输入结构是:

<字段>:<表达式>

什么是有效表达式

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

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


小智 10

这个问题的答案可能会有所帮助。

db.collection_name.aggregate({
    "$match": {
        "complist": {
            "$elemMatch": {
                "a": "a"
            }
        }
    }
});
Run Code Online (Sandbox Code Playgroud)


Gra*_*yle 9

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


Gra*_*yle 6

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

  • 听起来不错-但您要表达的意思很好。 (3认同)

Ste*_*ynx -19

好吧,碰巧你可以在查找的投影块上使用“array.field”。

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

做了我需要的。

  • 问题是关于聚合,而不是查找 (9认同)