有没有一种方法可以投影一个范围内的最大值,然后仅在一个聚合中从这个最大值开始在新范围内查找文档?
Is there a way to project max value in a range then finding documents within a new range starting at this max value in just one aggregate?
给定 Mongo 集合中的以下数据:
{
_id: "1",
dateA: ISODate("2021-12-31T00:00.000Z"),
dateB: ISODate("2022-01-11T00:00.000Z")
},
{
_id: "2",
dateA: ISODate("2022-01-02T00:00.000Z"),
dateB: ISODate("2022-01-08T00:00.000Z")
},
{
_id: "3",
dateA: ISODate("2022-01-03T00:00.000Z"),
dateB: ISODate("2022-01-05T00:00.000Z")
},
{
_id: "4",
dateA: ISODate("2022-01-09T00:00.000Z"),
dateB: null
},
{
_id: "5",
dateA: ISODate("2022-01-11T00:00.000Z"),
dateB: ISODate("2022-01-11T00:00.000Z")
},
{
_id: "6",
dateA: ISODate("2022-01-12T00:00.000Z"),
dateB: null
}
并给出以下范围:
ISODate("2022-01-01T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
我想在给定范围内找到所有带有 dateA
的值,然后我想从最大 dateB
值开始缩小范围,最后获取所有不包含的文档dateB
.
简历中:
我将从范围开始
ISODate("2022-01-01T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
然后换范围
ISODate("2022-01-08T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
然后用
找
dateB: null
最后,结果将是
的文档
_id: "4"
有没有办法在一个聚合中找到带有 _id: "4"
的文档?
我知道如何使用 2 个查询以编程方式执行此操作,但主要目标是只对数据库发出一个请求。
您可以使用$max
先找到maxDateB。然后执行 self $lookup
以应用 $match
并找到 doc _id: "4"
.
db.collection.aggregate([
{
$match: {
dateA: {
$gte: ISODate("2022-01-01"),
$lt: ISODate("2022-01-10")
}
}
},
{
"$group": {
"_id": null,
"maxDateB": {
"$max": "$dateB"
}
}
},
{
"$lookup": {
"from": "collection",
"let": {
start: "$maxDateB",
end: ISODate("2022-01-10")
},
"pipeline": [
{
$match: {
$expr: {
$and: [
{
$gte: [
"$dateA",
"$$start"
]
},
{
$lt: [
"$dateA",
"$$end"
]
},
{
$eq: [
"$dateB",
null
]
}
]
}
}
}
],
"as": "result"
}
},
{
"$unwind": "$result"
},
{
"$replaceRoot": {
"newRoot": "$result"
}
}
])
这是您
的Mongo Playground
假设匹配的初始 dateA
范围不是很大,这里是利用 $push
和 $filter
并避免命中 $lookup
阶段的替代方法:
db.foo.aggregate([
{$match: {dateA: {$gte: new ISODate("2022-01-01"), $lt: new ISODate("2022-01-10")} }},
// Kill 2 birds with one stone here. Get the max dateB AND prep
// an array to filter later. The items array will be as large
// as the match above but the output of this stage is a single doc:
{$group: {_id: null,
maxDateB: {$max: "$dateB" },
items: {$push: "$$ROOT"}
}},
{$project: {X: {$filter: {
input: "$items",
cond: {$and: [
// Each element of 'items' is passed as $$this so use
// dot notation to get at individual fields. Note that
// all other peer fields to 'items' like 'maxDateB' are
// in scope here and addressable using '$':
{$gt: [ "$$this.dateA", "$maxDateB"]},
{$eq: [ "$$this.dateB", null ]}
]}
}}
}}
]);
这会产生一个文档结果(我添加了一个额外的文档 _id 41 来测试超过 1 个文档的 null 相等性):
{
"_id" : null,
"X" : [
{
"_id" : "4",
"dateA" : ISODate("2022-01-09T00:00:00Z"),
"dateB" : null
},
{
"_id" : "41",
"dateA" : ISODate("2022-01-09T00:00:00Z"),
"dateB" : null
}
]
}
在此之后 $unwind
和 $replaceRoot
是可能的,但几乎没有必要这样做。
给定 Mongo 集合中的以下数据:
{
_id: "1",
dateA: ISODate("2021-12-31T00:00.000Z"),
dateB: ISODate("2022-01-11T00:00.000Z")
},
{
_id: "2",
dateA: ISODate("2022-01-02T00:00.000Z"),
dateB: ISODate("2022-01-08T00:00.000Z")
},
{
_id: "3",
dateA: ISODate("2022-01-03T00:00.000Z"),
dateB: ISODate("2022-01-05T00:00.000Z")
},
{
_id: "4",
dateA: ISODate("2022-01-09T00:00.000Z"),
dateB: null
},
{
_id: "5",
dateA: ISODate("2022-01-11T00:00.000Z"),
dateB: ISODate("2022-01-11T00:00.000Z")
},
{
_id: "6",
dateA: ISODate("2022-01-12T00:00.000Z"),
dateB: null
}
并给出以下范围:
ISODate("2022-01-01T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
我想在给定范围内找到所有带有 dateA
的值,然后我想从最大 dateB
值开始缩小范围,最后获取所有不包含的文档dateB
.
简历中:
我将从范围开始
ISODate("2022-01-01T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
然后换范围
ISODate("2022-01-08T00:00.000Z") .. ISODate("2022-01-10T00:00.000Z")
然后用
找dateB: null
最后,结果将是
的文档_id: "4"
有没有办法在一个聚合中找到带有 _id: "4"
的文档?
我知道如何使用 2 个查询以编程方式执行此操作,但主要目标是只对数据库发出一个请求。
您可以使用$max
先找到maxDateB。然后执行 self $lookup
以应用 $match
并找到 doc _id: "4"
.
db.collection.aggregate([
{
$match: {
dateA: {
$gte: ISODate("2022-01-01"),
$lt: ISODate("2022-01-10")
}
}
},
{
"$group": {
"_id": null,
"maxDateB": {
"$max": "$dateB"
}
}
},
{
"$lookup": {
"from": "collection",
"let": {
start: "$maxDateB",
end: ISODate("2022-01-10")
},
"pipeline": [
{
$match: {
$expr: {
$and: [
{
$gte: [
"$dateA",
"$$start"
]
},
{
$lt: [
"$dateA",
"$$end"
]
},
{
$eq: [
"$dateB",
null
]
}
]
}
}
}
],
"as": "result"
}
},
{
"$unwind": "$result"
},
{
"$replaceRoot": {
"newRoot": "$result"
}
}
])
这是您
的Mongo Playground假设匹配的初始 dateA
范围不是很大,这里是利用 $push
和 $filter
并避免命中 $lookup
阶段的替代方法:
db.foo.aggregate([
{$match: {dateA: {$gte: new ISODate("2022-01-01"), $lt: new ISODate("2022-01-10")} }},
// Kill 2 birds with one stone here. Get the max dateB AND prep
// an array to filter later. The items array will be as large
// as the match above but the output of this stage is a single doc:
{$group: {_id: null,
maxDateB: {$max: "$dateB" },
items: {$push: "$$ROOT"}
}},
{$project: {X: {$filter: {
input: "$items",
cond: {$and: [
// Each element of 'items' is passed as $$this so use
// dot notation to get at individual fields. Note that
// all other peer fields to 'items' like 'maxDateB' are
// in scope here and addressable using '$':
{$gt: [ "$$this.dateA", "$maxDateB"]},
{$eq: [ "$$this.dateB", null ]}
]}
}}
}}
]);
这会产生一个文档结果(我添加了一个额外的文档 _id 41 来测试超过 1 个文档的 null 相等性):
{
"_id" : null,
"X" : [
{
"_id" : "4",
"dateA" : ISODate("2022-01-09T00:00:00Z"),
"dateB" : null
},
{
"_id" : "41",
"dateA" : ISODate("2022-01-09T00:00:00Z"),
"dateB" : null
}
]
}
在此之后 $unwind
和 $replaceRoot
是可能的,但几乎没有必要这样做。