MongoDB
 sql >> Base de données >  >> NoSQL >> MongoDB

$ dérouler un objet dans le cadre d'agrégation

Il n'est pas possible de faire le type de calcul que vous décrivez avec le cadre d'agrégation - et ce n'est pas car il n'y a pas de $unwind méthode pour les non-tableaux. Même si les objets person:value étaient des documents dans un tableau, $unwind n'aiderait pas.

La fonctionnalité "regrouper par" (que ce soit dans MongoDB ou dans n'importe quelle base de données relationnelle) se fait sur la valeur d'un champ ou d'une colonne. Nous regroupons par valeur de champ et somme/moyenne/etc en fonction de la valeur d'un autre champ.

Un exemple simple est une variante de ce que vous suggérez, un champ d'évaluation ajouté à l'exemple de collection d'articles, mais pas sous la forme d'une carte de l'utilisateur à l'évaluation, mais sous la forme d'un tableau comme celui-ci :

{ title : title of article", ...
  ratings: [
         { voter: "user1", score: 5 },
         { voter: "user2", score: 8 },
         { voter: "user3", score: 7 }
  ]
}

Vous pouvez maintenant agréger cela avec :

[ {$unwind: "$ratings"},
  {$group : {_id : "$ratings.voter", averageScore: {$avg:"$ratings.score"} } } 
]

Mais cet exemple structuré comme vous le décrivez ressemblerait à ceci :

{ title : title of article", ...
  ratings: {
         user1: 5,
         user2: 8,
         user3: 7
  }
}

ou même ceci :

{ title : title of article", ...
  ratings: [
         { user1: 5 },
         { user2: 8 },
         { user3: 7 }
  ]
}

Même si vous pouviez $unwind cela, il n'y a rien à agréger ici. À moins que vous ne connaissiez la liste complète de toutes les clés possibles (utilisateurs), vous ne pouvez pas faire grand-chose avec cela. [*]

Un schéma de base de données relationnelle analogue à ce que vous avez serait :

CREATE TABLE T (
   user1: integer,
   user2: integer,
   user3: integer
   ...
);

Ce n'est pas ce qui serait fait, à la place nous ferions ceci :

CREATE TABLE T (
   username: varchar(32),
   score: integer
);

et maintenant nous agrégeons en utilisant SQL :

select username, avg(score) from T group by username;

Il existe une demande d'amélioration pour MongoDB qui pourrait vous permettre de le faire dans le cadre d'agrégation à l'avenir - la possibilité de projeter des valeurs sur des clés et vice versa. En attendant, il y a toujours map/reduce.

[*] Il existe une manière compliquée de procéder si vous connaissez toutes les clés uniques (vous pouvez trouver toutes les clés uniques avec une méthode similaire à celle-ci) mais si vous connaissez toutes les clés, vous pouvez également exécuter une séquence de requêtes du formulaire db.articles.find({"ratings.user1":{$exists:true}},{_id:0,"ratings.user1":1}) pour chaque utilisateurX qui renverra toutes leurs notes et vous pouvez les additionner et les moyenner assez simplement plutôt que de faire une projection très complexe que le cadre d'agrégation exigerait.