【问题标题】:Mongodb - aggregate items from array, matching item property or subarray item propertyMongodb - 从数组聚合项,匹配项属性或子数组项属性
【发布时间】:2023-03-08 17:58:01
【问题描述】:

我有以下结构的文档:

{
  _id: "UNIQUE_ID",
  myarray: [
    {
      mykey: '12345',
      // other fields
    },
    {
      // other fields
      nestedarray: [
        {
          mykey: '67890',
          // other fields
        }
      ]
    }
  ]
}

我需要从myarray 返回所有项目,其中mykey(在myarraynestedarray 的项目上)属于一组值。例如,对于上面的文档,如果值集是['12345, '67890'],则应该返回来自myarray 的两项。

我正在使用以下代码来做到这一点:

collection.aggregate([
  {
    $match: {
      "_id": documentId,
      $or: [
        { "myarray": {$elemMatch: {"mykey": { $in: ['12345, '67890'] } } } },
        { "myarray.$.nestedarray": {$elemMatch: {"mykey": { $in: ['12345, '67890'] } }} }
      ]
    }
  },
  {
    $project: {
      myarray: {
        $filter: {
          input: '$myarray',
          as: 'arrayitem',
          cond: {
            $or: [
              { $eq: ["$$arrayitem.mykey", '12345'] },
              { $eq: ["$$arrayitem.nestedarray.[$].mykey", '12345'] }
            ]
          }
        }
      }
    }
  }
]);

但这只会返回 mykeymyarray 级别匹配的项目(在 nestedarray 内不匹配)。

我做错了什么?

另外,如何在 $filter 函数中使用 set ['12345, '67890'] 而不是单个值 '12345'

澄清:

  • 如果mykey 匹配来自myarray 的项目:包括该项目(该项目将没有nestedarray 字段)
  • 如果mykey 匹配来自nestedarray 的项目:包括来自myarray 的项目,其中包含nestedarray(也包含nestedarray 的完整内容)。来自myarray 的此项将没有mykey 字段

示例:

数据:

{
  _id: "UNIQUE_ID",
  myarray: [
    {
      mykey: '11111',
      // other fields
    },
    {
      // other fields
      nestedarray: [
        {
          mykey: '22222',
          // other fields
        },
        {
          mykey: '84325',
          // other fields
        }
      ]
    },
    {
      mykey: '645644',
      // other fields
    },
    {
      // other fields
      nestedarray: [
        {
          mykey: '23242',
          // other fields
        },
        {
          mykey: '23433',
          // other fields
        }
      ]
    }
  ]
}

一组值:['11111', '22222']

预期的查询结果:

{
  _id: "UNIQUE_ID",
  myarray: [
    {
      mykey: '11111',
      // other fields
    },
    {
      // other fields
      nestedarray: [
        {
          mykey: '22222',
          // other fields
        },
        {
          mykey: '84325',
          // other fields
        }
      ]
    }
  ]
}

【问题讨论】:

  • 如果mykey 存在于nestedarray 中但不存在于myArray 中怎么办?
  • 那么应该包含来自myarray的包含nestedarray的项目,并且应该只包含来自nestedarray的匹配项目
  • 好的,那么下面的答案对你有用。
  • @AnthonyWinzlet 实际上来自nestedarray 的所有项目都是必需的(即使其中只有一个匹配)。
  • 所以基本上你不想过滤nestedarray数组?

标签: mongodb aggregation-framework


【解决方案1】:

您可以使用单个$filter,然后作为cond,您可以直接检查mykey 或使用$anyElementTrue 作为数组。

db.col.aggregate([
    {
        $project: {
            myarray: {
                $filter: {
                    input: "$myarray",
                    cond: {
                        $or: [
                            { $in: [ "$$this.mykey", ["11111", "22222"] ] },
                            { $anyElementTrue: [ 
                                { 
                                    $map: { 
                                        input: { $ifNull: [ "$$this.nestedarray", [] ] }, 
                                        as: "na", 
                                        in: { $in: [ "$$na.mykey", ["11111", "22222"] ] } 
                                    } 
                                } 
                                ] 
                            }
                        ]
                    }
                }
            }
        }
    }
])

Mongo playground

【讨论】:

  • 感谢您的回答!试过了,但这只返回了带有非嵌套 mykey 的项目。
  • @GCSDC 这对我来说有点不清楚,修改(并简化)了我的答案,适用于您的示例
  • 这就是我要找的!非常感谢!
【解决方案2】:

你可以使用下面的聚合

db.collection.aggregate([
  { "$match": { "_id": documentId }},
  { "$project": {
    "myarray": {
      "$filter": {
        "input": {
          "$map": {
            "input": "$myarray",
            "as": "arrayitem",
            "in": {
              "mykey": "$$arrayitem.mykey",
              "nestedarray": "$$arrayitem.nestedarray",
              "aaaa": {
                "$filter": {
                  "input": "$$arrayitem.nestedarray",
                  "as": "vv",
                  "cond": { "$in": ["$$vv.mykey", ["12345", "67890"]] }
                }
              }
            }
          }
        },
        "as": "ff",
        "cond": {
          "$or": [
            { "$in": ["$$ff.mykey", ["12345", "67890"]] },
            { "$gte": [{ "$size": { "$ifNull": ["$$ff.aaaa", []] }}, 1] }
          ]
        }
      }
    }
  }},
  { "$project": { "myarray.aaaa": 0 }}
])

这是工作的example

【讨论】:

  • 感谢您的回答!在这种情况下,我应该保持$match 与我在我的问题上发布的完全一致,还是应该放弃它(在这种情况下,如何匹配文档_id)?
  • 使用单个 $match_id。剩下的事情$filter 会做。
  • 试过了,但我收到以下错误errmsg:"The argument to $size must be an array, but was of type: null"
  • 再次更新了我的答案
  • 差不多了。唯一的问题是它没有为myarray 项目带来其他属性(仅mykeynestedarray)。我正在尝试弄清楚如何做到这一点。
猜你喜欢
  • 2019-10-21
  • 2021-01-25
  • 1970-01-01
  • 2021-09-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多