Stu*_*Man 5 mongoose mongodb aggregation-framework
我在具有此架构的 MongoDB 集合中有一些文档:
{
"_id": {
"$oid": "60c1e8e318afd80016ce58b1"
},
"searchPriority": 1,
"isLive": false,
"vehicleCondition": "USED",
"vehicleDetails": {
"city": "Delhi"
}
},
{
"_id": {
"$oid": "60c1f2f418afd80016ce58b5"
},
"searchPriority": 2,
"isLive": false,
"vehicleCondition": "USED",
"vehicleDetails": {
"city": "Delhi"
}
},
{
"_id": {
"$oid": "60cb429eadd33c00139d2be7"
},
"searchPriority": 1,
"isLive": false,
"vehicleCondition": "USED",
"vehicleDetails": {
"city": "Gurugram"
}
},
{
"_id": {
"$oid": "60c21be618afd80016ce5905"
},
"searchPriority": 2,
"isLive": false,
"vehicleCondition": "USED",
"vehicleDetails": {
"city": "New Delhi"
}
},
{
"_id": {
"$oid": "60e306d29e452d00134b978f"
},
"searchPriority": 3,
"isLive": false,
"vehicleCondition": "USED",
"vehicleDetails": {
"city": "New Delhi"
}
}
Run Code Online (Sandbox Code Playgroud)
vehicleCondition可以是NEWor USED,isLive可以是trueor ,false并且searchPriority是 1 到 3 之间的一个整数。(数字越小意味着它在搜索结果中应该越高)
在这里,除了_id其他字段都不是唯一的。我在isLive,vehicleDetails.city和上创建了一个复合索引 searchPriority。
在我的应用程序中,我将执行一些这种形式的查询:
isLiveis true、vehicleDetails.cityisDelhi或New DelhiorGurugram和vehicleConditionis
USED(or NEW)。为此,我可以执行这样的查找查询:
db.collection.find({"isLive": true, "vehicleDetails.city": { $in: [ "Gurugram", "Delhi", "New Delhi" ] }, "vehicleCondition": "USED" }, {})
Run Code Online (Sandbox Code Playgroud)
我希望此查询的结果按以下顺序排序:
$in查找查询中 arrray 中第一个城市的所有汽车,具有最低优先级$in在查找查询中属于第一个城市的所有汽车,具有第二低的优先级$in查找查询中 arrray 中第一个城市的所有汽车,具有第三低的优先级$in在查找查询中属于第二个城市的所有汽车,具有最低优先级$in在查找查询中属于第二个城市的所有汽车,具有第二低的优先级$in在查找查询中属于第二个城市的所有汽车,具有第三低的优先级 在查找查询中属于第三个城市的所有汽车$in,具有最低的优先级$in在查找查询中属于第三个城市的所有汽车,具有第二低的优先级$in在查找查询中属于第三个城市的所有汽车,具有第三低的优先级我怎样才能做到这一点?由于此查询返回的文档数量可能非常大,因此我将使用分页来限制返回文档的数量。这个额外的要求对这个问题的可能解决方案有什么影响吗?
因此,我已阅读其他答案(提供了技术解决方案),但是根据您的评论和请求,它不合适。
所以首先aggregate在这里使用虽然在技术上解决了问题,但还是存在一些问题。
正如您提到的,查询可以有大量的文档匹配,与该find方法不同,聚合管道确实会将所有文档加载到内存中,这将创造性地导致性能问题,我还看到您提到了有关没有索引的内容。这将导致对每个 API 调用进行“集合”扫描。
我建议你做的是:
首先,您绝对必须建立一个复合索引,isLive, vehicleCondition, "vehicleDetails.city"以防万一您没有复合索引。这对于大规模使用来说是必须的。
现在我们已经解决了这个问题,我建议您将调用分成几个部分,我将粘贴一些 puesdo 代码,这些代码可能看起来有点到处都是,但我确实相信这是您可以实现的最佳方法使用 Mongo 因为每个查询都应该通过使用先前构建的索引来提高效率。
我将简要解释该方法,我们希望能够独立于其他城市查询每个城市,这样我们就可以使用“自定义排序”功能,而无需将所有匹配项加载到内存中。
为此,我们需要知道每个城市需要“跳过”和“限制”多少,例如城市#2(德里)限制将是(限制 - 城市#1(古鲁格拉姆)匹配)。
这是伪代码,我故意让它简单,这样就可以理解了。不过,我会在最后添加一些想法来进行一些基本的改进。
let limit = 10; // determined by req?
const skip = 0; // determined by req?
const cities = ['Gurugram', 'Delhi', 'New Delhi'];
// we need this to resolve the proper skip / limit. the last city is not relevant.
const countPromises = [];
for (let i = 0; i < cities.length - 1; i++) {
countPromises.push(db.collection.countDocuments({
'isLive': true,
'vehicleDetails.city': cities[i],
'vehicleCondition': 'USED',
}));
}
await Promise.all(countPromises);
// first city initial skip
const citySkips = [skip];
for (let i = 0; i < countPromises.length - 1; i++) {
// if we have x results in the first city then we need to skip-x skipping for the next city.
citySkips.push(Math.max(skip - countPromises[0], 0));
}
let finalResults = [];
for (let i = 0; i < cities.length; i++) {
// assuming we skip over ALL city i results.
if (citySkips[i] >= countPromises[i]) {
continue;
}
const cityLimit = limit - finalResults.length;
if (cityLimit <= 0) {
break;
}
const cityResults = await db.collection.find({
'isLive': true,
'vehicleDetails.city': cities[i],
'vehicleCondition': 'USED',
}).sort({ sortPriority: 1 }).skip(citySkips[i]).limit(cityLimit);
finalResults = finalResults.concat(cityResults);
}
Run Code Online (Sandbox Code Playgroud)
好的,您可以进行可能的改进:
countDocuments确定每个城市skip所需的部分。limitfor循环可以类似于Promise.all计数以加速结果。同样,如果城市数量永远不会太多,这可能是一个很好的解决方案。| 归档时间: |
|
| 查看次数: |
126 次 |
| 最近记录: |