Mongo DB中深度嵌套对象数组的过滤

3

我在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.usernameusers数组对象,并检索匹配的对象数组(始终只有1个)。
  • 阶段2:将从阶段1输出与kpiObj中的kpiName匹配,并检索匹配的对象数组(始终只有1个)。
  • 阶段3:将从阶段2输出与resultObj中的mark匹配,并检索匹配的对象数组。

我已经花了过去3天时间观看了几个教程并查看了几个stackoverflow问题,但是还没有能够获得期望的输出。我已经使用以下查询语句获得了阶段1的期望输出。希望能得到任何帮助。

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.kpiNameepsilon,以及kpiObj.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
@Joe:我已经更新了预期输出。 - relu
1个回答

4

尝试以下聚合查询:

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数组时,可以用$map替换$filter,并仅返回所需字段而不是整个对象。


1
非常感谢。这已经完美无缺了 :) 您能否推荐一些好的互联网教程,帮助我介绍esp Mongo v 4.x的概念? - relu
1
@新手:看这个:: https://university.mongodb.com,那是免费的并且直接来自MongoDB。 - whoami - fakeFaceTrueSoul

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接