TLDR;
Nowoczesne wersje powinny używać $reduce
z $setUnion
po początkowej $group
jak pokazano:
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" ] }
}
}
}}
])
Miałeś rację, znajdując $addToSet
operatora, ale podczas pracy z treścią w tablicy zazwyczaj musisz przetwarzać za pomocą $unwind
pierwszy. To "denormalizuje" wpisy tablicy i zasadniczo tworzy "kopię" dokumentu nadrzędnego z każdym wpisem tablicy jako pojedynczą wartością w polu. Właśnie tego potrzebujesz, aby uniknąć zachowań, które widzisz, bez użycia tego.
Twoje „liczenie” stanowi jednak interesujący problem, ale łatwo go rozwiązać za pomocą „podwójnego odwijania” po początkowym $group
operacja:
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"
}}
])
Ostatni bit z $project
jest tam również, ponieważ użyłem „tymczasowych” nazw dla każdego z pól na innych etapach potoku agregacji. Dzieje się tak, ponieważ istnieje optymalizacja w $project
który „kopiuje” pola z istniejącego etapu w kolejności, w jakiej pojawiły się „przed” dodaniem „nowych” pól do dokumentu.
W przeciwnym razie wynik wyglądałby następująco:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
Gdzie pola nie są w tej samej kolejności, jak mogłoby się wydawać. To naprawdę trywialne, ale dla niektórych ma znaczenie, więc warto wyjaśnić, dlaczego i jak sobie z tym poradzić.
Więc $unwind
wykonuje pracę, aby zachować elementy rozdzielone, a nie w tablicach, i wykonując $group
pierwszy pozwala uzyskać „liczbę” wystąpień klawisza „grupowanie”.
$first
użyty później operator „zachowuje” tę wartość „count”, ponieważ została „zduplikowana” dla każdej wartości obecnej w tablicy „tags”. To i tak ta sama wartość, więc to nie ma znaczenia. Po prostu wybierz jeden.