我正在尝试对一些搜索结果进行评分/排名。
通常这是有效的:(userIds
这里是一组代表我们想要得分/排名的用户的ID,而SomeModel
包含与那些给我们排序标准的用户相关联的资产。)
SomeModel.aggregate([
{$match: {ownerId: {$in:userIds}}},
//Do stuff here to generate a score for the results. This works fine...
...
//at this point you'll have an array like this: {userId:<id>, score:<number>}[]
//push all those results into a field called 'origin' so we can add another
//field with the full set if IDs, merge them, and return them...
{$group: {
_id:0,
origin: {
$push: '$$ROOT'
}
}},
//Add back the full set with project, giving a score of 0 for each.
...
]).then( results => {
//enjoy your results that include all matched IDs plus the
//non-matched ones, who then get a score of 0.
});
只要第一个$match
返回一些结果,这一切都可以正常工作。如果那个$match
没有返回任何结果,那么$group
步骤似乎根本没有产生任何东西,就像我期望它至少返回的那样:
{
_id:0,
origin:[]
}
......但事实并非如此。如果我现在看看我的结果,我就得到[]
。
我试图改变那个$group
步骤来检查这样:
{$group: { //make these an array so we can merge it....
_id:0,
origin: {
$push :{
$cond:{
if: {$eq:[{$size : '$$ROOT'}, 0]},
then: [null],
else: '$$ROOT'
}
}
}
}},
...但后来我得到错误“$ size的参数必须是一个数组,但是类型为:object”。
这样做的正确方法是什么?为了清楚起见,我想创建一个容器对象,将对象中的结果保存为字段origin
中的数组。 (然后我能够将这些与我开始的完整列表连接起来并得到一个正确的排序列表)。
如果管道当前包含结果,则此方法有效,但如果不包含,则组步骤不执行任何操作。
期望的输入/输出:
我的问题特别是在$group
阶段,所以我将专注于此。
示例1:此示例与上面的代码一起正常工作。
输入:
[ {ownerId: <ID A>, score: 234}, {ownerId: <ID B>, score: 265}]
$group
期望的输出:
[{
_id:0,
origin:[ {ownerId: <ID A>, score: 234}, {ownerId: <ID B>, score: 265}]
}]
实际输出:匹配!
示例2:此示例与上述代码无法正常工作。
输入:
[]
$group
期望的输出:
[{
_id:0,
origin:[]
}]
实际产量:
[]
聚合管道的概念是,在执行管道时,MongoDB将运营商相互管道化。这里的“管道”采用Linux的含义:运算符的输出成为以下运算符的输入。每个运算符的结果是一个新的文档集合。在上面的Mongo执行管道如下:
collection | $match | $group | $project => result
这意味着如果$match
步骤与给定条件的管道中的任何文档都不匹配,Mongo将不会进一步管道,因此您得到一个空结果,因为管道以$match
结束,它不会更进一步。
然而,你可以做的是在$group
管道中注入条件,但是如果有大量的收集,这将导致一些性能损失。这是因为$group
步骤将处理集合中的所有文档,而不是通过使用$match
返回那些匹配条件作为第一优先级的文档来减少进入管道的文档数量。
通过这条路线,将您的$group
阶段视为第一个具有以下条件的管道步骤:
[
{ "$group": {
"_id": 0,
"origin": {
"$addToSet": {
"$cond": [
{ "$in": ["$ownerId", userIds] },
"$$ROOT", null
]
}
}
} },
{ "$project": {
"origin": {
"$filter": {
"input": "$origin",
"as": "el",
"cond": { "$ne": ["$$el", null] }
}
}
} }
]