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

Mongodb 基于其他集合数组中匹配的对象属性执行$lookup,mongodb,aggregation-framework,Mongodb,Aggregation Framework,我正在尝试使用条件对集合执行$lookup,我面临的问题是,我希望匹配其他集合中数组accounts数组中所有对象的文本字段。 我尝试过使用$map以及$in和$setIntersection,但似乎没有任何效果。而且,我无法找到匹配数组中每个对象的文本字段的方法 我的文件结构如下: 车牌收集: { "_id": "Batch 1", "rego" : "1QX-WA-123", "date" : 1516374000000.0 "accounts": [{

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

我的文件结构如下:

车牌收集:

{
    "_id": "Batch 1",
    "rego" : "1QX-WA-123",
    "date" : 1516374000000.0
    "accounts": [{
        "text": "Acc1",
        "date": 1516374000000
    },{
        "text": "Acc2",
        "date": 1516474000000
    }]
}
{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0
}
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"
  }}
])
收款:

{
    "_id": "Batch 1",
    "rego" : "1QX-WA-123",
    "date" : 1516374000000.0
    "accounts": [{
        "text": "Acc1",
        "date": 1516374000000
    },{
        "text": "Acc2",
        "date": 1516474000000
    }]
}
{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0
}
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"
  }}
])
我正在努力实现这样的目标:

{
    $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' 
    }
},
我试图获得的输出是:

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

嗯,不知道你是怎么尝试的,但对我来说很有效:

{
    $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' 
    } 
}

就我个人而言,我会从plates collection开始聚合,而不是初始条件可以更干净地过滤日期范围。然后,获得所需的输出只需解开结果帐户匹配项并反转内容即可

使用MongoDB 3.6功能非常简单,您必须具备这些功能才能与一起使用。在这里我们甚至不需要这个表格:

这当然是一个内部联接,它只返回matc

从accounts集合执行联接意味着您需要额外的处理来从plates集合中的accounts数组中删除不匹配的条目:

{
    "_id": "Batch 1",
    "rego" : "1QX-WA-123",
    "date" : 1516374000000.0
    "accounts": [{
        "text": "Acc1",
        "date": 1516374000000
    },{
        "text": "Acc2",
        "date": 1516474000000
    }]
}
{
    "_id": "Acc1",
    "date": 1516374000000
    "createdAt" : 1513810712802.0
}
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"
  }}
])
请注意,日期属性应表示为常规查询条件,而不是在$expr块中,以获得最佳查询性能

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

这也是一个值得注意的左连接,它返回所有帐户,而不管是否有任何匹配的条件图版,因此您可能会在返回的结果中得到一个空图版数组。如果你不想要的话,你可以过滤掉它们,但是在这种情况下,前一个查询表单确实比这个表单有效得多,因为关系已经定义,我们只处理符合条件的板块

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

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

您实际采用的方向取决于左侧或内部连接表单是否是您真正想要的,以及您真正想要选择的项目的最有效查询条件的位置。

除了$expr之外,$gte和$lte部分将更有效,因为它们实际上可以应用于指数它们是常量,毕竟不是字段比较。另外,FYI{$lookup:{from:plates,foriegnField:accounts.text,localField:{u id,as:cusips},{$unwind:$cusips},{$match:{cusips.date:{$gte:ISODate 2016-01-01T00:00:00.000Z.getTime,$lte:ISODate 2019-01-01T00:00:00.000Z.getTime}基本上与我刚才提到的更正完全相同。您需要查看解释输出以了解原因。它是解开的,但您可以始终使用$group。@Alex Blex$in对我不起作用。它说第二个参数应该是数组,但对象是supplied@Waqas,集合中存在帐户不是数组的文档。请注意,您的问题中是bidder.name,但我假设它应该是accounts.text,因为您提供的文件中没有投标人。除此之外,请遵循Neil的答案以获得更好的性能。我相信他也做出了同样的假设。@AlexBlex是的,我更新了这个问题,但是,'plates'集合中的帐户是一个文档数组,这很奇怪,但是你可能也真的想从其他方向启动此连接plates->accounts。原因是帐户详细信息位于plates集合中的一个数组中。因此,按此方向操作意味着您仍然可以从文档中获得与您要加入的帐户不匹配的帐户。没有更多的杂耍。根据您想要的结果,您可能希望通过另一种方式使选择更加简单。你为什么要修改你的问题?您提供的数据中的字段路径是$account.text,而不是$account.name。而且你已经被告知过好几次了,反正你的答案是错误的。@NeilLunn好吧,我更新了这个问题来纠正这个错误,不管怎样,我尝试了你的建议,似乎很有效。