Chú thích cho những người đang tìm kiếm - Bá tước nước ngoài
Tốt hơn một chút so với câu trả lời ban đầu là thực sự sử dụng dạng mới hơn của $ tra cứu
từ MongoDB 3.6. Điều này thực sự có thể thực hiện "đếm" trong biểu thức "đường ống con" thay vì trả về "mảng" để lọc và đếm tiếp theo hoặc thậm chí sử dụng $unwind
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"let": { "id": "$_id" },
"pipeline": [
{ "$match": {
"originalLink": "",
"$expr": { "$eq": [ "$$id", "$_id" ] }
}},
{ "$count": "count" }
],
"as": "linkCount"
}},
{ "$addFields": {
"linkCount": { "$sum": "$linkCount.count" }
}}
])
Không phải câu hỏi ban đầu yêu cầu điều gì mà là một phần của câu trả lời dưới đây ở dạng tối ưu nhất hiện nay, tất nhiên là kết quả của $ lookup
được giảm xuống "số lượng phù hợp" chỉ thay vì "tất cả các tài liệu phù hợp".
Bản gốc
Cách chính xác để thực hiện việc này là thêm "linkCount"
vào $ group
giai đoạn cũng như $ first
trên bất kỳ trường bổ sung nào của tài liệu mẹ để có được dạng "số ít" như trạng thái "trước" $ unwind
đã được xử lý trên mảng là kết quả của $ lookup
:
Tất cả chi tiết
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$unwind": "$link" },
{ "$match": { "link.originalLink": "" } },
{ "$group": {
"_id": "$_id",
"partId": { "$first": "$partId" },
"link": { "$push": "$link" },
"linkCount": {
"$sum": {
"$size": {
"$ifNull": [ "$link.linkHistory", [] ]
}
}
}
}}
])
Sản xuất:
{
"_id" : ObjectId("594a6c47f51e075db713ccb6"),
"partId" : "f56c7c71eb14a20e6129a667872f9c4f",
"link" : [
{
"_id" : ObjectId("594b96d6f51e075db67c44c9"),
"originalLink" : "",
"emailGroupId" : ObjectId("594a6c47f51e075db713ccb6"),
"linkHistory" : [
{
"_id" : ObjectId("594b96f5f51e075db713ccdf")
},
{
"_id" : ObjectId("594b971bf51e075db67c44ca")
}
]
}
],
"linkCount" : 2
}
Group By partId
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$unwind": "$link" },
{ "$match": { "link.originalLink": "" } },
{ "$group": {
"_id": "$partId",
"linkCount": {
"$sum": {
"$size": {
"$ifNull": [ "$link.linkHistory", [] ]
}
}
}
}}
])
Sản xuất
{
"_id" : "f56c7c71eb14a20e6129a667872f9c4f",
"linkCount" : 2
}
Lý do bạn làm theo cách này với $ unwind
và sau đó là $ đối sánh
là do cách MongoDB thực sự xử lý đường ống khi được phát hành theo thứ tự đó. Đây là điều xảy ra với $ lookup
như được trình bày trong "giải thích"
đầu ra từ hoạt động:
{
"$lookup" : {
"from" : "link",
"as" : "link",
"localField" : "_id",
"foreignField" : "emailGroupId",
"unwinding" : {
"preserveNullAndEmptyArrays" : false
},
"matching" : {
"originalLink" : {
"$eq" : ""
}
}
}
},
{
"$group" : {
Tôi sẽ rời khỏi phần với $ group
trong đầu ra đó để chứng minh rằng hai giai đoạn đường ống khác "biến mất". Điều này là do chúng đã được "cuộn lại" vào $ tra cứu
giai đoạn đường ống như hình. Trên thực tế, đây là cách MongoDB giải quyết khả năng có thể vượt quá Giới hạn BSON do kết quả "kết hợp" của $ lookup
vào một mảng của tài liệu mẹ.
Bạn có thể viết luân phiên thao tác như sau:
Tất cả chi tiết
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$addFields": {
"link": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"linkCount": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"as": "l",
"in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
}
}
}
}}
])
Nhóm theo partId
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$addFields": {
"link": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"linkCount": {
"$sum": {
"$map": {
"input": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
},
"as": "l",
"in": { "$size": { "$ifNull": [ "$$l.linkHistory", [] ] } }
}
}
}
}},
{ "$unwind": "$link" },
{ "$group": {
"_id": "$partId",
"linkCount": { "$sum": "$linkCount" }
}}
])
Truy vấn nào có cùng đầu ra nhưng "khác" với truy vấn đầu tiên ở chỗ $ filter
ở đây được áp dụng "sau" TẤT CẢ kết quả của $ lookup
được trả về mảng mới của tài liệu mẹ.
Vì vậy, về mặt hiệu suất, thực sự sẽ hiệu quả hơn nếu làm theo cách đầu tiên cũng như khả năng di chuyển đối với các tập kết quả lớn có thể có "trước khi lọc", điều này sẽ phá vỡ Giới hạn BSON 16MB.
Lưu ý nhỏ cho những ai quan tâm, trong các bản phát hành MongoDB trong tương lai (có lẽ là 3.6 trở lên), bạn có thể sử dụng $ ReplaceRoot
thay vì $ addFields
với việc sử dụng $ mergeObjects
mới nhà điều hành đường ống. Ưu điểm của điều này là như một "khối", chúng ta có thể khai báo "đã lọc"
nội dung dưới dạng một biến qua $ let
, có nghĩa là bạn không cần phải viết cùng một $ filter
"hai lần":
db.emailGroup.aggregate([
{ "$lookup": {
"from": "link",
"localField": "_id",
"foreignField": "emailGroupId",
"as": "link"
}},
{ "$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$$ROOT",
{ "$let": {
"vars": {
"filtered": {
"$filter": {
"input": "$link",
"as": "l",
"cond": { "$eq": [ "$$l.originalLink", "" ] }
}
}
},
"in": {
"link": "$$filtered",
"linkCount": {
"$sum": {
"$map": {
"input": "$$filtered.linkHistory",
"as": "lh",
"in": { "$size": { "$ifNull": [ "$$lh", [] ] } }
}
}
}
}
}}
]
}
}}
])
Tuy nhiên, cách tốt nhất để làm như vậy $ lookup
hoạt động "vẫn" tại thời điểm này bằng cách sử dụng $ unwind
thì $ match
mẫu, cho đến khi bạn có thể cung cấp đối số truy vấn cho $ tra cứu
trực tiếp.