We\'re trying to \'join\' an array of strings to a single string within an aggregation.
Given is the following dataset:
Collection 1:
{
id: 123
Starting Mongo 4.4
, the $group
stage has a new aggregation operator $accumulator allowing custom accumulations of documents as they get grouped:
// { "collectionId" : 1234, "name" : "Max" }
// { "collectionId" : 876, "name" : "Rob" }
// { "collectionId" : 1234, "name" : "Andy" }
db.collection.aggregate([
{ $group: {
_id: "$collectionId",
names: {
$accumulator: {
accumulateArgs: ["$name"],
init: function() { return [] },
accumulate: function(names, name) { return names.concat(name) },
merge: function(names1, names2) { return names1.concat(names2) },
finalize: function(names) { return names.join(",") },
lang: "js"
}
}
}}
])
// { "_id" : 876, "names" : "Rob" }
// { "_id" : 1234, "names" : "Max,Andy" }
The accumulator:
name
(accumulateArgs
)init
)accumulate
and merge
)finalize
)Sometimes it's easiest to use JavaScript:
db.getCollection('Collection1').aggregate([
{
$lookup:
{
from: 'Collection2',
localField: 'id',
foreignField: 'collection1_id',
as: 'col2'
}
}]).map((e) => ({
id: e.id,
field: e.field,
collection2: Array.isArray(e.col2) &&
e.col2.reduce((arr, el) => {
arr.push(el.name);
return arr;
}, []).join(', ')
}))
To flatten this array, you need to shift process to client.
mongo will provide some new flattening options in new edition, but afaik it will be arithmetic ones (avg, min, max....).
You were on the right track.
Just add $reduce over $concat in your $project
stage.
'collection2': {
'$reduce': {
'input': '$collection2',
'initialValue': '',
'in': {
'$concat': [
'$$value',
{'$cond': [{'$eq': ['$$value', '']}, '', ', ']},
'$$this']
}
}
}
Note: We use $cond to prevent a leading ,
in the concatenation.
You could also use $substrCP before $reduce
as an alternative to $cond
.