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

Cách tham gia vào hai bộ sưu tập bổ sung với các điều kiện

Điều bạn còn thiếu ở đây là $lookup tạo ra một "mảng" trong trường đầu ra được chỉ định bởi as trong các đối số của nó. Đây là khái niệm chung của "quan hệ" trong MongoDB, trong đó "quan hệ" giữa các tài liệu được biểu diễn dưới dạng "thuộc tính phụ" nằm "trong" chính tài liệu, là số ít hoặc "mảng" đối với nhiều tài liệu.

Vì MongoDB là "schemaless", giả định chung của $lookup là bạn có nghĩa là "nhiều" và do đó kết quả là "luôn luôn" là một mảng. Vì vậy, tìm kiếm "kết quả tương tự như trong SQL" thì bạn cần $unwind mảng đó sau $lookup . Cho dù đó là "một" hay "nhiều" thì cũng không có hậu quả gì, vì nó vẫn "luôn luôn" là một mảng:

db.getCollection.('tb1').aggregate([
  // Filter conditions from the source collection
  { "$match": { "status": { "$ne": "closed" } }},

  // Do the first join
  { "$lookup": {
    "from": "tb2",
    "localField": "id",
    "foreignField": "profileId",
    "as": "tb2"
  }},

  // $unwind the array to denormalize
  { "$unwind": "$tb2" },

  // Then match on the condtion for tb2
  { "$match": { "tb2.profile_type": "agent" } },

  // join the second additional collection
  { "$lookup": {
    "from": "tb3",
    "localField": "tb2.id",
    "foreignField": "id",
    "as": "tb3"
  }},

  // $unwind again to de-normalize
  { "$unwind": "$tb3" },

  // Now filter the condition on tb3
  { "$match": { "tb3.status": 0 } },

  // Project only wanted fields. In this case, exclude "tb2"
  { "$project": { "tb2": 0 } }
])

Ở đây, bạn cần lưu ý những điều khác mà bạn còn thiếu trong bản dịch:

Trình tự là "quan trọng"

Đường ống tổng hợp "diễn đạt ngắn gọn" hơn SQL. Trên thực tế, chúng tốt nhất được coi là "một chuỗi các bước" được áp dụng cho nguồn dữ liệu để đối chiếu và chuyển đổi dữ liệu. Tương tự tốt nhất cho điều này là các hướng dẫn dòng lệnh "đường ống", chẳng hạn như:

ps -ef  | grep mongod | grep -v grep | awk '{ print $1 }'

Nơi "đường ống" | có thể được coi là "giai đoạn đường ống" trong "đường ống" tổng hợp MongoDB.

Như vậy, chúng tôi muốn $match để lọc những thứ từ bộ sưu tập "nguồn" như thao tác đầu tiên của chúng tôi. Và điều này nói chung là thực tiễn tốt vì nó loại bỏ bất kỳ tài liệu nào không đáp ứng các điều kiện bắt buộc khỏi các điều kiện khác. Cũng giống như những gì đang xảy ra trong ví dụ về "đường dẫn dòng lệnh" của chúng tôi, trong đó chúng ta lấy "đầu vào" sau đó lấy "đường dẫn" đến grep để "loại bỏ" hoặc "bộ lọc".

Đường dẫn quan trọng

Điều tiếp theo bạn làm ở đây là "tham gia" qua $lookup . Kết quả là một "mảng" các mục từ "from" đối số tập hợp được so khớp bởi các trường được cung cấp để xuất ra trong "as" "đường dẫn trường" dưới dạng "mảng".

Đặt tên được chọn ở đây là quan trọng, vì bây giờ "tài liệu" từ tập hợp nguồn coi tất cả các mục từ "nối" hiện tồn tại tại đường dẫn đã cho đó. Để làm cho việc này dễ dàng, tôi sử dụng cùng một tên "bộ sưu tập" làm "nối" cho "đường dẫn" mới.

Vì vậy, bắt đầu từ "tham gia" đầu tiên, đầu ra là "tb2" và điều đó sẽ chứa tất cả các kết quả từ bộ sưu tập đó. Cũng có một điều quan trọng cần lưu ý với chuỗi $unwind sau và sau đó $match , về cách MongoDB thực sự xử lý truy vấn.

Một số chuỗi "thực sự" quan trọng

Vì "có vẻ như" có "ba" giai đoạn đường ống, là $lookup sau đó $unwind và sau đó $match . Nhưng trên thực tế, MongoDB thực sự làm một điều gì đó khác, điều này được chứng minh trong đầu ra của { "explain": true } được thêm vào .aggregate() lệnh:

    {
        "$lookup" : {
            "from" : "tb2",
            "as" : "tb2",
            "localField" : "id",
            "foreignField" : "profileId",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "profile_type" : {
                    "$eq" : "agent"
                }
            }
        }
    }, 
    {
        "$lookup" : {
            "from" : "tb3",
            "as" : "tb3",
            "localField" : "tb2.id",
            "foreignField" : "id",
            "unwinding" : {
                "preserveNullAndEmptyArrays" : false
            },
            "matching" : {
                "status" : {
                    "$eq" : 0.0
                }
            }
        }
    }, 

Vì vậy, ngoài điểm đầu tiên của "trình tự" áp dụng, bạn cần đặt $match tuyên bố ở đâu là cần thiết và làm điều "tốt nhất", điều này thực sự trở nên "thực sự quan trọng" với khái niệm "gia nhập". Điều cần lưu ý ở đây là chuỗi $lookup của chúng tôi sau đó $unwind và sau đó $match , thực sự được xử lý bởi MongoDB chỉ là $lookup các giai đoạn, với các hoạt động khác được "tập hợp" thành một giai đoạn đường ống cho mỗi giai đoạn.

Đây là điểm khác biệt quan trọng đối với các cách "lọc" kết quả thu được bằng $lookup . Vì trong trường hợp này, các điều kiện "truy vấn" thực tế trên "tham gia" từ $match được thực hiện trên bộ sưu tập để tham gia "trước khi" kết quả được trả về chính.

Điều này kết hợp với $unwind (được dịch thành unwinding ) như được hiển thị ở trên là cách MongoDB thực sự đối phó với khả năng "kết hợp" có thể dẫn đến việc tạo ra một mảng nội dung trong tài liệu nguồn khiến nó vượt quá giới hạn 16MB BSON. Điều này sẽ chỉ xảy ra trong trường hợp kết quả được kết hợp rất lớn, nhưng lợi ích tương tự là ở nơi "bộ lọc" thực sự được áp dụng, nằm trên bộ sưu tập đích "trước khi" kết quả được trả về.

Đó là kiểu xử lý tốt nhất "tương quan" với cùng một hành vi như một SQL JOIN. Do đó, đây cũng là cách hiệu quả nhất để nhận kết quả từ $lookup trong đó có các điều kiện khác để áp dụng cho JOIN ngoài việc chỉ đơn giản là "cục bộ" của các giá trị khóa "nước ngoài".

Cũng lưu ý rằng thay đổi hành vi khác là từ những gì về cơ bản là LEFT JOIN được thực hiện bởi $lookup trong đó tài liệu "nguồn" sẽ luôn được giữ lại bất kể sự hiện diện của tài liệu phù hợp trong bộ sưu tập "đích". Thay vào đó, $unwind thêm vào điều này bằng cách "loại bỏ" bất kỳ kết quả nào từ "nguồn" không có bất kỳ kết quả nào khớp với "đích" bằng các điều kiện bổ sung trong $match .

Trên thực tế, chúng thậm chí còn bị loại bỏ trước do preserveNullAndEmptyArrays: false được ngụ ý được bao gồm và sẽ loại bỏ bất kỳ thứ gì mà khóa "cục bộ" và "khóa ngoài" thậm chí không khớp giữa hai bộ sưu tập. Đây là một điều tốt cho loại truy vấn cụ thể này vì "kết hợp" được dự định là "bằng nhau" trên các giá trị đó.

Kết luận

Như đã lưu ý trước đây, MongoDB thường xử lý "quan hệ" rất khác với cách bạn sử dụng "Cơ sở dữ liệu quan hệ" hoặc RDBMS. Khái niệm chung về "quan hệ" trên thực tế là "nhúng" dữ liệu, dưới dạng một thuộc tính đơn lẻ hoặc dưới dạng một mảng.

Bạn có thể thực sự mong muốn đầu ra như vậy, đó cũng là một phần lý do tại sao điều đó mà không có $unwind trình tự ở đây kết quả đầu ra của $lookup thực sự là một "mảng". Tuy nhiên, sử dụng $unwind trong bối cảnh này thực sự là điều hiệu quả nhất cần làm, cũng như đảm bảo rằng dữ liệu "đã tham gia" không thực sự khiến giới hạn BSON nói trên bị vượt quá do "kết hợp" đó.

Nếu bạn thực sự muốn các mảng đầu ra, thì điều tốt nhất nên làm ở đây là sử dụng $group giai đoạn đường ống và có thể là nhiều giai đoạn để "chuẩn hóa" và "hoàn tác kết quả" của $unwind

  { "$group": {
    "_id": "$_id",
    "tb1_field": { "$first": "$tb1_field" },
    "tb1_another": { "$first": "$tb1_another" },
    "tb3": { "$push": "$tb3" }    
  }}

Trên thực tế, trường hợp này bạn sẽ liệt kê tất cả các trường bạn yêu cầu từ "tb1" theo tên thuộc tính của họ bằng cách sử dụng $first để chỉ giữ lần xuất hiện "đầu tiên" (về cơ bản được lặp lại bởi các kết quả của "tb2""tb3" không ràng buộc) và sau đó $push "chi tiết" từ "tb3" thành một "mảng" để đại diện cho mối quan hệ với "tb1" .

Nhưng hình thức chung của đường ống tổng hợp như đã cho là đại diện chính xác về cách thu được kết quả từ SQL gốc, là kết quả đầu ra "không chuẩn hóa" do kết quả của "kết hợp". Bạn có muốn "bình thường hóa" lại kết quả sau khi điều này là tùy thuộc vào bạn.




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. MongoDB {tổng hợp $ match} so với {find} tốc độ

  2. MongoDB So khớp một mảng với $ type?

  3. Lỗi khi nâng cấp Mongodb từ 3.2 lên 3.6

  4. Cách tôi đã viết một ứng dụng đứng đầu bảng xếp hạng trong một tuần với Realm và SwiftUI

  5. Từ điển Python:loại bỏ ký tự u '