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

Concatenación de cadenas por grupo

Puede hacerlo con el marco de agregación como una operación de "dos pasos". Que consiste en acumular primero los elementos en una matriz a través de $push dentro de un $group tubería, y luego usar $concat con $reduce en la matriz producida en proyección final:

db.collection.aggregate([
  { "$group": {
    "_id": "$tag_id",
    "client_id": { "$push": "$client_id" }
  }},
  { "$addFields": {
    "client_id": {
      "$reduce": {
        "input": "$client_id",
        "initialValue": "",
        "in": {
          "$cond": {
            "if": { "$eq": [ "$$value", "" ] },
            "then": "$$this",
            "else": {
              "$concat": ["$$value", ",", "$$this"]
            }
          }
        }
      }
    }
  }}
])

También aplicamos $cond aquí para evitar concatenar una cadena vacía con una coma en los resultados, por lo que se parece más a una lista delimitada.

FYI Hay un problema de JIRA SERVER-29339 que pide $reduce para implementarse como una expresión acumulada para permitir su uso directamente en un $group etapa de tubería. No es probable que suceda pronto, pero teóricamente reemplazaría $push en lo anterior y hacer de la operación una sola etapa del oleoducto. La sintaxis propuesta de muestra es sobre el problema de JIRA.

Si no tiene $reduce (requiere MongoDB 3.4) luego simplemente publique el proceso del cursor:

db.collection.aggregate([
  { "$group": {
    "_id": "$tag_id",
    "client_id": { "$push": "$client_id" }
  }},
]).map( doc =>
  Object.assign(
    doc,
   { "client_id": doc.client_id.join(",") }
  )
)

Lo que luego lleva a la otra alternativa de hacer esto usando mapReduce si realmente debes:

db.collection.mapReduce(
  function() {
    emit(this.tag_id,this.client_id);
  },
  function(key,values) {
    return [].concat.apply([],values.map(v => v.split(","))).join(",");
  },
  { "out": { "inline": 1 } }
)

Que, por supuesto, genera en el mapReduce específico forma de _id y value como el conjunto de claves, pero es básicamente la salida.

Usamos [].concat.apply([],values.map(...)) porque la salida del "reductor" puede ser una "cadena delimitada" porque mapReduce funciona de manera incremental con grandes resultados y, por lo tanto, la salida del reductor puede convertirse en "entrada" en otra pasada. Por lo tanto, debemos esperar que esto pueda suceder y tratarlo en consecuencia.