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

Concatenar valores de cadena en una matriz en un solo campo en MongoDB

En las versiones Modern MongoDB puede hacerlo. Todavía no puede aplicar "directamente" una matriz a $concat , sin embargo, puede usar $reduce para trabajar con los elementos de la matriz y producir esto:

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, por supuesto, con $indexOfArray para no "concatenar" con el "_" guión bajo cuando es el "primer" índice de la matriz.

También mi "deseo" adicional ha sido respondido con $sum :

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

En general, esto se plantea un poco con los operadores de agregación que toman una serie de elementos. La distinción aquí es que significa una "matriz" de "argumentos" proporcionados en la representación codificada en oposición a un "elemento de matriz" presente en el documento actual.

La única forma en que realmente puede hacer el tipo de concatenación de elementos dentro de una matriz presente en el documento es hacer algún tipo de opción de JavaScript, como con este ejemplo en mapReduce:

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

Por supuesto, si en realidad no está agregando nada, entonces posiblemente el mejor enfoque sea simplemente hacer esa operación de "unión" dentro de su código de cliente en el procesamiento posterior de los resultados de su consulta. Pero si necesita usarse para algún propósito en varios documentos, entonces mapReduce será el único lugar donde podrá usarlo.

Podría añadir que "por ejemplo" me encantaría que algo así funcionara:

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

Y en conjunto:

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

Pero no funciona así porque $add espera argumentos en lugar de una matriz del documento. ¡Suspiro! :(. Parte del razonamiento "por diseño" para esto podría argumentarse que "solo porque" es una matriz o "lista" de valores singulares que se pasan del resultado de la transformación, no está "garantizado" que esos son en realidad valores de tipo numérico singular "válidos" que el operador espera. Al menos no en los métodos implementados actualmente de "verificación de tipo".

Eso significa que por ahora todavía tenemos que hacer esto:

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

Y, lamentablemente, tampoco habría forma de aplicar dicho operador de agrupación para concatenar cadenas.

Por lo tanto, puede esperar algún tipo de cambio en esto, o esperar algún cambio que permita modificar una variable de ámbito externo dentro del ámbito de un $map operación de alguna manera. Mejor aún, un nuevo $join La operación también sería bienvenida. Pero estos no existen al momento de escribir, y probablemente no existirán por algún tiempo.