MongoDB
 sql >> Database >  >> NoSQL >> MongoDB

Condizione di corrispondenza dei membri dell'array di conteggio aggregato

L'errore è dovuto al fatto che non è più un array dopo che $unwind e quindi non è più un argomento valido per $size .

Sembra che tu stia tentando di "unire" un paio di risposte esistenti senza capire cosa stanno facendo. Quello che vuoi veramente qui è $filter e $size

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

Oppure "reinventare la ruota" utilizzando $reduce :

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

O per quello che stavi cercando di fare con $unwind , in realtà $group ancora per "contare" quante partite ci sono state:

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

Le prime due forme sono "ottimali" per i moderni ambienti MongoDB. Il modulo finale con $unwind e $group è un costrutto "legacy" che in realtà non è stato necessario per questo tipo di operazioni da MongoDB 2.6, sebbene con alcuni operatori leggermente diversi.

In questi primi due stiamo fondamentalmente confrontando il field1 valore di ogni elemento dell'array mentre è ancora un array. Entrambi $filter e $reduce sono operatori moderni progettati per funzionare con un array esistente. Lo stesso confronto viene eseguito su ciascuno utilizzando l'aggregazione $eq operatore che restituisce un valore booleano in base al fatto che gli argomenti forniti siano "uguali" o meno. In questo caso su ogni membro dell'array al valore previsto di "a" .

Nel caso di $filter , l'array rimane effettivamente intatto ad eccezione di eventuali elementi che non soddisfano la condizione fornita in "cond" vengono rimossi dall'array. Dal momento che abbiamo ancora un "array" come output, possiamo quindi utilizzare $size per misurare il numero di elementi dell'array rimasti dopo l'elaborazione di tale condizione di filtro.

Il $reduce d'altra parte lavora attraverso gli elementi dell'array e fornisce un'espressione su ciascun elemento e un valore "accumulatore" memorizzato, che abbiamo inizializzato con "initialValue" . In questo caso lo stesso $eq il test viene applicato all'interno di $cond operatore. Questo è un "ternario" o if/then/else operatore condizionale che consente a un'espressione testata che restituisce un valore booleano di restituire then valore quando true o l'else valore quando false .

In quell'espressione restituiamo 1 o 0 rispettivamente e fornire il risultato complessivo dell'aggiunta di quel valore restituito e dell'attuale "accumulatore" "$$value" con $sum operatore per sommarli.

Il modulo finale ha utilizzato $unwind sulla matrice. Ciò che in realtà fa è decostruire i membri dell'array per creare un "nuovo documento" per ogni membro dell'array e i relativi campi padre nel documento originale. Questo effettivamente "copia" il documento principale per ogni membro dell'array.

Una volta $unwind la struttura dei documenti viene modificata in una forma "più piatta". Questo è il motivo per cui puoi quindi eseguire il successivo $match fase di pipeline per rimuovere i documenti non corrispondenti.

Questo ci porta a $group che viene applicato per "riunire" tutte le informazioni relative a una chiave comune. In questo caso è il _id campo del documento originale, che è stato ovviamente copiato in ogni documento prodotto da $unwind . Tornando a questa "chiave comune" come un unico documento, possiamo "contare" i restanti "documenti" estratti dall'array utilizzando $sum accumulatore.

Se volessimo indietro l'"array" rimanente, puoi $push e ricostruisci l'array con solo i membri rimanenti:

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

Ma ovviamente invece di usare $size in un'altra fase della pipeline, possiamo semplicemente "contare" come abbiamo già fatto con $sum