El error se debe a que ya no es una matriz después de $unwind
y por lo tanto ya no es un argumento válido para $size
.
Parece que está intentando "fusionar" un par de respuestas existentes sin comprender lo que están haciendo. Lo que realmente quiere aquí es $filter
y $size
db.collection.aggregate([
{ "$project": {
"total": {
"$size": {
"$filter": {
"input": "$Array",
"cond": { "$eq": [ "$$this.field1", "a" ] }
}
}
}
}}
])
O "reinventar la rueda" usando $reduce
:
db.collection.aggregate([
{ "$project": {
"total": {
"$reduce": {
"input": "$Array",
"initialValue": 0,
"in": {
"$sum": [
"$$value",
{ "$cond": [{ "$eq": [ "$$this.field1", "a" ] }, 1, 0] }
}
}
}
}}
])
O por lo que intentabas hacer con $unwind
, en realidad $group
de nuevo para "contar" cuantas coincidencias hubo:
db.collection.aggregate([
{ "$unwind": "$Array" },
{ "$match": { "Array.field1": "a" } },
{ "$group": {
"_id": "$_id",
"total": { "$sum": 1 }
}}
])
Las dos primeras formas son las "óptimas" para los entornos MongoDB modernos. El formulario final con $unwind
y $group
es una construcción "heredada" que realmente no ha sido necesaria para este tipo de operación desde MongoDB 2.6, aunque con algunos operadores ligeramente diferentes.
En esos dos primeros, básicamente estamos comparando el field1
valor de cada elemento de la matriz mientras sigue siendo una matriz. Ambos $filter
y $reduce
son operadores modernos diseñados para trabajar con un arreglo existente en su lugar. Se hace la misma comparación en cada uno usando la agregación $eq
operador que devuelve un valor booleano basado en si los argumentos dados son "iguales" o no. En este caso, en cada miembro de la matriz al valor esperado de "a"
.
En el caso de $filter
, la matriz en realidad permanece intacta a excepción de los elementos que no cumplieron con la condición proporcionada en "cond"
se eliminan de la matriz. Como todavía tenemos una "matriz" como salida, podemos usar $size
operador para medir el número de elementos de matriz que quedan después de que se procesó esa condición de filtro.
El $reduce
por otro lado, funciona a través de los elementos de la matriz y proporciona una expresión sobre cada elemento y un valor "acumulador" almacenado, que inicializamos con "initialValue"
. En este caso lo mismo $eq
la prueba se aplica dentro de $cond
operador. Este es un "ternario" o if/then/else
operador condicional que permite una expresión probada que devuelve un valor booleano para devolver el then
valor cuando true
o el else
valor cuando false
.
En esa expresión devolvemos 1
o 0
respectivamente y proporcione el resultado general de sumar ese valor devuelto y el "acumulador" actual "$$value"
con el $sum
operador para agregarlos.
La forma final usó $unwind
en la matriz. Lo que esto hace en realidad es deconstruir los miembros de la matriz para crear un "nuevo documento" para cada miembro de la matriz y sus campos principales relacionados en el documento original. Esto efectivamente "copia" el documento principal para cada miembro de la matriz.
Una vez que $unwind
la estructura de los documentos se cambia a una forma "más plana". Esta es la razón por la que puede hacer el subsiguiente $match
etapa de canalización para eliminar los documentos no coincidentes.
Esto nos lleva a $group
que se aplica para "reunir" toda la información relacionada con una clave común. En este caso es el _id
campo del documento original, que por supuesto fue copiado en cada documento producido por $unwind
. A medida que volvemos a esta "clave común" como un solo documento, podemos "contar" los "documentos" restantes extraídos de la matriz usando $sum
acumulador.
Si quisiéramos recuperar la "matriz" restante, entonces puede $push
y reconstruya la matriz con solo los miembros restantes:
{ "$group": {
"_id": "$_id",
"Array": { "$push": "$Array" },
"total": { "$sum": 1 }
}}
Pero, por supuesto, en lugar de usar $size
en otra etapa de canalización, simplemente podemos "contar" como ya lo hicimos con $sum