Detta borde göra susen. Jag testade det på din inmatningsuppsättning och lade medvetet till några dupevärden som NYC
visas i mer än en DESTINATION
för att säkerställa att den blev de-duperad (dvs. distinkt antal som efterfrågats). För skojs skull, kommentera alla steg och sedan uppifrån och ned AVkommentera den för att se effekten av varje steg i pipelinen.
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"}} }
]);