根据字段查找两个对象数组之间的交集

时间:2018-04-25 05:01:40

标签: mongodb mongodb-query aggregation-framework

在mongo收藏中,我有以下结构的文件。

{
    "_id" : "Suzuki",
    "qty" : 10,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123",
        }, 
        {
            "_id" : "2Wqrd4yofvLmqLm5H",
            "createdAt" : 1513810712802.0,
            "date" : 1501632000000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-111",
        }
    ]
}

我正在尝试过滤accounts数组中的对象,使其仅包含rego数组中plates存在的对象。

我尝试了以下查询,但是,它会引发错误:all operands of $setIntersection must be arrays. One argument if of type object.

db.getCollection('dummy').aggregate([{
    $project: {
        plates: 1, 
        accounts: 1,
        intersect: {
            $setIntersection: [
                { $arrayElemAt: [ "$plates", 0 ] },
                { $arrayElemAt: [ "$accounts", 4 ] }
            ]
        }
    }
}])

我期待的预期输出是:

{
    "_id" : "Suzuki",
    "qty" : 10,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123",
        }
    ]
}

1 个答案:

答案 0 :(得分:0)

所以有两种方法,但你真正想要的只是改为$filter

使用$in可能是首选:

db.getCollection('dummy').aggregate([
  { "$addFields": {
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "cond": {
          "$in": [ "$$this.rego", "$plates.rego" ]
        }
      }
    }
  }}
])

或者,如果您至少没有MongoDB 3.4,那么使用$anyElementTrue

db.getCollection('dummy').aggregate([
  { "$project": {
    "qty": 1,
    "plates": 1,
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "as": "acc",
        "cond": {
          "$anyElementTrue": {
            "$map": {
              "input": "$plates.rego",
              "as": "rego",
              "in": { "$eq": [ "$$rego", "$$acc.rego" ] }
            }
          }
        }
      }
    }
  }}
])

甚至$setIsSubset

db.getCollection('dummy').aggregate([
  { "$project": {
    "qty": 1,
    "plates": 1,
    "accounts": {
      "$filter": {
        "input": "$accounts",
        "as": "acc",
        "cond": {
          "$setIsSubset": [ ["$$acc.rego"], "$plates.rego" ]
        }
      }
    }
  }}
])

对于这种类型的操作,它实际上不是$setIntersection,因为这需要将“只是字段值”作为“集合”进行比较,输出实际上只是“那个”而不是“对象”。

您可以通过将数组索引与生成的“set”位置匹配来做一些愚蠢的事情:

db.getCollection('dummy').aggregate([
  { "$addFields": {
    "accounts": {
      "$map": { 
        "input": { "$setIntersection": ["$plates.rego", "$accounts.rego"] },
        "in": {
          "$arrayElemAt": [
            "$accounts",
            { "$indexOfArray": [ "$accounts.rego", "$$this" ] }      
          ]
        }
      }
    }
  }}
])

但实际上你可能只是希望$filter结果更加实用。如果您希望将输出设置为“set”,那么您只需使用$filter或类似运算符包装$setDifference输出即可使条目“唯一”。

在所有变体中,这些都会返回:

{
    "_id" : "Suzuki",
    "qty" : 10.0,
    "plates" : [ 
        {
            "rego" : "1QX-WA-123",
            "date" : 1516374000000.0
        }, 
        {
            "rego" : "1QX-WA-456",
            "date" : 1513369800000.0
        }
    ],
    "accounts" : [ 
        {
            "_id" : "23kpi9MD4KnTvnaW7",
            "createdAt" : 1513810712802.0,
            "date" : 1503446400000.0,
            "type" : "Suzuki",
            "rego" : "1QX-WA-123"
        }
    ]
}

"accounts"数组“过滤”中的项目与"rego"数组中相应的"plates"数量相匹配。