当然,你也可以简化您的流水线级数相当多,主要是因为连续$match
阶段是一个真正的单一阶段,你应该总是使用匹配任何聚合流水线的开始的标准。即使它实际上不“过滤”数组内容,它至少也只是选择包含实际匹配条目的文档。这极大地加快了速度,尤其是对于大型数据集。
对于两个日期范围,以及这只是一个$or
查询参数。此外,它将在数组过滤完成之前“应用”,因为毕竟它是以文档级别匹配开始的。如此反复,在第一个管道$match
:
db.getCollection('customers').aggregate([
// Filter all document conditions first. Reduces things to process.
{ "$match": {
"status": "Closed",
"lines": { "$elemMatch": {
"status": "Closed",
"deliveryMethod": "Tech Delivers"
}},
"$or": [
{ "date": {
"$gte": new Date("2016-01-01"),
"$lt": new Date("2016-04-01")
}},
{ "date": {
"$gte": new Date("2016-04-01"),
"$lt": new Date("2016-08-01")
}}
]
}},
// Unwind the array
{ "$unwind": "$lines" },
// Filter just the matching elements
// Successive $match is really just one pipeline stage
{ "$match": {
"lines.status": "Closed",
"lines.deliveryMethod": "Tech Delivers"
}},
// Then group on the productline values within the array
{ "$group":{
"_id": "$lines.productLine",
"countA": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date", new Date("2016-01-01") ] },
{ "$lt": [ "$date", new Date("2016-04-01") ] }
]},
1,
0
]
}
},
"countB": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date", new Date("2016-04-01") ] },
{ "$lt": [ "$date", new Date("2016-08-01") ] }
]},
1,
0
]
}
}
}}
])
的$or
基本上是“连接”两个结果集为它寻找“要么”范围内的标准申请。由于这是除了其他论点之外给出的,所以这个逻辑是一个“AND”条件,与其他条件中的其他条件一样,这个条件与$or
论点相符。注意$gte
和$lt
组合也是在同一个键上表达“AND”条件的另一种形式。
应用$elemMatch
是因为数组元素需要“两个”条件。如果您直接将它们应用为“点符号”,那么真正要求的是“至少有一个数组元素”匹配每个条件,而不是匹配“两个”条件的数组元素。
$unwind
之后的后面的过滤可以使用“点符号”,因为数组元素现在被“解除归一化”为单独的文档。因此,每个文档只有一个元素符合条件。
当您应用$group
,而不是仅仅使用{ "$sum": 1 }
的你,而“有条件地评估是否通过$cond
计数与否。由于这两个日期范围的结果中,你只需要确定当前文档是”卷起来“属于一个日期范围或另一个。作为”三元“(如果/然后/其他)运算符,这是$cond
提供。
它查看文档中的"date"
中的值,如果它匹配条件设置(第一个参数 - 如果)然后它返回1
(第二个参数 - 然后),否则它返回0
,有效地不会增加到当前计数
由于这些是“逻辑的”条件,则“与”表示与逻辑$and
运算符,它本身返回true
或false
,要求既包含条件是true
。
还要注意对象构造函数中的更正,因为如果您没有用该表示中的字符串实例化,则生成的Date
处于“本地时间”,而不是“MongoDB”存储日期的“UTC”格式。只有使用“本地”构造函数才是真正的意思,而且通常人们确实不这样做。
另一个说明是$lt
日期更改,应该总是比您要查找的上一个日期大“一天”。请记住,这些是“日期开始”日期,因此您通常需要在日期内的所有可能时间,而不是刚开始。所以它是“不到第二天”的正确条件。
根据记录,从2.6版本的MongoDB,很可能不如“预过滤器”数组内容你$unwind
“之前”。这消除了在发生“解规范化”时生成新文档的开销,这与您要应用于数组元素的条件不匹配。
MongoDB的3.2和更大,使用:
db.getCollection('customers').aggregate([
// Filter all document conditions first. Reduces things to process.
{ "$match": {
"status": "Closed",
"lines": { "$elemMatch": {
"status": "Closed",
"deliveryMethod": "Tech Delivers"
}},
"$or": [
{ "date": {
"$gte": new Date("2016-01-01"),
"$lt": new Date("2016-04-01")
}},
{ "date": {
"$gte": new Date("2016-04-01"),
"$lt": new Date("2016-08-01")
}}
]
}},
// Pre-filter the array content to matching elements
{ "$project": {
"lines": {
"$filter": {
"input": "$lines",
"as": "line",
"cond": {
"$and": [
{ "$eq": [ "$$line.status", "Closed" ] },
{ "$eq": [ "$$line.deliveryMethod", "Tech Delivers" ] }
]
}
}
}
}},
// Unwind the array
{ "$unwind": "$lines" },
// Then group on the productline values within the array
{ "$group":{
"_id": "$lines.productLine",
"countA": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date": new Date("2016-01-01") ] },
{ "$lt": [ "$date", new Date("2016-04-01") ] }
]},
1,
0
]
}
},
"countB": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date", new Date("2016-04-01") ] },
{ "$lt": [ "$date", new Date("2016-08-01") ] }
]},
1,
0
]
}
}
}}
])
或者至少MongoDB的2.6,然后应用$redact
代替:
db.getCollection('customers').aggregate([
// Filter all document conditions first. Reduces things to process.
{ "$match": {
"status": "Closed",
"lines": { "$elemMatch": {
"status": "Closed",
"deliveryMethod": "Tech Delivers"
}},
"$or": [
{ "date": {
"$gte": new Date("2016-01-01"),
"$lt": new Date("2016-04-01")
}},
{ "date": {
"$gte": new Date("2016-04-01"),
"$lt": new Date("2016-08-01")
}}
]
}},
// Pre-filter the array content to matching elements
{ "$redact": {
"$cond": {
"if": {
"$and": [
{ "$eq": [ "$status", "Closed" ] },
{ "$eq": [
{ "$ifNull": ["$deliveryMethod", "Tech Delivers" ] },
"Tech Delivers"
]
},
"then": "$$DESCEND",
"else": "$$PRUNE"
}
}},
// Unwind the array
{ "$unwind": "$lines" },
// Then group on the productline values within the array
{ "$group":{
"_id": "$lines.productLine",
"countA": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date": new Date("2016-01-01") ] },
{ "$lt": [ "$date", new Date("2016-04-01") ] }
]},
1,
0
]
}
},
"countB": {
"$sum": {
"$cond": [
{ "$and": [
{ "$gte": [ "$date", new Date("2016-04-01") ] },
{ "$lt": [ "$date", new Date("2016-08-01") ] }
]},
1,
0
]
}
}
}}
])
注意到有趣的小$ifNull
在那里这是由于需要到$$DESCEND
的递归性质,由于全部级别的文件被检查,包括“顶级”文件,然后“下降ing“到后续的数组和成员甚至嵌套的对象中。由于先前的顶级字段的查询选择标准,“状态”字段存在并且具有“关闭”的值,但是当然没有称为“deliveryMethod”的“顶级”元素,因为它仅在数组内元素。
,基本上是“照顾”,那么需要使用$redact
这样的时候要取,如果结构如果文件不允许这样的条件,那么它是不是一个真正的选择,所以恢复处理$unwind
然后$match
代替。
但是在可能的情况下,使用这些方法优先于$unwind
,然后使用$match
处理,因为它可以节省大量时间,并通过使用更新的技术来使用更少的资源。
您是否认为可以发布一些示例文档以便我可以尝试? :3谢谢! – Tacocat