嵌套过滤器:$filter 数组,然后是 $filter 子数组
Posted
技术标签:
【中文标题】嵌套过滤器:$filter 数组,然后是 $filter 子数组【英文标题】:Nested filters: $filter array, then $filter child array 【发布时间】: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")
]
这是所需的输出(“垃圾”子文档被排除在 feeClassifications
和 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")
]
]
【问题讨论】:
【参考方案1】:由于我们要过滤外部和内部数组字段,我们可以使用$map
变量运算符返回一个包含我们想要的“值”的数组。
在$map
表达式中,我们提供了一个逻辑$cond
itional $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 ]
【讨论】:
以上是关于嵌套过滤器:$filter 数组,然后是 $filter 子数组的主要内容,如果未能解决你的问题,请参考以下文章