MongoDB-有条件地匹配两个数组

时间:2020-06-05 16:33:14

标签: mongodb mongodb-query aggregation-framework

有以下对象数组:

let externalArray = 
[
 { name: "xxx",
   max: 100,
   unit: "myUnit"
 },
 { name: "yyy",
   max: 90,
   unit: "myUnit"
 }
]

以及以下mongodb结构:

[
    {
        "myList": [
            {
                "name": "xxx",
                "amount": 66.3,
                "unit": "myUnit"
            },
            {
                "name": "yyy",
                "amount": 11.6,
                "unit": "myUnit"
            },
            {
                "name": "zzz",
                "amount": 6.9,
                "unit": "myUnit"
            }
        ]
    }
]

如何在聚合中使用$ match查询来仅输出myList.amount <= externalArray.max和myList.unit等于同名externalArray.unit的对象?非常感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

由于您正在将输入数组与文档中的数组进行比较,因此您需要与$filter一起做更多的事情:

db.collection.aggregate([
    /** Unwind (Split array into objects :: total docs = each doc * no.of objects in array of respective doc) */
    {
      $unwind: "$myList"
    },
    /** Iterate on input `externalArray` & check for all conditions. Creates an array field `matches` */
    {
      $addFields: {
        matches: {
          $filter: {
            input: externalArray,
            cond: {
              $and: [ { $eq: [ "$$this.name", "$myList.name" ] },
                { $eq: [ "$$this.unit", "$myList.unit" ] },
                { $lte: [ "$myList.amount", "$$this.max" ] }
              ]
            }
          }
        }
      }
    },
    /** In filter step if it matches with any condition then `matches` will have 1 or more objects from `externalArray` 
     * Excluding all docs where there is no match */
    {
      $match: { matches: { $ne: [] } }
    },
    /** Remove unnecessary field */
    {
      $project: { matches: 0 }
    },
    /** Since we unwind the array - group back all docs based on `_id` */
    {
      $group: { _id: "$_id", myList: { $push: "$myList" } }
    }
  ])

测试: mongoplayground

注意:

在回复中:

  1. myList数组将仅包含来自externalArray的匹配对象。
  2. 您不会看到myList不存在或myListexternalArray之间不存在匹配对象的文档(因此,两个数组之间至少应有一个匹配对象)以便将文档取出来。)

答案 1 :(得分:0)

您可以在将外部数组传​​递到聚合管道之前对其进行转换,使其具有以下结构并用作条件。

我们可以在$match表达式中使用它来匹配具有至少一个匹配项的文档。

[{
  "name": "xxx",
  "amount": {
    "$lte": 100
  },
  "unit": "myUnit"
},
{
  "name": "yyy",
  "amount": {
    "$lte": 90
  },
  "unit": "myUnit"
}]
/* example js implementation */
const matchConditions = externalArray.map(({ name, max, unit })=> ({
  name,
  amount: { $lte: max },
  unit
}))

我们可以在$filter条件下使用以下内容

[{
  "$and": [
    { "$eq": ["name", "xxx"] },
    { "$lte"  ["amount", 100 },
    { "$eq": ["unit", "myUnit"] }
  ]
},
{
  "$and": [
    { "$eq": ["$$this.name", "yyy"] },
    { "$lte":  ["$$this.amount", 90 },
    { "$eq": ["$$this.unit", "myUnit"] }
  ]
}]
/* example js implementation */
const filterConditions = externalArray.map(({ name, max, unit }) => ({
  $and: [
    { $eq: ['$$this.name', name] },
    { $lte:  ['$$this.amount', max },
    { $eq: ['$$this.unit', unit] }
  ]
}))

然后最后我们可以使用以下聚合

db.collection.aggregate([
  {
    $match: {
      myList: {
        $elemMatch: {
          $or: matchConditions
        }
      }
    }
  },
  {
    $addFields: {
      myList: {
        $filter: {
          input: '$myList',
          cond: {
            $or: filterConditions
          }
        }
      }
    }
  }
])