TLDR;
Съвременните версии трябва да използват $reduce
с $setUnion
след първоначалната $group
както е показано:
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" ] }
}
}
}}
])
Бяхте прав, когато открихте $addToSet
оператор, но когато работите със съдържание в масив, обикновено трябва да обработвате с $unwind
първо. Това "денормализира" записите в масива и по същество прави "копие" на родителския документ с всеки запис в масива като единична стойност в полето. Това е, което трябва, за да избегнете поведението, което виждате, без да го използвате.
Вашият „брой“ обаче представлява интересен проблем, но лесно се решава чрез използването на „двойно отвиване“ след първоначална $group
операция:
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"
}}
])
Този последен бит с $project
също е там, защото използвах "временни" имена за всяко от полетата в други етапи на конвейера за агрегиране. Това е така, защото има оптимизация в $project
че "копира" полетата от съществуващ етап в реда, в който вече са се появили, "преди" каквито и да е "нови" полета да бъдат добавени към документа.
В противен случай изходът ще изглежда така:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
Където полетата не са в същия ред, както може би си мислите. Наистина е тривиално, но има значение за някои хора, така че си струва да обясним защо и как да се справите.
Така че $unwind
върши работата, за да държи елементите разделени, а не в масиви, и изпълнява $group
first ви позволява да получите "броя" на поява на ключа "групиране".
$first
Операторът, използван по-късно, "запазва" тази стойност "count", тъй като току-що се "дублира" за всяка стойност, присъстваща в масива "tags". Така или иначе всичко е с еднаква стойност, така че няма значение. Просто изберете един.