MongoDB
 sql >> Cơ Sở Dữ Liệu >  >> NoSQL >> MongoDB

Nhận số lượng phần tử đã lọc trong mảng từ $ lookup cùng với toàn bộ tài liệu

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.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Làm cách nào để tìm tổng các giá trị bên trong một mảng lồng nhau bằng cách sử dụng tính năng tổng hợp mongodb?

  2. Lược đồ lồng nhau Mongoose so với các mô hình lồng nhau

  3. Windows MongoDB - Đã cài đặt La bàn nhưng không tìm thấy La bàn trong hệ thống

  4. Pymongo - ValueError:NaTType không hỗ trợ utcoffset khi sử dụng insert_many

  5. MongoDB / Meteor / Xuất MONGO_URL sang các ứng dụng đã triển khai