Vous pouvez le faire avec le cadre d'agrégation en tant qu'opération "en deux étapes". Qui consiste à accumuler d'abord les éléments dans un tableau via $push
avec un $group
pipeline, puis d'utiliser $concat
avec $reduce
sur le tableau produit en projection finale :
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"]
}
}
}
}
}
}}
])
Nous appliquons également $cond
ici pour éviter de concaténer une chaîne vide avec une virgule dans les résultats, afin qu'elle ressemble davantage à une liste délimitée.
Pour votre information, il y a un problème JIRA SERVER-29339
qui demande $reduce
à implémenter en tant qu'expression d'accumulateur
pour permettre son utilisation directement dans un $group
étape du pipeline. Il est peu probable que cela se produise de sitôt, mais cela remplacerait théoriquement $push
dans ce qui précède et faire de l'opération une seule étape du pipeline. L'exemple de syntaxe proposé se trouve sur le problème JIRA.
Si vous n'avez pas $reduce
(nécessite MongoDB 3.4) puis post-traitez simplement le curseur :
db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
]).map( doc =>
Object.assign(
doc,
{ "client_id": doc.client_id.join(",") }
)
)
Ce qui conduit ensuite à l'autre alternative consistant à le faire en utilisant mapReduce
si vous devez vraiment :
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 } }
)
Qui sort bien sûr dans le mapReduce
spécifique forme de _id
et value
comme jeu de clés, mais il s'agit essentiellement de la sortie.
Nous utilisons [].concat.apply([],values.map(...))
car la sortie du "réducteur" peut être une "chaîne délimitée" car mapReduce
fonctionne de manière incrémentielle avec de grands résultats et donc la sortie du réducteur peut devenir "entrée" lors d'une autre passe. Nous devons donc nous attendre à ce que cela puisse arriver et le traiter en conséquence.