TLDR;
Các bản phát hành hiện đại nên sử dụng $reduce
với $setUnion
sau $group
ban đầu như được hiển thị:
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" ] }
}
}
}}
])
Bạn đã đúng khi tìm $addToSet
nhưng khi làm việc với nội dung trong một mảng, bạn thường cần xử lý bằng $unwind
Đầu tiên. Điều này "khử chuẩn hóa" các mục nhập mảng và về cơ bản tạo "bản sao" của tài liệu mẹ với mỗi mục nhập mảng là một giá trị số ít trong trường. Đó là những gì bạn cần để tránh hành vi mà bạn đang thấy nếu không sử dụng nó.
Mặc dù "số lượng" của bạn đặt ra một vấn đề thú vị, nhưng dễ dàng giải quyết thông qua việc sử dụng "thư giãn kép" sau $group
ban đầu hoạt động:
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"
}}
])
Bit cuối cùng với $project
cũng ở đó bởi vì tôi đã sử dụng tên "tạm thời" cho từng trường trong các giai đoạn khác của quy trình tổng hợp. Điều này là do có sự tối ưu hóa trong $project
"sao chép" các trường từ vùng hiện có theo thứ tự chúng đã xuất hiện "trước khi" bất kỳ trường "mới" nào được thêm vào tài liệu.
Nếu không, đầu ra sẽ giống như sau:
{ "count":2 , "tags":[ "tag1", "tag2", "tag3" ], "Host": "abc.com", "ArtId": "123" }
Trường hợp các trường không theo thứ tự như bạn nghĩ. Thực sự thì nhỏ nhặt, nhưng nó quan trọng đối với một số người, rất đáng để giải thích tại sao và cách xử lý.
Vì vậy, $unwind
thực hiện công việc để giữ các mục được tách biệt và không nằm trong mảng và thực hiện $group
đầu tiên cho phép bạn nhận "số lượng" các lần xuất hiện của khóa "nhóm".
$first
toán tử được sử dụng sau đó "giữ" giá trị "đếm" đó, vì nó chỉ được "nhân đôi" cho mọi giá trị có trong mảng "thẻ". Dù sao thì tất cả đều có giá trị như nhau nên không thành vấn đề. Chỉ cần chọn một.