J'ai un problème pour obtenir les scores de tous les étudiants qui ont répondu aux questions d'un test en utilisant l'approche du pipeline d'agrégation mongodb
Mon pipeline d'agrégation donne un tableau d'objets, qui est composé de chaque réponse des élèves aux questions du test.
Le pipeline serait quelque chose comme celui ci-dessous, mon exemple est simplifié à partir de mon problème réel. Fondamentalement, je groupe et pousse chaque tableau de questions pour chaque utilisateur dans le champ des scores. Ensuite, j'utilise réduire pour aplatir le champ des scores
{ $group: {
_id: {},
scores: { $push: "$questions" }
} },
{ $addFields: {
testScores: {
$reduce: {
input: "$scores",
initialValue: [ ],
in: { $concatArrays: [ "$$value", "$$this" ] }
}
}
} }
Le résultat ressemblerait à quelque chose comme:
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 }
]
Ma question est de savoir comment obtenir le score moyen de tous les scores de «questionOne» et de toutes les autres questions? Je ne peux pas dérouler mon tableau car j'ai un grand nombre de tests et il semble que mongoDb ne puisse pas dérouler un nombre suffisant sans renvoyer null pour le résultat de l'agrégation.
En JavaScript, j'utiliserais réduire, mais si je comprends bien, mongodb permet d'utiliser des variables en dehors de la fonction de réduction, mais pour autant que je sache, vous ne pouvez pas modifier la fonction de réduction, donc quelque chose de similaire à la fonction ci-dessous ne serait pas possible.
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
},{} }
Merci pour tous les conseils
Oui, c'est possible avec $ réduire, mais il y a deux mises en garde importantes:
vars
section, vous ne pouvez faire référence qu'aux variables définies dans les portées externes, mais vous ne pouvez pas définir plusieurs variables et faire référence les unes aux autres dans le même bloc - c'est pourquoi il doit y avoir beaucoup d'imbrication dans cette solution$reduce
expose la $$value
variable qui représente l'état actuel de l'agrégation. Le fait est que vous devez considérer cette variable comme immuable, ce qui signifie que vous pouvez vous y référer mais que vous ne pouvez pas la modifierEnsuite, vous pouvez essayer l'agrégation suivante:
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" }] } ] }
}
}
}
}
}
}
}
}
}
}
}
}
])
En fait chacun $let
définit ici une étape d'algorithme:
id
ou fournir la valeur par défautupdated
nous calculons de nouvelles valeurs comprenantaverage
pour renvoyer la valeur, nous devons considérer deux cas:
updated
document au tableau actuel (en utilisant $ concatArrays )$$index
Pour votre exemple, il produit:
{
"averages" : [
{
"id" : "questionOne",
"score" : 14,
"count" : 4,
"avg" : 3.5
},
{
"id" : "questionFifty",
"score" : 14,
"count" : 4,
"avg" : 3.5
}
]
}
Cet article est collecté sur Internet, veuillez indiquer la source lors de la réimpression.
En cas d'infraction, veuillez [email protected] Supprimer.
laisse moi dire quelques mots