【问题标题】:Nested filters: $filter array, then $filter child array嵌套过滤器:$filter 数组,然后是 $filter 子数组
【发布时间】:2016-11-14 00:39:34
【问题描述】:

本质上,我正在尝试过滤掉已“丢弃”的子文档和子子文档。这是我的架构的精简版:

permitSchema = {
  _id,
  name,
  ...
  feeClassifications: [
    new Schema({
      _id,
      _trashed,
      name,
      fees: [
        new Schema({
          _id,
          _trashed,
          name,
          amount
        })
      ]
    })
  ],
  ...
}

所以我可以通过feeClassifications 获得我想要的效果。但我正在努力寻找一种对feeClassifications.fees 也产生相同效果的方法。

所以,这可以按需要工作:

Permit.aggregate([
  { $match: { _id: mongoose.Types.ObjectId(req.params.id) }},
  { $project: {
    _id: 1,
    _name: 1,
    feeClassifications: {
      $filter: {
        input: '$feeClassifications',
        as: 'item',
        cond: { $not: {$gt: ['$$item._trashed', null] } }
      }
    }
  }}
])

但我也想过滤嵌套数组fees。我尝试了一些方法,包括:

Permit.aggregate([
  { $match: { _id: mongoose.Types.ObjectId(req.params.id) }},
  { $project: {
    _id: 1,
    _name: 1,
    feeClassifications: {
      $filter: {
        input: '$feeClassifications',
        as: 'item',
        cond: { $not: {$gt: ['$$item._trashed', null] } }
      },
      fees: {
        $filter: {
          input: '$fees',
          as: 'fee',
          cond: { $not: {$gt: ['$$fee._trashed', null] } }
        }
      }
    }
  }}
])

这似乎跟 mongodb docs 最接近。但我得到了错误: this object is already an operator expression, and can't be used as a document expression (at 'fees')

更新:------------

根据要求,这是一个示例文档:

{
    "_id" : ObjectId("57803fcd982971e403e3e879"),
    "_updated" : ISODate("2016-07-11T19:24:27.204Z"),
    "_created" : ISODate("2016-07-09T00:05:33.274Z"),
    "name" : "Single Event",
    "feeClassifications" : [ 
        {
            "_updated" : ISODate("2016-07-11T19:05:52.418Z"),
            "_created" : ISODate("2016-07-11T17:49:12.247Z"),
            "name" : "Event Type 1",
            "_id" : ObjectId("5783dc18e09be99840fad29f"),
            "fees" : [ 
                {
                    "_updated" : ISODate("2016-07-11T18:51:10.259Z"),
                    "_created" : ISODate("2016-07-11T18:41:16.110Z"),
                    "name" : "Basic Fee",
                    "amount" : 156.5,
                    "_id" : ObjectId("5783e84cc46a883349bb2339")
                }, 
                {
                    "_updated" : ISODate("2016-07-11T19:05:52.419Z"),
                    "_created" : ISODate("2016-07-11T19:05:47.340Z"),
                    "name" : "Secondary Fee",
                    "amount" : 50,
                    "_id" : ObjectId("5783ee0bad7bf8774f6f9b5f"),
                    "_trashed" : ISODate("2016-07-11T19:05:52.410Z")
                }
            ]
        }, 
        {
            "_updated" : ISODate("2016-07-11T18:22:21.567Z"),
            "_created" : ISODate("2016-07-11T18:22:21.567Z"),
            "name" : "Event Type 2",
            "_id" : ObjectId("5783e3dd540078de45bbbfaf"),
            "_trashed" : ISODate("2016-07-11T19:24:27.203Z")
        }
    ]
}

这是所需的输出(“垃圾”子文档被排除在 feeClassificationsfees 之外):

{
    "_id" : ObjectId("57803fcd982971e403e3e879"),
    "_updated" : ISODate("2016-07-11T19:24:27.204Z"),
    "_created" : ISODate("2016-07-09T00:05:33.274Z"),
    "name" : "Single Event",
    "feeClassifications" : [ 
        {
            "_updated" : ISODate("2016-07-11T19:05:52.418Z"),
            "_created" : ISODate("2016-07-11T17:49:12.247Z"),
            "name" : "Event Type 1",
            "_id" : ObjectId("5783dc18e09be99840fad29f"),
            "fees" : [ 
                {
                    "_updated" : ISODate("2016-07-11T18:51:10.259Z"),
                    "_created" : ISODate("2016-07-11T18:41:16.110Z"),
                    "name" : "Basic Fee",
                    "amount" : 156.5,
                    "_id" : ObjectId("5783e84cc46a883349bb2339")
                }
            ]
        }
    ]
}

【问题讨论】:

    标签: mongodb mongoose aggregation-framework


    【解决方案1】:

    由于我们要过滤外部和内部数组字段,我们可以使用$map 变量运算符返回一个包含我们想要的“值”的数组。

    $map 表达式中,我们提供了一个逻辑$conditional $filter 来从文档和子文档数组字段中删除不匹配的文档。

    条件为$lt,当子文档和/或子文档数组字段中不存在字段“_trashed”时返回true。

    请注意,在 $cond 表达式中,我们还为 <false case> 返回 false。当然,我们需要对$map结果应用过滤器以删除所有false

    Permit.aggregate(
        [ 
            { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
            { "$project": { 
                "_updated": 1, 
                "_created": 1, 
                "name": 1, 
                "feeClassifications": { 
                    "$filter": {
                        "input": {
                            "$map": { 
                                "input": "$feeClassifications", 
                                "as": "fclass", 
                                "in": { 
                                    "$cond": [ 
                                        { "$lt": [ "$$fclass._trashed", 0 ] }, 
                                        { 
                                            "_updated": "$$fclass._updated", 
                                            "_created": "$$fclass._created", 
                                            "name": "$$fclass.name", 
                                            "_id": "$$fclass._id", 
                                            "fees": { 
                                                "$filter": { 
                                                    "input": "$$fclass.fees", 
                                                    "as": "fees", 
                                                    "cond": { "$lt": [ "$$fees._trashed", 0 ] }
                                                }
                                            }
                                        }, 
                                        false 
                                    ]
                                }
                            }
                        }, 
                        "as": "cls",  
                        "cond": "$$cls"
                    }
                }
            }}
        ]
    )
    

    在即将发布的 MongoDB 版本中(截至撰写本文时以及自 MongoDB 3.3.5 起),您可以将 $map 表达式中的 $cond 表达式替换为 $switch 表达式:

    Permit.aggregate(
        [ 
            { "$match": { "_id": mongoose.Types.ObjectId(req.params.id) } },
            { "$project": { 
                "_updated": 1, 
                "_created": 1, 
                "name": 1, 
                "feeClassifications": { 
                    "$filter": {
                        "input": {
                            "$map": { 
                                "input": "$feeClassifications", 
                                "as": "fclass", 
                                "in": { 
                                    "$switch": { 
                                        "branches": [ 
                                            { 
                                                "case": { "$lt": [ "$$fclass._trashed", 0 ] }, 
                                                "then": { 
                                                    "_updated": "$$fclass._updated", 
                                                    "_created": "$$fclass._created", 
                                                    "name": "$$fclass.name", 
                                                    "_id": "$$fclass._id", 
                                                    "fees": { 
                                                        "$filter": { 
                                                            "input": "$$fclass.fees", 
                                                            "as": "fees", 
                                                            "cond": { "$lt": [ "$$fees._trashed", 0 ] }
                                                        }
                                                    }
                                                } 
                                            } 
                                        ], 
                                        "default":  false 
                                    }
                                }
                            }
                        },
                        "as": "cls",  
                        "cond": "$$cls"
                    }
                }
            }}
        ]
    )
    

    【讨论】:

    • 感激不尽!感谢您提供信息丰富的答案和解释。这让我花在自己试图解决这个问题上的时间变得值得!
    【解决方案2】:

    对于更复杂的大数据,这将是不必要的困难。 只需在 $filter 输入中添加一个虚线注释字段即可对其进行编辑。您可以通过虚线注释将文档搜索到任何深度的 JSON,而无需进一步复杂的 $filter 映射。

    "$filter":{
           "input": "$feeClassifications._trashed",
           "as": "trashed",
           "cond": { "$lt": [ "$$trashed._trashed", 0 ] }                           
     }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-06-27
      • 1970-01-01
      • 2020-11-01
      • 1970-01-01
      • 2020-12-11
      相关资源
      最近更新 更多