Това трябва да свърши работа. Тествах го на вашия входен набор и съзнателно добавих някои подвеждащи стойности като NYC
показване в повече от една DESTINATION
за да се гарантира, че е дедуплиран (т.е. различен брой, както е поискано). За забавление коментирайте всички етапи, след което ДЕкоментирайте отгоре надолу, за да видите ефекта от всеки етап от конвейера.
var id = "1";
c=db.foo.aggregate([
// Find a thing:
{$match: {"_id" : id}}
// Do the lookup into the objects collection:
,{$lookup: {"from" : "foo2",
"localField" : "objectsIds",
"foreignField" : "_id",
"as" : "objectResults"}}
// OK, so we've got a bunch of extra material now. Let's
// get down to just the metaDataMap:
,{$project: {x: "$objectResults.metaDataMap"}}
,{$unwind: "$x"}
,{$project: {"_id":0}}
// Use $objectToArray to get all the field names dynamically:
// Replace the old x with new x (don't need the old one):
,{$project: {x: {$objectToArray: "$x"}}}
,{$unwind: "$x"}
// Collect unique field names. Interesting note: the values
// here are ARRAYS, not scalars, so $push is creating an
// array of arrays:
,{$group: {_id: "$x.k", tmp: {$push: "$x.v"}}}
// Almost there! We have to turn the array of array (of string)
// into a single array which we'll subsequently dedupe. We will
// overwrite the old tmp with a new one, too:
,{$addFields: {tmp: {$reduce:{
input: "$tmp",
initialValue:[],
in:{$concatArrays: [ "$$value", "$$this"]}
}}
}}
// Now just unwind and regroup using the addToSet operator
// to dedupe the list:
,{$unwind: "$tmp"}
,{$group: {_id: "$_id", uniqueVals: {$addToSet: "$tmp"}}}
// Add size for good measure:
,{$addFields: {size: {"$size":"$uniqueVals"}} }
]);