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

Truy vấn giao điểm mảng lồng nhau MongoDB

Có một số cách để thực hiện việc này bằng cách sử dụng khung tổng hợp

Chỉ là một tập hợp dữ liệu đơn giản, ví dụ:

{
    "_id" : ObjectId("538181738d6bd23253654690"),
    "movies": [
        { "_id": 1, "rating": 5 },
        { "_id": 2, "rating": 6 },
        { "_id": 3, "rating": 7 }
    ]
},
{
    "_id" : ObjectId("538181738d6bd23253654691"),
    "movies": [
        { "_id": 1, "rating": 5 },
        { "_id": 4, "rating": 6 },
        { "_id": 2, "rating": 7 }
    ]
},
{
    "_id" : ObjectId("538181738d6bd23253654692"),
    "movies": [
        { "_id": 2, "rating": 5 },
        { "_id": 5, "rating": 6 },
        { "_id": 6, "rating": 7 }
    ]
}

Sử dụng "người dùng" đầu tiên làm ví dụ, bây giờ bạn muốn tìm xem có ai trong số hai người dùng còn lại có ít nhất hai bộ phim giống nhau hay không.

Đối với MongoDB 2.6 trở lên, bạn có thể chỉ cần sử dụng $setIntersection toán tử cùng với $size nhà điều hành:

db.users.aggregate([

    // Match the possible documents to reduce the working set
    { "$match": {
        "_id": { "$ne": ObjectId("538181738d6bd23253654690") },
        "movies._id": { "$in": [ 1, 2, 3 ] },
        "$and": [
            { "movies": { "$not": { "$size": 1 } } }
        ]
    }},

    // Project a copy of the document if you want to keep more than `_id`
    { "$project": {
        "_id": {
            "_id": "$_id",
            "movies": "$movies"
        },
        "movies": 1,
    }},

    // Unwind the array
    { "$unwind": "$movies" },

    // Build the array back with just `_id` values
    { "$group": {
        "_id": "$_id",
        "movies": { "$push": "$movies._id" }
    }},

    // Find the "set intersection" of the two arrays
    { "$project": {
        "movies": {
            "$size": {
                "$setIntersection": [
                   [ 1, 2, 3 ],
                   "$movies"
                ]
            }
        }
    }},

    // Filter the results to those that actually match
    { "$match": { "movies": { "$gte": 2 } } }

])

Điều này vẫn có thể xảy ra trong các phiên bản trước của MongoDB không có các toán tử đó, chỉ cần sử dụng thêm một vài bước:

db.users.aggregate([

    // Match the possible documents to reduce the working set
    { "$match": {
        "_id": { "$ne": ObjectId("538181738d6bd23253654690") },
        "movies._id": { "$in": [ 1, 2, 3 ] },
        "$and": [
            { "movies": { "$not": { "$size": 1 } } }
        ]
    }},

    // Project a copy of the document along with the "set" to match
    { "$project": {
        "_id": {
            "_id": "$_id",
            "movies": "$movies"
        },
        "movies": 1,
        "set": { "$cond": [ 1, [ 1, 2, 3 ], 0 ] }
    }},

    // Unwind both those arrays
    { "$unwind": "$movies" },
    { "$unwind": "$set" },

    // Group back the count where both `_id` values are equal
    { "$group": {
        "_id": "$_id",
        "movies": {
           "$sum": {
               "$cond":[
                   { "$eq": [ "$movies._id", "$set" ] },
                   1,
                   0
               ]
           }
        } 
    }},

    // Filter the results to those that actually match
    { "$match": { "movies": { "$gte": 2 } } }
])

Chi tiết

Điều đó có thể hơi khó khăn một chút, vì vậy chúng ta có thể xem xét từng giai đoạn và chia nhỏ các giai đoạn đó để xem họ đang làm gì.

$ trận đấu :Bạn không muốn thao tác trên mọi tài liệu trong bộ sưu tập, vì vậy đây là cơ hội để xóa các mục không thể khớp ngay cả khi vẫn còn nhiều việc phải làm để tìm chính xác những cái. Vì vậy, điều hiển nhiên là loại trừ cùng một "người dùng" và sau đó chỉ đối sánh các tài liệu có ít nhất một trong những bộ phim giống như đã được tìm thấy cho "người dùng" đó.

Điều có ý nghĩa tiếp theo là cân nhắc điều đó khi bạn muốn đối sánh n sau đó chỉ các mục nhập tài liệu có mảng "phim" lớn hơn n-1 có thể thực sự chứa các kết quả phù hợp. Việc sử dụng $and ở đây trông buồn cười và không bắt buộc cụ thể, nhưng nếu các kết quả phù hợp bắt buộc là 4 thì phần thực tế đó của câu lệnh sẽ giống như sau:

        "$and": [
            { "movies": { "$not": { "$size": 1 } } },
            { "movies": { "$not": { "$size": 2 } } },
            { "movies": { "$not": { "$size": 3 } } }
        ]

Vì vậy, về cơ bản bạn "loại trừ" các mảng có thể không đủ dài để có n diêm. Lưu ý ở đây rằng $size này toán tử trong biểu mẫu truy vấn khác với $size cho khung tổng hợp. Chẳng hạn, không có cách nào để sử dụng điều này với toán tử bất đẳng thức chẳng hạn như $gt mục đích của nó là để khớp cụ thể với "kích thước" được yêu cầu. Do đó, biểu mẫu truy vấn này để chỉ định tất cả các kích thước có thể có nhỏ hơn.

$ dự án :Có một số mục đích trong câu lệnh này, trong đó một số mục đích khác nhau tùy thuộc vào phiên bản MongoDB mà bạn có. Thứ nhất, và theo tùy chọn, một bản sao tài liệu đang được giữ dưới _id giá trị để các trường này không bị sửa đổi bởi các bước còn lại. Phần khác ở đây là giữ mảng "phim" ở đầu tài liệu như một bản sao cho giai đoạn tiếp theo.

Điều gì cũng đang xảy ra trong phiên bản được trình bày cho các phiên bản trước 2.6 là có một mảng bổ sung đại diện cho _id giá trị cho "phim" phù hợp. Việc sử dụng $cond toán tử ở đây chỉ là một cách tạo ra một biểu diễn "theo nghĩa đen" của mảng. Thật hài hước, MongoDB 2.6 giới thiệu một toán tử được gọi là $literal để thực hiện chính xác điều này mà không theo cách hài hước mà chúng tôi đang sử dụng $cond ngay tại đây.

$ thư giãn :Để làm bất cứ điều gì xa hơn, mảng phim cần được mở ra vì trong cả hai trường hợp đó là cách duy nhất để cô lập _id hiện có giá trị cho các mục nhập cần được so khớp với "tập hợp". Vì vậy, đối với phiên bản trước 2.6, bạn cần "giải phóng" cả hai mảng hiện có.

$ nhóm :Đối với MongoDB 2.6 trở lên, bạn chỉ đang nhóm lại thành một mảng chỉ chứa _id giá trị của phim bị xóa "xếp hạng".

Trước 2.6 vì tất cả các giá trị được trình bày "cạnh nhau" (và có nhiều sự trùng lặp) nên bạn đang thực hiện so sánh hai giá trị để xem chúng có giống nhau hay không. Đó là true , điều này cho $cond câu lệnh toán tử để trả về giá trị 1 hoặc 0 trong đó điều kiện là false . Điều này được chuyển trực tiếp qua $sum để tổng số phần tử phù hợp trong mảng vào "bộ" bắt buộc.

$ dự án :Đây là phần khác biệt đối với MongoDB 2.6 và lớn hơn là vì bạn đã đẩy lùi một mảng "phim" _id các giá trị mà bạn đang sử dụng sau đó $setIntersection để so sánh trực tiếp các mảng đó. Kết quả của việc này là một mảng chứa các phần tử giống nhau, mảng này sau đó được bao bọc trong một $size để xác định có bao nhiêu phần tử được trả về trong tập hợp đó.

$ trận đấu :Là giai đoạn cuối cùng đã được thực hiện ở đây, bước rõ ràng là chỉ đối sánh những tài liệu có số lượng phần tử giao nhau lớn hơn hoặc bằng số được yêu cầu.

Chung kết

Về cơ bản đó là cách bạn làm điều đó. Trước 2.6 thì phức tạp hơn một chút và sẽ yêu cầu nhiều bộ nhớ hơn một chút do việc mở rộng được thực hiện bằng cách sao chép từng thành viên mảng được tìm thấy bởi tất cả các giá trị có thể của tập hợp, nhưng đây vẫn là một cách hợp lệ để thực hiện điều này.

Tất cả những gì bạn cần làm là áp dụng điều này với n lớn hơn các giá trị phù hợp để đáp ứng các điều kiện của bạn và tất nhiên đảm bảo rằng đối sánh người dùng ban đầu của bạn có n bắt buộc khả năng. Nếu không, chỉ cần tạo mã này trên n-1 từ độ dài của mảng "phim" của "người dùng".




  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Tham số chiếu MongoDB không hoạt động trong findOne ()

  2. cách chuyển đổi chuỗi thành giá trị số trong mongodb

  3. Chuyển đổi RFC3339 DateTime sang Date trong java

  4. MongoDB:Truy vấn địa lý không đúng định dạng với $ geoIntersect trên một đa giác

  5. Bộ lọc Linq đến MongoDB