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.