Tôi cần khớp tất cả các tài liệu trong đó mọi phần tử của một mảng khớp với một số vị từ. Điều đó có thể được thực hiện?
Câu trả lời
Vâng, truy vấn để làm điều này thực sự khá đơn giản để xây dựng
Hãy nhớ rằng khi bạn so khớp một mảng, MongoDB sẽ “tiếp cận bên trong” mảng để so sánh vị từ với mọi phần tử mảng đơn lẻ và trả về tài liệu nếu vị từ khớp với ít nhất một trong số chúng. Tôi muốn nói với những người mới sử dụng MongoDB rằng hãy nghĩ về mảng như một trường có thể chứa nhiều giá trị khác nhau cùng một lúc. Một khi bạn bắt đầu nghĩ về mảng theo cách đó, sẽ dễ hiểu hơn rất nhiều truy vấn như “trong đó A lớn hơn 50 VÀ A nhỏ hơn 10” không phải là vô nghĩa nếu “A” là một mảng, bởi vì các phần tử mảng khác nhau
Điều đó có nghĩa là để đảm bảo mọi phần tử mảng khớp với một số cấu trúc, bạn nên phủ định cấu trúc đó và sau đó phủ định lại truy vấn
Một ví dụ đơn giản có lẽ có thể giúp
Hãy tưởng tượng bạn có bộ tài liệu này
{ “a”: [ 1, 2, 3, 4 ] } { “a”: [ 3, 4, 5, 6 ] } { “a”: [ 5, 6, 7, 8 ] } { “a”: [ 1, 2, 3, 4, 5 ] }
Làm thế nào để bạn tìm thấy tất cả các tài liệu mà “a” nhỏ hơn 5?
{ “a”: [ 1, 2, 3, 4 ] } { “a”: [ 3, 4, 5, 6 ] } { “a”: [ 1, 2, 3, 4, 5 ] }
Điều này là do ít nhất một phần tử trong mỗi mảng này khớp với vị từ truy vấn của chúng tôi. Tài liệu thứ ba không có phần tử nào nhỏ hơn 5
Bây giờ chúng tôi chỉ muốn lấy lại các tài liệu có mọi phần tử khớp với cùng một vị từ. Một cách khác để nói “Tôi muốn mọi tài liệu có mỗi phần tử của mảng nhỏ hơn 5” sẽ là “Tôi muốn mọi tài liệu không có phần tử nào lớn hơn hoặc bằng 5”. Vì vậy, trước tiên chúng tôi phủ nhận truy vấn ban đầu của mình
db.coll.find[{"a":{"$gte":5}}]
và sau đó chúng tôi phủ nhận toàn bộ kết quả
db.coll.find[{"$nor":[{"a":{"$gte":5}}]}]
và như bạn mong đợi, kết quả là
{ “a”: [ 1, 2, 3, 4 ] }
Khi chúng ta xử lý các con số, thật dễ dàng để “phủ định” một điều kiện, nhưng với mảng, việc lập luận về “$not” và “$nor” có thể phức tạp, vì vậy, hãy thử lại với các chuỗi mà chúng ta không thể sử dụng “$gt” và
{ “a” : [ “1”, “2”, “3”, “4” ] } { “a” : [ “3”, “4”, “5”, “6” ] } { “a” : [ “5”, “6”, “7”, “8” ] } { “a” : [ “1”, “2”, “3”, “4”, “5” ] }
Hãy thử điều tương tự mà chúng tôi đã thử ở trên, trước tiên chúng tôi sẽ tìm kiếm một trong số các tập hợp “1”, “2”, “3”, “4” và bắt đầu từ đó
db.coll.find[{“a”:{“$in”:[“1”,“2”,“3”,“4”]}}] { “a” : [ “1”, “2”, “3”, “4” ] } { “a” : [ “3”, “4”, “5”, “6” ] } { “a” : [ “1”, “2”, “3”, “4”, “5” ] } db.coll.find[{“a”:{“$nin”:[“1”,“2”,“3”,“4”]}}] { “a” : [ “5”, “6”, “7”, “8” ] }Chuyện gì đã xảy ra? . Vậy làm cách nào để thể hiện truy vấn mà chúng tôi muốn tất cả các tài liệu có phần tử không nằm trong danh sách của chúng tôi?
Bất cứ khi nào câu hỏi [hoặc truy vấn] liên quan đến một phần tử của một mảng, thì rất có thể bạn nên sử dụng [“$elemMatch”][1] để diễn đạt nó. Thông thường, chúng tôi sử dụng “$elemMatch” để diễn đạt rằng chúng tôi muốn cùng một phần tử mảng khớp với nhiều điều kiện trong các vị từ truy vấn, nhưng cũng đúng khi sử dụng nó khi bạn đang cố gắng phủ định ý nghĩa của truy vấn bằng cách áp dụng phủ định cho
// find me all documents where at least one array element is *not* on our list db.coll.find[{"a":{"$elemMatch":{"$nin":["1","2","3","4"]}}}] { "a" : [ "1", "2", "3", "4", "5" ] } { "a" : [ "3", "4", "5", "6" ] } { "a" : [ "5", "6", "7", "8" ] } // now we negate the entire query db.coll.find[{"$nor":[{"a":{"$elemMatch":{"$nin":["1","2","3","4"]}}}]}] { "a" : [ "1", "2", "3", "4" ] }
Đây là một ví dụ phức tạp khác liên quan đến một biểu thức chính quy - trong khi bạn có thể phủ định một biểu thức chính quy, bạn có thể vô tình giới hạn việc so khớp chỉ với các loại chuỗi và khi bạn có các mảng kiểu hỗn hợp [không được khuyến nghị, nhưng điều đó xảy ra] sẽ không mang lại cho bạn kết quả mong muốn
{ "a" : [ "str1", "str2", "str3", "notstr" ] } { "a" : [ "str1", "str2", "str3", "str4" ] } { "a" : [ 1, 2, 3, 4, 5 ] } { "a" : [ 5, 6, 7, 8, 9 ] } { "a" : [ "str1", 0, 10 ] }
Giả sử tôi chỉ muốn lấy lại các tài liệu có tất cả các phần tử “a” bắt đầu bằng các ký tự “str”. Hãy xem xét một số truy vấn và kết quả của chúng
db.coll.find[{“a”:/^str/}] { “a” : [ “str1”, “str2”, “str3”, “notstr” ] } { “a” : [ “str1”, “str2”, “str3”, “str4” ] } { “a” : [ “str1”, 0, 10 ] } db.coll.find[{“a”:{$not:/^str/}}] { “a” : [ 1, 2, 3, 4, 5 ] } { “a” : [ 5, 6, 7, 8, 9 ] } // negate regular expression: db.coll.find[{“a”:/^[?!str]/}] { “a” : [ “str1”, “str2”, “str3”, “notstr” ] } db.coll.find[{“a”:{$not:/^[?!str]/}}] { “a” : [ “str1”, “str2”, “str3”, “str4” ] } { “a” : [ 1, 2, 3, 4, 5 ] } { “a” : [ 5, 6, 7, 8, 9 ] } { “a” : [ “str1”, 0, 10 ] }
Có bất ngờ nào ở đây không? . Chúng tôi cũng thấy rằng “$not” được thêm vào bất kỳ truy vấn regex nào sẽ trả về phần bổ sung của các tài liệu được trả về mà không có “$not”. Đó không phải là những gì chúng ta cần khi cố gắng lấy tất cả các tài liệu với mọi phần tử thỏa mãn vị ngữ. Hãy xem liệu “$elemMatch” có mang đến cho chúng ta những gì chúng ta muốn không
{ “a”: [ 1, 2, 3, 4 ] } { “a”: [ 3, 4, 5, 6 ] } { “a”: [ 1, 2, 3, 4, 5 ] }0
Bây giờ, hãy thử nó trên một cấu trúc tài liệu phức tạp hơn với một vị từ phức tạp hơn
{ “a”: [ 1, 2, 3, 4 ] } { “a”: [ 3, 4, 5, 6 ] } { “a”: [ 1, 2, 3, 4, 5 ] }1
Nếu vị ngữ của chúng ta chỉ nói về “b. x” hoặc chỉ về “b. y”, chúng tôi sẽ sử dụng “$elemMatch” thay vì ký hiệu chấm để chạy truy vấn giống như ví dụ đầu tiên của chúng tôi. Để tìm tất cả các tài liệu trong đó “b. x” là 1, 2 hoặc 3, chúng ta có thể thực hiện các bước này [giả sử tất cả các truy vấn yêu cầu trong phép chiếu chỉ dành cho trường mà tôi đang truy vấn]