MongoDb $elemMatch 与 $lookup 变量

Posted

技术标签:

【中文标题】MongoDb $elemMatch 与 $lookup 变量【英文标题】:MongoDb $elemMatch with $lookup variable 【发布时间】:2021-03-24 11:17:17 【问题描述】:

我有一个给定的 MongoDb 集合,其中包含与此类似的文档:


items: [
   id: 1, data: 'a' ,
   id: 2, data: 'b' ,
   id: 3, data: 'c' ,
]

现在我必须从这个集合中$lookup。我需要来自 id = 2 元素的数组数据,其中 id = 1 数据与查找值 $$value 匹配。我第一次天真地尝试找到正确的文档如下所示:

$match 
  items:  $elemMatch: 
    id: 1,
    data: '$$value'
   

但是 $$value 没有被评估,所以数据与文字而不是它的值进行比较。我还尝试使用$expr 评估该值,但我无法获得正确的语法(如果可能的话?)。

我能够开始工作的唯一工作方式是先提取数据,然后进行匹配:

[
    
        $project: 
            _id: 0,
            data1: 
                $filter: 
                    input: '$items', as: 'item',
                    cond: $eq: ['$$item.id', 1]
                
            ,
            data2: 
                $filter: 
                    input: '$items', as: 'item',
                    cond: $eq: ['$$item.id', 2]
                
            ,
        
    ,
    
        $set: 
            data1: $arrayElemAt: ['$data1.data', 0],
            data2: $arrayElemAt: ['$data2.data', 0]
        
    ,
    
        $match: 
            $expr: $eq: ['$data1', '$$value']
        
    
];

但正如人们所预料的那样,这种方法要慢得多。对于涉及的数据,查询需要 7 秒,而上层方法(使用常量而不是变量 $$value)快 3 倍以上。

是否可以在$elemMatch 运算符中直接使用变量$$value?或者是否有任何其他优化可用于加速集合查找?

【问题讨论】:

【参考方案1】:

首先让我们了解一下为什么您的幼稚方法会失败,Mongo 的 pipelined lookup 文档指出:

$match 阶段需要使用 $expr 运算符来访问变量。 $expr 允许在 $match 语法中使用聚合表达式。

因此,您在 $lookup 开头定义的变量 value 只能通过使用 $expr 访问,现在也在 $expr 文档中指定:

参数可以是任何有效的聚合表达式

遗憾的是,$elemMatch 不是“聚合表达式”,因为它属于“查询语言”。这就是您的第一种方法失败的原因。只是不允许在 $expr 中使用 $elemMatch,这是访问 $$value 字段所必需的。

那么我们能做些什么呢?好吧,你可以像你已经开始做的那样使用$filter:

db.collection.aggregate([
  
    $lookup: 
      from: "collection2",
      let: 
        value: "$data"
      ,
      pipeline: [
        
          $match: 
            $expr: 
              $gt: [
                
                  $size: 
                    $filter: 
                      input: "$items",
                      as: "item",
                      cond: 
                        $and: [
                          
                            $eq: [
                              "$$item.id",
                              1
                            ]
                          ,
                          
                            $eq: [
                              "$$item.data",
                              "$$value"
                            ]
                          
                        ]
                      
                    
                  
                ,
                0
              ],
              
            
          
        
      ],
      as: "res"
    
  
])

如果您提供两个集合的完整结构和所需的最终结果,则更容易完整回答,因为我必须在这里猜测一些内容才能编写此管道。

Mongo Playground

【讨论】:

感谢您的详细解释。您的演示结构足以满足我的需求。如果可以的话,我会再为 Mongo Playground 链接 +1!不幸的是,我根据您的解决方案更新的查询比我的第一种方法慢... 没问题,常量值总是最有效的,因为它不需要像$filter 这样的任何结构操作用法。

以上是关于MongoDb $elemMatch 与 $lookup 变量的主要内容,如果未能解决你的问题,请参考以下文章

mongodb 中的Multikey Index Bounds解释$elemMatch

带有 elemMatch 的 MongoDB 查询,用于从对象内部匹配嵌套数组数据 [重复]

MongoDb:$elemMatch,将字符串转换为数字

MongoDB:$ elemMatch和$ and和在数组内查找对象之间有什么区别?

使用Java中的$ elemMatch实现Mongodb查询

mongodb 查询子节点