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

Concatena i valori di stringa nell'array in un singolo campo in MongoDB

Nelle versioni Modern MongoDB puoi. Non puoi ancora applicare "direttamente" un array a $concat , tuttavia puoi usare $reduce per lavorare con gli elementi dell'array e produrre questo:

db.collection.aggregate([
  { "$addFields": {
    "values": { 
      "$reduce": {
        "input": "$values",
        "initialValue": "",
        "in": {
          "$cond": {
            "if": { "$eq": [ { "$indexOfArray": [ "$values", "$$this" ] }, 0 ] },
            "then": { "$concat": [ "$$value", "$$this" ] },
            "else": { "$concat": [ "$$value", "_", "$$this" ] }
          }    
        }
      }        
    }
  }}
])

Combinando ovviamente con $indexOfArray per non "concatenare" con il "_" underscore quando è il "primo" indice dell'array.

Anche il mio "desiderio" aggiuntivo ha ricevuto una risposta con $sum :

db.collection.aggregate([
  { "$addFields": {
    "total": { "$sum": "$items.value" }
  }}
])

Questo tipo di problema viene sollevato un po' in generale con gli operatori di aggregazione che accettano una matrice di elementi. La distinzione qui è che significa un "array" di "aguments" forniti nella rappresentazione codificata in contrapposizione a un "elemento array" presente nel documento corrente.

L'unico modo in cui puoi davvero eseguire il tipo di concatenazione di elementi all'interno di un array presente nel documento è eseguire una sorta di opzione JavaScript, come con questo esempio in mapReduce:

db.collection.mapReduce(
    function() {
        emit( this._id, { "values": this.values.join("_") } );
    },
    function() {},
    { "out": { "inline": 1 } }
)

Ovviamente, se in realtà non stai aggregando nulla, forse l'approccio migliore è semplicemente eseguire l'operazione di "unione" all'interno del codice client durante l'elaborazione dei risultati della query. Ma se deve essere utilizzato per qualche scopo tra i documenti, mapReduce sarà l'unico posto in cui puoi usarlo.

Potrei aggiungere che "per esempio" mi piacerebbe che qualcosa del genere funzionasse:

{
    "items": [
        { "product": "A", "value": 1 },
        { "product": "B", "value": 2 },
        { "product": "C", "value": 3 }
    ]
}

E in aggregato:

db.collection.aggregate([
    { "$project": {
        "total": { "$add": [
            { "$map": {
                "input": "$items",
                "as": "i",
                "in": "$$i.value"
            }}
        ]}
    }}
])

Ma non funziona in questo modo perché $add si aspetta argomenti invece di un array dal documento. Sospiro! :(. Parte del ragionamento "by design" per questo potrebbe essere sostenuto che "solo perché" è una matrice o "elenco" di valori singolari passati dal risultato della trasformazione non è "garantito" che quelli siano valori di tipo numerico singolare effettivamente "validi" che l'operatore si aspetta. Almeno non ai metodi di "controllo del tipo" attualmente implementati.

Ciò significa che per ora dobbiamo ancora farlo:

db.collection.aggregate([
   { "$unwind": "$items" },
   { "$group": {
       "_id": "$_id",
        "total": { "$sum": "$items.value" }
   }}
])

Inoltre, purtroppo, non ci sarebbe modo di applicare un tale operatore di raggruppamento per concatenare le stringhe.

Quindi puoi sperare in una sorta di cambiamento su questo, o sperare in qualche cambiamento che consenta di modificare una variabile con ambito esterno nell'ambito di una $map operazione in qualche modo. Meglio ancora un nuovo $join anche l'operazione sarebbe gradita. Ma questi non esistono al momento della scrittura e probabilmente non lo saranno per qualche tempo a venire.