Dies sollte den Zweck erfüllen. Ich habe es an Ihrem Eingabesatz getestet und absichtlich einige Dupe-Werte wie NYC
hinzugefügt in mehr als einem DESTINATION
erscheinen um sicherzustellen, dass es dedupliziert wurde (d. h. eindeutige Anzahl wie gewünscht). Kommentieren Sie zum Spaß alle Phasen aus und kommentieren Sie sie dann von oben nach unten aus, um die Auswirkungen jeder Phase der Pipeline zu sehen.
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"}} }
]);