【问题标题】:Filtering deeply nested array of objects in Mongo DB在 Mongodb 中过滤深度嵌套的对象数组
【发布时间】:2020-09-24 01:50:18
【问题描述】:

我在 Mongo DB 中有一个看起来像这样的集合 -

const clientsColection = { 
    "_id" : ObjectId("5ec8492c27ecdc17362b86cb"),
    "clientName" : "data" ,  
    "users" : [
        {
            "roles" : [], 
            "operations" : [], 
            "_id" : ObjectId("5ecac60ab527bd0ba4a615cf"), 
            "isAdmin" : false, 
            "username" : "Adduser"
        }, 
        {
            "roles" : [], 
            "operations" : [], 
            "_id" : ObjectId("5ecac60ab527bd0ba4a616cf"), 
            "isAdmin" : false, 
            "username" : "new"
        }
    ], 
    "kpiObj" : [
        {
            "kpiName" : "epsilon", 
            "resultObj" : {
                "result" : [
                    {
                        "mark" : 4, 
                        "plz" : "01069"
                    }, 
                    {
                        "mark" : 5, 
                        "plz" : "01067"
                    }
                ], 
            }
        }, 
        {
            "kpiName" : "epsilon2", 
            "resultObj" : {
                "result" : [
                    {
                        "mark" : 3, 
                        "plz" : "01069"
                    }, 
                    {
                        "mark" : 1, 
                        "plz" : "01067"
                    }
                ], 
            }
        }
    ] 
}

我正在尝试使用 aggregateprojectfilter 运算符对嵌套的对象数组执行过滤器,但我还没有成功获得预期的输出。

我想实现以下目标:-

  • 阶段 1:将 users 数组对象与 users.username 匹配并检索匹配的对象数组(始终只有 1 个)。
  • 阶段 2:将阶段 1 的输出与 kpiObj 中的 kpiName 匹配,并检索匹配的对象数组(始终只有一个)。
  • 第 3 阶段。将第 2 阶段的输出与resultObj 中的mark 匹配,并检索匹配的对象数组。

在过去的 3 天里,我观看了几个教程并解决了几个堆栈溢出问题,但未能获得预期的输出。 通过使用以下查询,我已经能够获得 stage1 的预期输出。任何帮助将不胜感激。

db.getCollection("clientsCollection").aggregate([
    { $match: { 'users.username': 'Adduser' } },
    {
        $project: {
            users: {
                $filter: {
                    input: '$users',
                    as: 'user',
                    cond: { $eq: ['$$user.username', 'Adduser'] }
                }
            }, 'kpiObj.kpiName':1, 'kpiObj.resultObj.result.score':1 , 'kpiObj.resultObj.result.plz':1
        }
    }
])

输出*

为了匹配usernameAdduserkpiObj.kpiNameepsilonkpiObj.resultObj.result.mark 为4,我期待以下输出:-

const clientsColection = { 
    "_id" : ObjectId("5ec8492c27ecdc17362b86cb"),
    "clientName" : "data" ,  
    "users" : [
        {
            "username" : "Adduser"
        }
    ], 
    "kpiObj" : [
        {
            "kpiName" : "epsilon", 
            "resultObj" : {
                "result" : [
                    {
                        "mark" : 4, 
                        "plz" : "01069"
                    }
                ], 
            }
        }
    ] 
}

【问题讨论】:

  • 在该示例中,应针对kpiName 测试哪些标准?您想在输出文档方面得到什么?
  • @Joe :- 我已经更新了预期的输出。

标签: mongodb mongoose mongodb-query aggregation-framework


【解决方案1】:

试试下面的聚合查询:

db.collection.aggregate([
    /** Filter for docs with possible conditions */
    {
      $match: {
        "users.username": "Adduser",
        "kpiObj.kpiName": "epsilon",
        "kpiObj.resultObj.result.mark": 4
      }
    },
    /** Re-create `users` array with new users array with only matching object */
    {
      $addFields: { users: { $filter: { input: "$users", cond: { $eq: [ "$$this.username", "Adduser" ] } } } }
    },
    /** Re-create `kpiObj` array with new kpiObj array with only matching object */
    {
      $addFields: {
        kpiObj: {
          $filter: { input: "$kpiObj", cond: { $eq: [ "$$this.kpiName", "epsilon" ] } }
        }
      }
    },
    /** Unwind (Split array into objects - here you'll have only 1) for flexibility to iterate over `kpiObj.resultObj.result` */
    {
      $unwind: "$kpiObj"
    },
    /** Re-create `kpiObj.resultObj.result` array with new result array with only matching object */
    {
      $addFields: {
        "kpiObj.resultObj.result": {
          $filter: { input: "$kpiObj.resultObj.result", cond: { $eq: [ "$$this.mark", 4 ] } }
        }
      }
    },
    /** remove docs where there is not match on `kpiObj.resultObj.result` */
    {
      $match: { "kpiObj.resultObj.result": { $ne: [] } }
    },
    /** I would consider `kpiObj` & `users` as single objects `{}`, 
     * ratherthan array of single objects `[{}]`
     * Just in case if you need to be an array making object to array
     *  */
    {
      $addFields: { kpiObj: [ "$kpiObj" ] }
    }
  ])

测试: mongoplayground

注意:

您可以使用$project 而不是$addFields 来限制每个阶段中的字段。如您所愿,最后添加此阶段:

  {
    $project: {
      "users.username": 1,
      kpiObj: 1
    }
  }

或在迭代users 数组时将$filter 替换为$map,并且只返回必需的字段而不是整个对象。

【讨论】:

  • 非常感谢。它非常完美:) 你能在互联网上推荐一些好的教程来帮助我介绍 esp Mongo v 4.x 的这些概念吗?
  • @newbie : 检查这个 :: university.mongodb.com ,这是免费的,直接来自 MongoDB。
猜你喜欢
  • 2019-01-29
  • 1970-01-01
  • 1970-01-01
  • 2019-08-10
  • 2017-06-29
  • 1970-01-01
  • 2021-06-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多