TLDR;
Moderní verze by měly používat $reduce
pomocí $setUnion
za počáteční $group
jak je znázorněno:
db.collection.aggregate([
{ "$group": {
"_id": { "Host": "$Host", "ArtId": "$ArtId" },
"count": { "$sum": 1 },
"tags": { "$addToSet": "$tags" }
}},
{ "$addFields": {
"tags": {
"$reduce": {
"input": "$tags",
"initialValue": [],
"in": { "$setUnion": [ "$$value", "$$this" ] }
}
}
}}
])
Měli jste pravdu, když jste našli $addToSet
operátor, ale při práci s obsahem v poli obecně potřebujete zpracovat pomocí $unwind
za prvé. To "denormalizuje" položky pole a v podstatě vytvoří "kopii" nadřazeného dokumentu s každým záznamem pole jako singulární hodnotou v poli. To je to, co potřebujete, abyste se vyhnuli chování, které vidíte, aniž byste to použili.
Váš "počet" však představuje zajímavý problém, ale lze jej snadno vyřešit pomocí "dvojitého uvolnění" po úvodní $group
operace:
db.collection.aggregate([
// Group on the compound key and get the occurrences first
{ "$group": {
"_id": { "Host": "$Host", "ArtId": "$ArtId" },
"tcount": { "$sum": 1 },
"ttags": { "$push": "$tags" }
}},
// Unwind twice because "ttags" is now an array of arrays
{ "$unwind": "$ttags" },
{ "$unwind": "$ttags" },
// Now use $addToSet to get the distinct values
{ "$group": {
"_id": "$_id",
"tcount": { "$first": "$tcount" },
"tags": { "$addToSet": "$ttags" }
}},
// Optionally $project to get the fields out of the _id key
{ "$project": {
"_id": 0,
"Host": "$_id.Host",
"ArtId": "$_id.ArtId",
"count": "$tcount",
"tags": "$ttags"
}}
])
Poslední bit s $project
je tam také proto, že jsem použil "dočasné" názvy pro každé z polí v jiných fázích agregačního kanálu. Důvodem je optimalizace v $project
že „zkopíruje“ pole z existující fáze v pořadí, v jakém se již objevila „před“ přidáním jakýchkoli „nových“ polí do dokumentu.
Jinak by výstup vypadal takto:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
Kde pole nejsou ve stejném pořadí, jak si možná myslíte. Opravdu triviální, ale pro některé lidi na tom záleží, takže stojí za to vysvětlit proč a jak s tím zacházet.
Takže $unwind
dělá práci, aby položky zůstaly oddělené a ne v polích, a dělá $group
první vám umožňuje získat "počet" výskytů klíče "seskupení".
$first
operátor použitý později "uchová" tuto hodnotu "count", protože se právě "duplikoval" pro každou hodnotu přítomnou v poli "tags". Stejně má všechno stejnou hodnotu, takže na tom nezáleží. Stačí si vybrat jednu.