从Mongo 5 开始,这是新的$setWindowFields 聚合运算符的完美用例:
// { day: ISODate("2019-04-01T00:00:00.000Z"), value: 10 } <=
// { day: ISODate("2019-04-02T00:00:00.000Z"), value: 10 }
// { day: ISODate("2019-04-03T00:00:00.000Z"), value: 15 } <=
// { day: ISODate("2019-04-04T00:00:00.000Z"), value: 15 }
// { day: ISODate("2019-04-05T00:00:00.000Z"), value: 15 }
// { day: ISODate("2019-04-06T00:00:00.000Z"), value: 10 } <=
db.collection.aggregate([
{ $setWindowFields: {
sortBy: { day: 1 },
output: { pair: { $push: "$value", window: { documents: [-1, "current"] } } }
}},
// { day: ISODate("2019-04-01T00:00:00Z"), value: 10, pair: [ 10 ] }
// { day: ISODate("2019-04-02T00:00:00Z"), value: 10, pair: [ 10, 10 ] }
// { day: ISODate("2019-04-03T00:00:00Z"), value: 15, pair: [ 10, 15 ] }
// { day: ISODate("2019-04-04T00:00:00Z"), value: 15, pair: [ 15, 15 ] }
// { day: ISODate("2019-04-05T00:00:00Z"), value: 15, pair: [ 15, 15 ] }
// { day: ISODate("2019-04-06T00:00:00Z"), value: 10, pair: [ 15, 10 ] }
{ $match: { $expr: { $or: [
{ $eq: [ { $size: "$pair" }, 1 ] }, // first doc doesn't have a previous doc
{ $ne: [ { $first: "$pair" }, { $last: "$pair" } ] }
]}}},
{ $unset: ["pair"] }
])
// { day: ISODate("2019-04-01T00:00:00Z"), value: 10 }
// { day: ISODate("2019-04-03T00:00:00Z"), value: 15 }
// { day: ISODate("2019-04-06T00:00:00Z"), value: 10 }
这个:
- 以
$setWindowFields 聚合阶段开始,该阶段添加了一个 pair 字段,表示当前文档的值和前一个文档的值 (output: { pair: { ... }}):
-
$setWindowFields 为给定文档提供其他文档的视图(window)
- 在我们的例子中是
"current" 文档和前一个"-1": window: { documents: [-1, "current"] }。
- 这样我们就可以在这个窗口中构建一个值数组:
$push: "$value"
- 请注意,我们已确保按天对文档进行排序:
sortBy: { day: 1 }。
- 然后:
- 第一个文档中的过滤器(其数组只有一个元素可重新标记):
{ $eq: [ { $size: "$pair" }, 1 ] }
- 如果它们的
pair 具有相同的值,则过滤掉以下文档:{ $ne: [ { $first: "$pair" }, { $last: "$pair" } ] }