在使用mongodb聚合管道方法在测试中回答问题的所有学生获得分数时,我遇到了问题
我的聚合管道提供了一系列对象,该对象由每个学生对测试问题的答案组成。
管道将类似于下面的管道,我的示例从我的实际问题中简化了。基本上,我将每个用户的每个问题数组分组并将其推入“分数”字段。然后,我使用reduce来简化分数字段
{ $group: {
_id: {},
scores: { $push: "$questions" }
} },
{ $addFields: {
testScores: {
$reduce: {
input: "$scores",
initialValue: [ ],
in: { $concatArrays: [ "$$value", "$$this" ] }
}
}
} }
结果类似于:
testScores: [
{ id: 'questionOne' score: 1 },
{ id: 'questionOne', score: 3 },
{ id: 'questionOne', score: 8 },
{ id: 'questionOne' score: 2 },
....
{ id: 'questionFifty' score: 1 },
{ id: 'questionFifty', score: 3 },
{ id: 'questionFifty', score: 8 },
{ id: 'questionFifty' score: 2 }
]
我的问题是,如何获得“ questionOne”和所有其他问题的所有分数的平均分数? 我无法解散数组,因为我进行了大量测试,因此mongoDb似乎无法解散足够数量的数据,而不会为聚合结果返回null。
在JavaScript中,我将使用reduce,但据我所知,mongodb允许在reduce函数之外使用vars,但据我所知,您无法修改reduce函数,因此不可能实现与下面的函数类似的内容。 / p>
myArray.reduce((acc, next){
if(acc[next.id]}{
acc[next.id].score += next.score
acc[next.id].count+= 1
acc[next].avg = acc[next.id].score/acc[next.id].count
}else{
acc[next.id].score = next.score
acc[next.id].count = 1
}
return acc
},{} }
感谢任何指针
答案 0 :(得分:0)
是的,$reduce可以实现,但有两个重要警告:
vars
中,您只能引用在外部作用域中定义的变量,但不能在同一块中定义多个变量并互相引用-这就是为什么在该解决方案中有很多嵌套$reduce
公开了$$value
变量,该变量表示聚合的当前状态。事实是,您应该将该变量视为 immutable ,这意味着您可以引用它,但不能对其进行修改然后您可以尝试以下聚合:
db.col.aggregate([
{
$project: {
averages: {
$reduce: {
input: "$testScores",
initialValue: [],
in: {
$let: {
vars: {
index: { $indexOfArray: [ "$$value.id", "$$this.id" ] }
},
in: {
$let: {
vars: {
prev: {
$cond: [ { $ne: [ "$$index", -1 ] }, { $arrayElemAt: [ "$$value", "$$index" ] }, { id: "$$this.id", score: 0, count: 0 } ]
}
},
in: {
$let: {
vars: {
updated: {
id: "$$prev.id",
score: { $add: [ "$$prev.score", "$$this.score" ] },
count: { $add: [ "$$prev.count", 1 ] },
avg: {
$divide: [ { $add: [ "$$prev.score", "$$this.score" ] }, { $add: [ "$$prev.count", 1 ] } ]
}
}
},
in: {
$cond: {
if: { $eq: [ "$$index", -1 ] },
then: { $concatArrays: [ "$$value", [ "$$updated" ] ] },
else: { $concatArrays: [ { $slice: [ "$$value", "$$index"] }, [ "$$updated" ], { $slice: [ "$$value", { $add: [ "$$index", 1 ] }, { $size: "$$value" }] } ] }
}
}
}
}
}
}
}
}
}
}
}
}
])
实际上每个$let
都在此处定义了算法步骤:
id
的先前值或提供默认值updated
下计算新值,包括average
要返回该值,我们应该考虑两种情况:
updated
文档附加到当前数组(使用$concatArrays)$$index
之前和之后的文档对于您的示例,它输出:
{
"averages" : [
{
"id" : "questionOne",
"score" : 14,
"count" : 4,
"avg" : 3.5
},
{
"id" : "questionFifty",
"score" : 14,
"count" : 4,
"avg" : 3.5
}
]
}