考虑以下3个文件(简化):
{
"name" : "Alex Ham",
"opponents" : [
{
"location" : "west",
"position" : 5
},
{
"location" : "north",
"position" : 7
},
{
"location" : "east",
"position" : 2
}
]
},
{
"name" : "John Flex",
"opponents" : [
{
"location" : "north",
"position" : 9
},
{
"location" : "north",
"position" : 4
},
{
"location" : "south",
"position" : 2
}
]
},
{
"name" : "Adam Will",
"opponents" : [
{
"location" : "east",
"position" : 6
},
{
"location" : "south",
"position" : 8
}
]
}
我需要按照数组opponents
中的项目顺序匹配文档。我将有整数,如572,942,68等。整数的每个数字代表position
数组中的opponents
。
例如,对于572,我需要匹配第一个文档(Alex Ham),因为如果查看opponents
数组,项目序列及其position
字段为5,7,和2分别。
对于942,我要匹配第二个文档(John Flex),对于68,我要匹配第三个文档(Adam Will),依此类推。
考虑到拥有大量数据(几百万份文档)并考虑性能,我如何构建一个聚合管道以匹配上述案例的文档。
答案 0 :(得分:0)
请通过下面的管道解决问题。基本逻辑是$unwind同时保留索引,并使用索引使用公式position * (pow(10, size - index - 1))
来计算整数的位置值
[
{$project: {name: 1, opponents: 1, sz: {$size: '$opponents'}}},
{$unwind: {path: '$opponents', includeArrayIndex: 'i'}},
{$project: {name: 1, opponents: 1,
num: {$multiply: [{$pow: [10, {$subtract: [{$subtract: ['$sz', 1]}, '$i']}]}, '$opponents.position']}
}},
{$group: {
_id: '$_id',
num: {$sum: '$num'},
name: {$first: '$name'},
opponents: {$push: '$opponents'}
}},
{$match: {num: 572}},
]
答案 1 :(得分:0)
在这种情况下,我认为你不需要使用聚合。要通过聚合实现此任务,您需要遍历整个集合并将所有文档投影到包含可与输入匹配的值的新表单。尽管聚合速度很快,但仍然不够好。
相反,我建议将您的输入格式化为可以与文档匹配的表单:
const val = 572;
const arr = val.toString().split('');
const size = arr.length;
const selector = arr.map((v, i) => {
return {
[`opponents.${i}.position`]: parseInt(v, 10),
};
}).reduce((acc, cur) => {
return Object.assign(acc, cur);
}, {});
selector.opponents = {
$size: size,
};
console.log(selector);

现在使用这个新的选择器,您可以使用正常的.find
操作来获取文档:
collection.find(arr);