mongodb - 在 Mongodb 中过滤深度嵌套的对象数组

标签 mongodb mongoose mongodb-query aggregation-framework

我在 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.username 匹配 users 数组对象并检索匹配的对象数组(始终只有 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
        }
    }
])

输出*

用于将 username 匹配为 Adduser,将 kpiObj.kpiName 匹配为 epsilonkpiObj.resultObj。 result.mark 为 4,我期待以下输出:-

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

最佳答案

尝试下面的聚合查询:

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 并且只返回必填字段而不是整个对象。

关于mongodb - 在 Mongodb 中过滤深度嵌套的对象数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62205453/

相关文章:

javascript - 单击按钮后显示图像

python - 有没有一种快速/最佳的方法来获取特定键的唯一值列表?

mongodb - AWS 中用于 Meteor 项目设置的 Mongodb GUI

javascript - 使用 mongoose 更新文档(更新文档属性的属性)

node.js - 分页降序排列

MongoDb:如何对日期字段进行聚合、分组和排序?

node.js - 如何访问文本分数 MongoDB $文本搜索

arrays - 如何检查数组中的最后一个元素是否符合条件,mongodb

node.js - 如果其他 Mongoose 查找查询 - 没有结果

Mongodb 无法连接到本地服务器