Du kan göra det med aggregeringsramverket som en "tvåstegs" operation. Vilket är att först samla objekten till en array via $push
inom en $group
pipeline och sedan för att använda $concat
med $reduce
på den producerade arrayen i slutprojektion:
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"]
}
}
}
}
}
}}
])
Vi tillämpar även $cond
här för att undvika att sammanfoga en tom sträng med ett kommatecken i resultaten, så det ser mer ut som en avgränsad lista.
FYI Det finns ett JIRA-problem SERVER-29339
som kräver $reduce
ska implementeras som ett ackumulatoruttryck
för att tillåta användning direkt i en $group
rörledningsstadiet. Det är inte troligt att det kommer att hända snart, men det skulle teoretiskt sett ersätta $push
i ovanstående och göra operationen till ett enda steg i pipeline. Exempel på föreslagen syntax handlar om JIRA-frågan.
Om du inte har $reduce
(kräver MongoDB 3.4) sedan är det bara att efterbehandla markören:
db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
]).map( doc =>
Object.assign(
doc,
{ "client_id": doc.client_id.join(",") }
)
)
Vilket sedan leder till det andra alternativet att göra detta med mapReduce
om du verkligen måste:
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 } }
)
Vilket naturligtvis utmatas i den specifika mapReduce
form av _id
och value
som en uppsättning nycklar, men det är i princip utgången.
Vi använder [].concat.apply([],values.map(...))
eftersom utdata från "reducer" kan vara en "avgränsad sträng" eftersom mapReduce
fungerar inkrementellt med stora resultat och därför kan utsignalen från reduceraren bli "ingång" på ett annat pass. Så vi måste förvänta oss att detta kan hända och behandla det därefter.