obtenir des moyennes en utilisant l'agrégation mongodb à partir d'un tableau contenant des objets

Dorriz

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

Mickl

Oui, c'est possible avec $ réduire, mais il y a deux mises en garde importantes:

  • à l'intérieur de $ let lorsque vous définissez une varssection, 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
  • $reduceexpose la $$valuevariable 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 modifier

Ensuite, 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 $letdéfinit ici une étape d'algorithme:

  • en utilisant $ arrayElemAt pour vérifier s'il existe un agrégat pour le document actuellement traité
  • en utilisant $ cond et $ arrayElemAt pour obtenir la valeur précédente du traitement en cours idou fournir la valeur par défaut
  • puis sous updatednous calculons de nouvelles valeurs comprenantaverage

pour renvoyer la valeur, nous devons considérer deux cas:

  • quand il n'y a pas de valeur précédente, nous ajoutons simplement le updateddocument au tableau actuel (en utilisant $ concatArrays )
  • quand nous devons "mettre à jour" l'agrégat existant, nous devons supprimer l'ancienne valeur et ajouter la nouvelle - $ slice peut être utilisé ici pour obtenir les documents avant et après l'actuel$$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.

modifier le
0

laisse moi dire quelques mots

0commentaires
connexionAprès avoir participé à la revue

Articles connexes

TOP liste

  1. 1

    Comment exécuter un fichier python avec des droits d'administrateur dans pycharm

  2. 2

    comment obtenir un objet de requête dans les tests unitaires de django?

  3. 3

    mongo kafka connect source

  4. 4

    Vérifier la longueur du nombre à partir du message, puis utiliser la valeur dans l'instruction

  5. 5

    comment convertir une chaîne en un tuple dateutil jour de la semaine sans utiliser eval

  6. 6

    Comment ajouter un texte dans un texte Python/Tkinter

  7. 7

    Aide de variable de débogage pprint jinja2

  8. 8

    Dans les modèles Hugo, comment vérifier la longueur du tableau de fichiers JSON?

  9. 9

    Impression de la longueur du chemin le plus court dans le labyrinthe

  10. 10

    Exécuter la requête externe pour chaque date obtenue à partir de la requête interne

  11. 11

    Recherche de dicton Jinja2 à l'aide d'une clé variable

  12. 12

    Algorithme: diviser de manière optimale une chaîne en 3 sous-chaînes

  13. 13

    Comment obtenir l'intégration contextuelle d'une phrase dans une phrase à l'aide de BERT ?

  14. 14

    définir une propriété pour chaque nœud dans neo4j

  15. 15

    Pourquoi cette requête Java échoue-t-elle? renvoyer 0 quand il y a des résultats

  16. 16

    Comment changer le navigateur par défaut en Microsoft Edge pour Jupyter Notebook sous Windows 10 ?

  17. 17

    Laravel 8: Attempt to read property "id" on null

  18. 18

    Comment obtenir tous les champs d'un objet (y compris sa superclasse), à l'aide de l'API Mirrors de Dart?

  19. 19

    Référencement des assemblys de structure .net 4.7 dans la solution .net core 2

  20. 20

    Microsoft.WebApplication.targets

  21. 21

    obtenir le nombre de marqueur affiché sur la carte

chaudétiquette

Archive