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

Xóa trường được tìm thấy trong bất kỳ mảng mongodb nào

Vì vậy, tôi đã đặt một câu hỏi trong phần nhận xét nhưng dường như bạn đã bỏ qua, vì vậy tôi đoán tôi chỉ trả lời ba trường hợp có thể xảy ra mà tôi thấy.

Để bắt đầu, tôi không chắc liệu các phần tử được hiển thị trong các mảng lồng nhau có phải là duy nhất các phần tử trong mảng hoặc trên thực tế nếu arrayToDelete duy nhất trường hiện diện trong các phần tử đó. Vì vậy, về cơ bản tôi cần phải trừu tượng hóa một chút và bao gồm trường hợp đó:

{
    field: 'value',
    field2: 'value',
    scan: [
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
        [
            {   somethingToKeep: 1 },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
            {
                arrayToDelete: [0,1,2],
                anotherField: "a"
            },
        ],
    ]
}

Trường hợp 1 - Loại bỏ các phần tử mảng bên trong nơi có Trường

Điều này sẽ sử dụng $pull toán tử vì đó là những gì loại bỏ các phần tử mảng toàn bộ. Bạn thực hiện việc này trong MongoDB hiện đại với câu lệnh như sau:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },
  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }
)

Điều đó làm thay đổi tất cả các tài liệu phù hợp như thế này:

{
        "_id" : ObjectId("5ca1c36d9e31550a618011e2"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        }
                ]
        ]
}

Vì vậy, mọi phần tử chứa trường đó hiện đã bị xóa.

Trường hợp 2 - Chỉ cần xóa trường phù hợp khỏi các phần tử bên trong

Đây là nơi bạn sử dụng $unset . Nó chỉ khác một chút so với "hard indexed" phiên bản bạn đang làm:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[].$[].arrayToDelete": ""  } }
)

Điều nào làm thay đổi tất cả các tài liệu phù hợp thành:

{
        "_id" : ObjectId("5ca1c4c49e31550a618011e3"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        }
                ],
                [
                        {
                                "somethingToKeep" : 1
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        },
                        {
                                "anotherField" : "a"
                        }
                ]
        ]
}

Vì vậy, mọi thứ vẫn ở đó, nhưng chỉ các trường được xác định đã bị xóa khỏi mỗi tài liệu mảng bên trong.

Trường hợp 3 - Bạn thực sự muốn xóa "Mọi thứ" trong mảng.

Đây thực sự chỉ là một trường hợp đơn giản của việc sử dụng $set và xóa sạch mọi thứ đã có trước đó:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$set": { "scan": []  } }
)

Trường hợp kết quả sẽ khá tốt như mong đợi:

{
        "_id" : ObjectId("5ca1c5c59e31550a618011e4"),
        "field" : "value",
        "field2" : "value",
        "scan" : [ ]
}

Vậy tất cả những thứ này đang làm gì?

Điều đầu tiên bạn sẽ thấy là vị từ truy vấn . Đây thường là một ý tưởng hay để đảm bảo rằng bạn không khớp và thậm chí là "đang cố gắng" đáp ứng các điều kiện cập nhật trên các tài liệu thậm chí không chứa dữ liệu với mẫu mà bạn định cập nhật. Các mảng lồng nhau rất khó tốt nhất và nếu thực tế bạn thực sự nên tránh chúng, như những gì bạn thường "thực sự có ý nghĩa" thực sự được biểu thị trong một mảng số ít với các thuộc tính bổ sung đại diện cho những gì bạn "nghĩ" cái làm tổ thực sự đang làm cho bạn.

Nhưng chỉ vì họ khó không có nghĩa là không thể . Chỉ là bạn cần hiểu $elemMatch :

db.colelction.find(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  }}
)

Đó là find() cơ bản ví dụ, đối sánh dựa trên $elemMatch điều kiện cho bên ngoài mảng sử dụng một $elemMatch khác để phù hợp với một điều kiện khác trong bên trong mảng. Mặc dù điều này "xuất hiện" trở thành một vị ngữ số ít. Một cái gì đó như:

"scan.arrayToDelete": { "$exists": true }

Chỉ sẽ không hoạt động. Cũng không:

"scan..arrayToDelete": { "$exists": true }

Với "dấu chấm đôi" .. vì về cơ bản điều đó không hợp lệ.

Đó là vị từ truy vấn để khớp với "tài liệu" cần được xử lý, nhưng phần còn lại áp dụng để thực sự xác định * phần nào cần cập nhật ".

Trong Trường hợp 1 để $pull từ bên trong , trước tiên, chúng tôi cần xác định được phần tử nào của bên ngoài mảng chứa dữ liệu cần cập nhật. Đó là những gì "scan.$[a]" điều đang làm bằng cách sử dụng $[<identifier>] được lọc theo vị trí nhà điều hành.

Toán tử đó về cơ bản chuyển vị chỉ số phù hợp (so nhiều trong số chúng) trong mảng thành một vị từ khác được định nghĩa trong phần thứ ba của update lệnh kiểu với arrayFilters tiết diện. Phần này về cơ bản xác định các điều kiện cần được đáp ứng từ quan điểm của số nhận dạng được đặt tên.

Trong trường hợp này, "số nhận dạng" được đặt tên là a và đó là tiền tố được sử dụng trong arrayFilters mục nhập:

  { "arrayFilters": [
      {  "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } }
    ]
  }

Được đưa vào ngữ cảnh với tuyên bố cập nhật thực tế phần:

  {
    "$pull": {
      "scan.$[a]": { "arrayToDelete": { "$exists": true } }
    }
  },

Sau đó, từ quan điểm của "a" là định danh cho bên ngoài phần tử mảng đầu tiên vào trong từ "scan" , thì các điều kiện tương tự áp dụng như cho vị từ truy vấn ban đầu nhưng từ "bên trong" đầu tiên $elemMatch tuyên bố. Vì vậy, về cơ bản bạn có thể coi đây là một "truy vấn trong một truy vấn" từ góc độ đã "nhìn vào bên trong" nội dung của mỗi bên ngoài phần tử.

Bằng cùng một mã thông báo, $pull hoạt động giống như một "truy vấn trong một truy vấn" trong đó các đối số của chính nó cũng được áp dụng từ quan điểm của phần tử của mảng. Do đó, chỉ arrayToDelete trường hiện có thay vì:

  // This would be wrong! and do nothing :(
  {
    "$pull": {
      "scan.$[a]": { "$elemMatch": { "arrayToDelete": { "$exists": true } } }
    }
  }

Nhưng đó là tất cả những gì cụ thể đối với $pull và những thứ khác có các trường hợp khác nhau:

Trường hợp 2 xem nơi bạn muốn chỉ $unset trường được đặt tên. Có vẻ khá dễ dàng khi bạn chỉ cần đặt tên trường, phải không? Cũng không hẳn vì điều sau rõ ràng không đúng với những gì chúng ta biết trước đó:

  { "$unset": { "scan.arrayToDelete": ""  } } // Not right :(

Và tất nhiên, việc ghi chú các chỉ mục mảng cho mọi thứ chỉ là một vấn đề khó khăn:

  { "$unset": { 
    "scan.0.0.arrayToDelete": "",
    "scan.0.1.arrayToDelete": "",
    "scan.0.2.arrayToDelete": "",
    "scan.0.3.arrayToDelete": "",  // My fingers are tired :-<
  } }

Đây là lý do cho vị trí tất cả $[] nhà điều hành. Đây là một chút "bạo lực" so với $[<identifier>] được lọc theo vị trí trong đó thay vì khớp với một vị từ khác được cung cấp trong arrayFilters , những gì điều này chỉ đơn giản là áp dụng cho mọi thứ trong nội dung mảng tại "chỉ mục" đó. Trên thực tế, đó là một cách nói "tất cả các chỉ mục" mà không cần gõ từng cái một như kinh khủng trường hợp hiển thị ở trên.

Vì vậy, nó không dành cho mọi trường hợp , nhưng nó chắc chắn rất phù hợp với $unset vì nó có đặt tên đường dẫn rất cụ thể tất nhiên không thành vấn đề nếu đường dẫn đó không khớp với mọi phần tử của mảng.

Bạn có thể vẫn sử dụng arrayFilters và một $[<identifier>] được lọc theo vị trí , nhưng ở đây nó sẽ là quá mức cần thiết. Thêm vào đó, không có hại gì khi chứng minh cách tiếp cận khác.

Nhưng tất nhiên nó có lẽ đáng để hiểu làm thế nào chính xác là câu lệnh sẽ giống như vậy, vì vậy:

db.collection.updateMany(
  { "scan": {
    "$elemMatch": {
      "$elemMatch": {
        "arrayToDelete": { "$exists": true }
      }
    }
  } },
  { "$unset": { "scan.$[a].$[b].arrayToDelete": ""  } },
  {
    "arrayFilters": [
      { "a": { "$elemMatch": { "arrayToDelete": { "$exists": true } } } },
      { "b.arrayToDelete": { "$exists": true } },
    ]
  }
)

Lưu ý rằng "b.arrayToDelete" có thể không phải như những gì bạn mong đợi lúc đầu, nhưng được định vị trong quá trình quét "scan.$[a].$[b] nó thực sự nên có ý nghĩa như từ b tên phần tử sẽ đạt được thông qua "ký hiệu dấu chấm" như được hiển thị. Và trên thực tế trong cả hai trường hợp. Tuy nhiên, một lần nữa, một $unset sẽ chỉ áp dụng cho trường được đặt tên, vì vậy tiêu chí lựa chọn thực sự không bắt buộc.

Trường hợp 3 . Nó khá đơn giản nếu bạn không cần để giữ bất kỳ thứ gì khác trong mảng sau khi xóa nội dung này (tức là $pull trong đó các trường phù hợp với điều này là duy nhất những thứ trong đó hoặc $unset đối với vấn đề đó), sau đó chỉ cần không lộn xộn với bất kỳ thứ gì khác và chỉ cần xóa mảng .

Đây là một điểm khác biệt quan trọng nếu bạn coi đó là điểm cần làm rõ liệu các tài liệu có trường được đặt tên có chỉ các phần tử trong các mảng lồng nhau và thực sự rằng khóa được đặt tên là duy nhất điều có trong tài liệu.

Với lý do là sử dụng $pull như được hiển thị ở đây và trong những điều kiện đó, bạn sẽ nhận được:

{
        "_id" : ObjectId("5ca321909e31550a618011e6"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [ ],
            [ ],
            [ ]
        ]
}

Hoặc với $unset :

{
        "_id" : ObjectId("5ca322bc9e31550a618011e7"),
        "field" : "value",
        "field2" : "value",
        "scan" : [
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }],
            [{ }, { }, { }, { }]
        ]
}

Cả hai điều này rõ ràng là không mong muốn. Vì vậy, nó đúng là lý do nếu arrayToDelete trường là duy nhất nội dung có trong đó, thì cách hợp lý nhất để xóa tất cả chỉ đơn giản là thay thế mảng bằng một mảng trống. Hoặc thực sự là $unset toàn bộ thuộc tính tài liệu.

Tuy nhiên, hãy lưu ý rằng tất cả những "điều lạ mắt" này (ngoại trừ $set tất nhiên) yêu cầu bạn ít nhất phải có MongoDB 3.6 có sẵn để sử dụng chức năng này.

Trong trường hợp bạn vẫn đang chạy phiên bản MongoDB cũ hơn phiên bản đó (và tính đến ngày viết bài, bạn thực sự không nên vì hỗ trợ chính thức của bạn hết chỉ sau 5 tháng kể từ ngày này) thì các câu trả lời hiện có khác về Cách cập nhật nhiều Phần tử mảng trong mongodb thực sự dành cho bạn.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Cách sắp xếp mongodb với pymongo

  2. Làm cách nào để thoát @ trong mật khẩu trong kết nối pymongo?

  3. Cách chèn nhiều mục cùng lúc trong bộ sưu tập MongoDB

  4. Chèn dữ liệu vào mảng lồng nhau trong mongodb

  5. MongoDB $ toLong