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

Come unire il campo dell'array nel documento nell'aggregazione Mongo

TLDR;

Le versioni moderne dovrebbero usare $reduce con $setUnion dopo il $group iniziale come mostrato:

db.collection.aggregate([
  { "$group": {
    "_id": { "Host": "$Host", "ArtId": "$ArtId" },
    "count": { "$sum": 1 },
    "tags": { "$addToSet": "$tags" }
  }},
  { "$addFields": {
    "tags": {
      "$reduce": {
        "input": "$tags",
        "initialValue": [],
        "in": { "$setUnion": [ "$$value", "$$this" ] }
      }
    }
  }}
])

Avevi ragione nel trovare il $addToSet operatore, ma quando si lavora con il contenuto in un array è generalmente necessario elaborare con $unwind primo. Questo "denormalizza" le voci dell'array ed essenzialmente crea una "copia" del documento padre con ciascuna voce dell'array come valore singolare nel campo. Questo è ciò di cui hai bisogno per evitare il comportamento che stai vedendo senza usarlo.

Il tuo "conteggio" pone però un problema interessante, ma facilmente risolvibile attraverso l'uso di un "doppio svolgimento" dopo un $group iniziale operazione:

db.collection.aggregate([
    // Group on the compound key and get the occurrences first
    { "$group": {
        "_id": { "Host": "$Host", "ArtId": "$ArtId" },
        "tcount": { "$sum": 1 },
        "ttags": { "$push": "$tags" }
    }},

    // Unwind twice because "ttags" is now an array of arrays
    { "$unwind": "$ttags" },
    { "$unwind": "$ttags" },

    // Now use $addToSet to get the distinct values        
    { "$group": {
        "_id": "$_id",
        "tcount": { "$first": "$tcount" },
        "tags": { "$addToSet": "$ttags" }
    }},

    // Optionally $project to get the fields out of the _id key
    { "$project": {
        "_id": 0,
        "Host": "$_id.Host",
        "ArtId": "$_id.ArtId",
        "count": "$tcount",
        "tags": "$ttags"
    }}
])

L'ultimo pezzo con $project è presente anche perché ho usato nomi "temporanei" per ciascuno dei campi in altre fasi della pipeline di aggregazione. Questo perché c'è un'ottimizzazione in $project che "copia" i campi da una fase esistente nell'ordine in cui sono già apparsi "prima" che eventuali "nuovi" campi vengano aggiunti al documento.

Altrimenti l'output sarebbe simile a:

{  "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }

Dove i campi non sono nello stesso ordine come potresti pensare. Davvero banale, ma per alcune persone è importante, quindi vale la pena spiegare perché e come gestirlo.

Quindi $unwind fa il lavoro per mantenere gli elementi separati e non in array, e facendo il $group innanzitutto permette di ottenere il "conteggio" delle occorrenze della chiave di "raggruppamento".

Il $first l'operatore utilizzato in seguito "mantiene" quel valore di "conteggio", poiché è appena stato "duplicato" per ogni valore presente nell'array "tags". È tutto lo stesso valore comunque, quindi non importa. Scegline uno.