MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Concat String nach Gruppe

Sie können dies mit dem Aggregations-Framework als "zweistufige" Operation tun. Zunächst werden die Elemente über $push in einem Array gesammelt mit einem $group Pipeline und verwenden Sie dann $concat mit $reduce auf dem produzierten Array in Endprojektion:

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"]
            }
          }
        }
      }
    }
  }}
])

Wir wenden auch $cond an hier, um zu vermeiden, dass ein leerer String in den Ergebnissen mit einem Komma verkettet wird, sodass es eher wie eine Liste mit Trennzeichen aussieht.

FYI Es gibt ein JIRA-Problem SERVER-29339 was nach $reduce fragt als Akkumulatorausdruck zu implementieren um die Verwendung direkt in einer $group zuzulassen Pipeline-Phase. Wahrscheinlich nicht in absehbarer Zeit, aber es würde theoretisch ersetzen $push im obigen und machen die Operation zu einer einzelnen Pipeline-Stufe. Die vorgeschlagene Beispielsyntax befindet sich im JIRA-Problem.

Wenn Sie $reduce nicht haben (erfordert MongoDB 3.4) dann einfach den Cursor nachbearbeiten:

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

Was dann zu der anderen Alternative führt, dies mit mapReduce wenn es wirklich sein muss:

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 } }
)

Was natürlich im spezifischen mapReduce ausgegeben wird Form von _id und Wert wie der Satz von Schlüsseln, aber es ist im Grunde die Ausgabe.

Wir verwenden [].concat.apply([],values.map(...)) denn die Ausgabe des „reducer“ kann ein „getrennter String“ sein, weil mapReduce arbeitet inkrementell mit großen Ergebnissen und daher kann die Ausgabe des Reduzierers bei einem weiteren Durchlauf zur "Eingabe" werden. Wir müssen also damit rechnen, dass dies passieren kann, und es entsprechend behandeln.