【问题标题】:How to use $elemMatch on aggregate's projection?如何在聚合的投影上使用 $elemMatch?
【发布时间】:2014-10-21 02:25:15
【问题描述】:

这是我的对象:

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

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

通过查找我可以使用查询

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

只获取匹配某个值的第一个元素。这样我就可以检查返回的数组是否不为空,并且我知道“list”是否包含我正在寻找的值。我无法在查询中执行此操作,因为无论文档是否包含我在“列表”值中查找的值,我都想要该文档。我需要文档并且知道“列表”是否具有一定的价值。

通过聚合我可以使用查询

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

只读取 complist 中包含的对象的字段“a”。这是要获取论坛的话题基本信息,我不想要所有的话题信息,只需要几件事。

但是当我尝试使用查询时

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

尝试两者都做,它会抛出一个错误,说运算符 $elemMatch 无效。

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

【问题讨论】:

    标签: mongodb projection


    【解决方案1】:

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


    TLDR

    您不能在$project 阶段中​​使用$elemMatch。但是您可以使用其他聚合运算符(例如 $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 阶段的有效表达式。

    【讨论】:

    • Invalid $project :: caused by :: FieldPath field names may not start with '$'.
    • 我只是忘了添加字段名称。我编辑了它
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • 你是在使用 mongoosejs 访问 mongo db 吗?
      • 这行得通,它第一次不起作用,因为如果有像“complist.sub.path”这样的路径应该删除“complist”,这样它就只有“sub.path”下面。
      • 我没有找到在这个方法中获取数组索引的方法,所以如果你想稍后在聚合中进行额外的操作,那么你最好选择完整的 $unwind 方式,或像接受的答案一样使用 $project。
      【解决方案4】:

      实际上,最简单的解决方案是 $unwind 您的数组,然后 $match 适当的文档。您可以使用 $group 和 $push 再次结束相应的文档。

      【讨论】:

        【解决方案5】:

        虽然这个问题很老,但这是我 2017 年 11 月的贡献。

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

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

        【讨论】:

          【解决方案6】:

          对于聚合只需使用$expr:

          db.items.aggregate([
              {
                  "$match": {
                     "$expr": {"$in": ["a", "$list"]}
                  }
              },
          ])
          

          【讨论】:

            【解决方案7】:

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

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

            做了我需要的。

            【讨论】:

            • 问题是关于聚合,没有找到
            • 问题是关于聚合而不是查找。
            猜你喜欢
            • 2015-05-26
            • 2014-07-04
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2022-01-14
            • 1970-01-01
            • 1970-01-01
            • 2022-01-18
            相关资源
            最近更新 更多