Můžete to udělat s agregačním rámcem jako „dvoukrokovou“ operaci. Což je nejprve shromáždit položky do pole prostřednictvím $push
v rámci $group
potrubí a poté použít $concat
s $reduce
na vyrobeném poli ve finální projekci:
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"]
}
}
}
}
}
}}
])
Aplikujeme také $cond
zde, abyste se vyhnuli zřetězení prázdného řetězce čárkou ve výsledcích, takže to vypadá spíše jako seznam s oddělovači.
FYI Vyskytl se problém JIRA SERVER-29339
který požaduje $reduce
být implementován jako výraz akumulátoru
umožnit jeho použití přímo v $group
fáze potrubí. Není pravděpodobné, že by se to stalo v dohledné době, ale teoreticky by to nahradilo $push
ve výše uvedeném a učinit z operace jedinou etapu potrubí. Ukázka navrhované syntaxe se týká problému JIRA.
Pokud nemáte $reduce
(vyžaduje MongoDB 3.4), poté stačí zpracovat kurzor:
db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
]).map( doc =>
Object.assign(
doc,
{ "client_id": doc.client_id.join(",") }
)
)
Což pak vede k další alternativě, jak to udělat pomocí mapReduce
pokud opravdu musíte:
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 } }
)
Což je samozřejmě výstup ve specifickém mapReduce
ve tvaru _id
a value
jako sadu klíčů, ale je to v podstatě výstup.
Používáme [].concat.apply([],values.map(...))
protože výstupem „reduktoru“ může být „oddělený řetězec“, protože mapReduce
pracuje postupně s velkými výsledky, a proto se výstup reduktoru může stát "vstupem" při dalším průchodu. Musíme tedy očekávat, že se to může stát, a podle toho s tím zacházet.