基于其他集合数组中的匹配对象属性执行$ lookup

时间:2018-04-26 16:54:31

标签: mongodb aggregation-framework

我正在尝试使用条件对集合执行$lookup,我面临的问题是我希望匹配数组内所有对象的text字段({{1} } array)在其他(accounts)集合中。 我尝试过使用plates以及$map$in,但似乎没有任何效果。而且,我无法找到匹配数组中每个对象的文本字段的方法。

我的文档结构如下:

$setIntersection收藏:

plates

{ "_id": "Batch 1", "rego" : "1QX-WA-123", "date" : 1516374000000.0 "accounts": [{ "text": "Acc1", "date": 1516374000000 },{ "text": "Acc2", "date": 1516474000000 }] } 收藏:

accounts

我正在努力实现这样的目标:

{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0
}

我想要的输出是:

{
    $lookup: { 
        from: 'plates',
        let: { 'accountId': '$_id' },
        pipeline: [{
            '$match': {
                '$expr': { '$and': [ 
                    { '$eq': [ '$account.text', '$$accountId' ] }, 
                    { '$gte': [ '$date', ISODate ("2016-01-01T00:00:00.000Z").getTime() ] },
                    { '$lte': [ '$date', ISODate ("2019-01-01T00:00:00.000Z").getTime() ] }
                ]}
            }
        }],
        as: 'cusips' 
    }
},

2 个答案:

答案 0 :(得分:0)

嗯,不确定你是怎么尝试$in的,但它对我有用:

{
    $lookup: { 
        from: 'plates',
        let: { 'accountId': '$_id' },
        pipeline: [{
            '$match': {
                '$expr':  { '$and': [ 
                    { '$in': [ '$$accountId',  '$accounts.text'] },
                    { '$gte': [ '$date', ISODate ("2016-01-01T00:00:00.000Z").getTime() ] },
                    { '$lte': [ '$date', ISODate ("2019-01-01T00:00:00.000Z").getTime() ] }
                ]}
            }, 
        }],
        as: 'cusips' 
    } 
}

答案 1 :(得分:0)

就我个人而言,我会从"plates"集合中启动聚合,而不是初始$match条件可以更干净地过滤日期范围。获得所需的输出是一个简单的事情,即“展开”生成的"accounts"匹配和“反转”内容。

使用MongoDB 3.6功能非常简单,您必须拥有这些功能才能将$lookup$expr一起使用。我们甚至不需要$lookup的表格:

db.plates.aggregate([
  { "$match": {
    "date": { 
      "$gte": new Date("2016-01-01").getTime(),
      "$lte": new Date("2019-01-01").getTime()
    }
  }},
  { "$lookup": {
    "from": "accounts",
    "localField": "accounts.text",
    "foreignField": "_id",
    "as": "accounts"
  }},
  { "$unwind": "$accounts" },
  { "$group": {
    "_id": "$accounts",
    "plates": { "$push": { "_id": "$_id", "rego": "$rego" } }
  }},
  { "$replaceRoot": {
    "newRoot": {
      "$mergeObjects": ["$_id", { "plates": "$plates" }]
    }
  }}
])

这当然是一个“INNER JOIN”,它只会返回matc所在的"accounts"条目

"accounts"集合执行“加入”意味着您需要额外处理以从"accounts"集合中的"plates"数组中删除不匹配的条目:

db.accounts.aggregate([
  { "$lookup": {
    "from": "plates",
    "let": { "account": "$_id" },
    "pipeline": [
      { "$match": {
        "date": { 
          "$gte": new Date("2016-01-01").getTime(),
          "$lte": new Date("2019-01-01").getTime()
        },
        "$expr": { "$in": [ "$$account", "$accounts.text" ] }
      }},
      { "$project": { "_id": 1, "rego": 1 } }
    ],
    "as": "plates"
  }}
])

请注意,"date"属性上的$match应表示为常规查询条件,而不是$expr块中的$in,以获得最佳的查询效果。

$in用于将"$accounts.text"值的“数组”与为要加入的"_id"文档的"accounts"值定义的局部变量进行比较。因此,{{3}}的第一个参数是“单个”值,第二个参数是仅应匹配的"text"值的“数组”。

这也是一个“LEFT JOIN”,它返回所有"accounts",无论条件是否匹配"plates",因此你可能最终得到一个空"plates"返回结果中的数组。如果你不想要它们,你可以过滤掉那些,但是在那种情况下,以前的查询形式实际上比这个更有效,因为定义了关系,我们只处理会遇到的"plates"标准。

任何一种方法都会从问题中提供的数据中返回相同的响应:

{
        "_id" : "Acc1",
        "date" : 1516374000000,
        "createdAt" : 1513810712802,
        "plates" : [
                {
                        "_id" : "Batch 1",
                        "rego" : "1QX-WA-123"
                }
        ]
}

您实际采用的方向实际上取决于“LEFT”或“INNER”连接形式是否是您真正想要的,以及可以为您实际想要选择的项目提供最有效的查询条件。< / p>