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

Condition de correspondance des membres du tableau de nombre agrégé

L'erreur est due au fait qu'il ne s'agit plus d'un tableau après avoir $unwind et donc plus un argument valide pour $size .

Vous semblez essayer de "fusionner" quelques réponses existantes sans comprendre ce qu'elles font. Ce que vous voulez vraiment ici, c'est $filter et $size

db.collection.aggregate([
  { "$project": {
    "total": {
      "$size": {
        "$filter": {
          "input": "$Array",
          "cond": { "$eq": [ "$$this.field1", "a" ] }
        }
      }
    }
  }}
])

Ou "réinventez la roue" en utilisant $reduce :

db.collection.aggregate([
  { "$project": {
    "total": {
      "$reduce": {
        "input": "$Array",
        "initialValue": 0,
        "in": {
          "$sum": [
            "$$value", 
            { "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
        }
      }
    }
  }}
])

Ou pour ce que vous essayiez de faire avec $unwind , vous avez en fait $group à nouveau pour "compter" le nombre de correspondances :

db.collection.aggregate([
  { "$unwind": "$Array" },
  { "$match": { "Array.field1": "a" } },
  { "$group": {
    "_id": "$_id",
    "total": { "$sum": 1 }
  }}
])

Les deux premières formes sont "optimales" pour les environnements MongoDB modernes. Le formulaire final avec $unwind et $group est une construction "héritée" qui n'a vraiment pas été nécessaire pour ce type d'opération depuis MongoDB 2.6, mais avec quelques opérateurs légèrement différents.

Dans ces deux premiers, nous comparons essentiellement le field1 valeur de chaque élément du tableau alors qu'il s'agit toujours d'un tableau. Les deux $filter et $reduce sont des opérateurs modernes conçus pour fonctionner avec une baie existante en place. La même comparaison est faite sur chacun en utilisant l'agrégation $eq opérateur qui renvoie une valeur booléenne selon que les arguments donnés sont "égaux" ou non. Dans ce cas sur chaque membre du tableau à la valeur attendue de "a" .

Dans le cas de $filter , le tableau reste en fait intact à l'exception de tous les éléments qui ne remplissent pas la condition fournie dans "cond" sont supprimés du tableau. Puisque nous avons toujours un "tableau" en sortie, nous pouvons alors utiliser le $size pour mesurer le nombre d'éléments de tableau restants après le traitement de cette condition de filtre.

Le $reduce d'autre part travaille à travers les éléments du tableau et fournit une expression sur chaque élément et une valeur "accumulateur" stockée, que nous avons initialisée avec "initialValue" . Dans ce cas, le même $eq le test est appliqué dans le $cond opérateur. C'est un "ternaire" ou if/then/else opérateur conditionnel qui permet à une expression testée qui retourne une valeur booléenne de retourner le then valeur si true ou le else valeur si false .

Dans cette expression, nous renvoyons 1 ou 0 respectivement et fournir le résultat global de l'ajout de cette valeur renvoyée et de l'"accumulateur" actuel "$$value" avec le $sum opérateur pour les additionner.

Le formulaire final utilisé $unwind sur le tableau. En réalité, cela déconstruit les membres du tableau pour créer un "nouveau document" pour chaque membre du tableau et ses champs parents associés dans le document d'origine. Cela "copie" efficacement le document principal pour chaque membre du tableau.

Une fois que vous avez $unwind la structure des documents est changée en une forme "plus plate". C'est pourquoi vous pouvez ensuite faire le $match étape du pipeline pour supprimer les documents sans correspondance.

Cela nous amène à $group qui s'applique à "rassembler" toutes les informations liées à une clé commune. Dans ce cas c'est le _id champ du document original, qui a bien sûr été copié dans chaque document produit par le $unwind . Comme nous revenons à cette "clé commune" en tant que document unique, nous pouvons "compter" les "documents" restants extraits du tableau en utilisant le $sum accumulateur.

Si nous voulions récupérer le "tableau" restant, vous pouvez $push et reconstruisez le tableau avec uniquement les membres restants :

  { "$group": {
    "_id": "$_id",
    "Array": { "$push": "$Array" },
    "total": { "$sum": 1 }
  }}

Mais bien sûr au lieu d'utiliser $size dans une autre étape du pipeline, nous pouvons simplement encore "compter" comme nous l'avons déjà fait avec le $sum