sql >> Base de Datos >  >> NoSQL >> MongoDB

Condición de coincidencia de miembros de matriz de recuento agregado

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