使用$ redact过滤多个数组

时间:2016-03-17 09:21:53

标签: node.js mongodb mongodb-query aggregation-framework

我实际上是在尝试制作一个mongodb请求,首先在列表中填充过滤器,然后在另一个列表上填充。

说明

我的数据库中有以下对象:

{
  "_id": ObjectId("..."),
  "title": "MySuperProject",
  "files": [
    {
      "title":"My skiing day !",
      "right":[{
        "role":"USER",
        "access":["read"]
      }]
    },
    {
      "title":"My little dog, so cute !",
      "right":[{
        "role":"OTHER",
        "access":["read"]
      }]
    }
  ],

"people": [
    {
      "userName":"borat",
      "role":"OTHERONE",
      "right":[{
        "role":"USER",
        "access":["read"]
      }]
    },
    {
      "userName":"Thomas",
      "role":"OTHER",
      "right":[{
        "role":"OTHER",
        "access":["read"]
      }]
    }
  ]
}

我需要什么

我需要将参数传递给我的函数,即当前用户角色。在我的情况下,我通过" USER"作用。

这样,我需要检索这个结果:

{
  "_id": ObjectId("..."),
  "title": "MySuperProject",
  "files": [
    {
      "title":"My skiing day !",
      "right":[{
        "role":"USER",
        "access":["read"]
      }]
    }
  ],

"people": [
    {
      "userName":"borat",
      "role":"OTHER",
      "right":[{
        "role":"USER",
        "access":["read"]
      }]
    }
  ]
}

这会减少与我匹配的项目角色上的两个列表。

我有什么

我有一个查询,通过" role"来减少文件列表。属性。

事实是,在我的人员列表中,我有两次角色属性:

  • 一次确定人员角色
  • 一次确定哪种人可以访问(正确访问)人员档案

我的查询通过匹配遇到的第一个角色(people.role而不是people.right.role)来减少人员结果集。

我的查询如下:

db.t.aggregate([
  {
    $match: {
      "title": projectTitle
    }
  },
  {
    $redact: {
      $cond: [{
        $eq: [role, {
          $ifNull: ["$role", role]
        }]
      }, "$$DESCEND", "$$PRUNE"]
    }
  },
  {
    $redact: {
      $cond: [{
        $gt: [{
          $size: {
            $ifNull: ["$right", [1]]
          }
        }, 0]
      }, "$$DESCEND", "$$PRUNE"]
    }
  },
])

总结:我需要查询减少人员和与right.role值匹配的文件列表

1 个答案:

答案 0 :(得分:0)

$redact管道阶段不是最好的工具,因为它不能很好地转换为“不同级别的不同条件” “不同地处理不同的数组”< / em>由于其预期功能的性质。

这里更好的情况是普通的旧数组过滤,已经有一段时间的方法。

最好的选择是来自MongoDB 3.2的$filter,如果你有:

db.t.aggregate([
    { "$project": {
        "title": 1,
        "files": {
            "$filter": {
                "input": "$files",
                "as": "file",
                "cond": { "$eq": [ "$$file.right.role", "USER" ] }
            }
        },
        "people": {
            "$filter": {
                "input": "$people",
                "as": "person",
                "cond": { "$eq": [ "$$person.right.role", "USER" ] }
            }
        }
    }}
])

对于MongoDB 2.6,如果您的数组元素真正“不同”且文档中没有两个相同,那么您可以使用$map$setDifference“模拟”该较新的函数来删除数组中的任何false结果:

db.t.aggregate([
    { "$project": {
        "title": 1,
        "files": {
            "$setDifference": [
                { "$map": {
                    "input": "$files",
                    "as": "file",
                    "in": {
                       "$cond": [
                           { "$eq": [ "$$file.right.role", "USER" ] },
                           "$$file",
                           false
                       ]
                    }
                }},
                [false]
            ]
        },
        "people": {
            "$setDifference": [
                { "$map": {
                    "input": "$people",
                    "as": "person",
                    "in": {
                       "$cond": [
                           { "$eq": [ "$$person.right.role", "USER" ] },
                           "$$person",
                           false
                       ]
                    }
                }},
                [false]
            ]
        }
    }}
])

它是相同的基本条件测试,除了在后一种情况下$map只返回作为输入提供的每个数组元素的元素。因此,匹配的元素或false的值将被false中的“设置比较”移至$setDifference

这些都是单个管道阶段操作,因此它们是执行此操作的最快方法。

与往常一样,除非您真的需要这些信息继续进入更多的聚合管道阶段,或者如果确实将数据中的内容减少了“重要”数量,从而为您节省了大量的网络流量,那么它总会更好在客户端对数据库本身进行“过滤”。

这些进程中的任何一个都没有真正的开销,但客户端代码可以说是直截了当的,并且可能从“少”的网络流量中获得的可能会在服务器上的处理成本中丢失。