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