Você pode fazer isso com a estrutura de agregação como uma operação de "duas etapas". Que é primeiro acumular os itens em um array via
$push
dentro de um $group
pipeline e, em seguida, usar $concat
com $reduce
no array produzido na projeção final: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"]
}
}
}
}
}
}}
])
Também aplicamos
$cond
aqui para evitar a concatenação de uma string vazia com uma vírgula nos resultados, para que pareça mais uma lista delimitada. Para sua informação, há um problema no JIRA SERVER-29339 que pede
$reduce
a ser implementado como uma expressão de acumulador
para permitir seu uso diretamente em um $group
estágio de tubulação. Não é provável que aconteça tão cedo, mas teoricamente substituiria $push
acima e tornar a operação um único estágio de pipeline. A sintaxe proposta de amostra está no problema do JIRA. Se você não tiver
$reduce
(requer MongoDB 3.4) então apenas post processe o cursor:db.collection.aggregate([
{ "$group": {
"_id": "$tag_id",
"client_id": { "$push": "$client_id" }
}},
]).map( doc =>
Object.assign(
doc,
{ "client_id": doc.client_id.join(",") }
)
)
O que leva à outra alternativa de fazer isso usando
mapReduce
se você realmente deve: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 } }
)
O que, obviamente, gera no
mapReduce
específico forma de _id
e value
como o conjunto de chaves, mas é basicamente a saída. Usamos
[].concat.apply([],values.map(...))
porque a saída do "redutor" pode ser uma "string delimitada" porque mapReduce
funciona incrementalmente com grandes resultados e, portanto, a saída do redutor pode se tornar "entrada" em outra passagem. Portanto, precisamos esperar que isso possa acontecer e tratá-lo de acordo.