我试图在我的数据库中查询一组值,但不是自己获取值,而是希望在日期最接近的另一个文档(具有不同要求)的值和值的乘积。以下是我目前使用n + 1
查询的方式:
Model.find({
user: user1,
date: { $gte: start, $lte: end }
}, 'date value', (err, results) => {
results.forEach(e => {
Model.find({
user: user2,
date: { $gte: e.date }
}, 'value').sort({ date: 1 }).limit(1).exec((err, matched) => {
e.value *= matched[0].value;
});
});
});
所以我想要在与user1
相关联的日期范围内的值,每个日期乘以与user2
关联的最近文档(日期)的值。我们无法保证user1
和user2
的文档日期完全相同,因此我使用$gte
的组合,排序和限制来获取最接近(我知道它并不精确,因为可能比它早得多,但这已经足够了)。
我认为必须有办法用aggregate
来做到这一点。我正在考虑使用$lookup
来加入每个用户的相应文档中的值,但如果没有完全匹配的字段,我就不知道如何做到这一点。
我是否在正确的轨道上?必须有更好的方法来实现这一目标。
答案 0 :(得分:1)
您可以在3.6
中尝试以下聚合查询使用$lookup
let语法。
Model.aggregate([
{"$match":{
"user": user1,
"date": {"$gte":start, "$lte":end}
}},
{"$lookup":{
"from": collectionname, use collection name here
"let": {"date":"$date"},
"pipeline":[
{"$match":{
"user": user2,
"$expr":{"$gte":["$date","$$date"]}
}},
{"$sort":{"date": 1}},
{"$limit":1},
{"$project":{"value":1}}
],
"as": "lookup-data"
}},
{"$project":{
"date":1,
"value":{
"$multiply":[
"$value", {
"$let":{
"vars":{"lookupdata":{"$arrayElemAt":["$lookup-data",0]}},
"in":"$$lookupdata.value"
}
}
]
}
}}
])
答案 1 :(得分:1)
使用3.2查找语法是可能的,效率非常低:
db.collection.aggregate([
{$match: {user: user1, date:{$gte: start, $lte: end)}}},
{$addFields: {anotherUser: user2}},
{$lookup: {
from: "collection",
localField: "anotherUser",
foreignField: "user",
as: "anotherUser"
}},
{$project: {
user:1,
date:1,
value: {$let: {
vars: {
anotherUser: {$let: {
vars: {
all: {$filter: {
input: "$anotherUser",
as: "au",
cond: {$gte: ["$$au.date", "$date"]}
}}
},
in: { $let: {
vars: {
minDate: {$min: "$$all.date"}
},
in: { $arrayElemAt: [
{$filter: {
input: "$$all",
as: "su",
cond: {$eq: ["$$su.date", "$$minDate"]}
}},
0
]}
}}
}}
},
in: {$multiply: ["$value", "$$anotherUser.value"] }
}}
}}
])
$lookup
阶段会为user2
个文档的anotherUser
字段添加所有user1
个文档。然后按日期过滤,最终解析为单个值,但它仅在下一个$project
阶段发生。在大型数据集上$lookup
阶段很容易耗尽100MB limit。
我没有测试性能。如果按用户和日期有适当的索引,那么原始的多查询方法可能比这种管道更快。它绝对比聚合使用更少的内存,并且更具可读性/可测试性/可维护性。