Mặc dù tôi đứng về phía các bình luận rằng tôi không nghĩ cách bạn diễn đạt câu hỏi của mình thực sự liên quan đến một vấn đề cụ thể mà bạn gặp phải, nhưng tôi sẽ giải thích cách nói SQL thành ngữ trong kiểu giải pháp MongoDB. Tôi cho rằng giải pháp thực tế của bạn sẽ khác nhưng bạn đã không trình bày cho chúng tôi vấn đề đó mà chỉ có SQL.
Vì vậy, hãy coi các tài liệu sau như một tập hợp mẫu, xóa các trường _id trong danh sách này để rõ ràng:
{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
{ "name" : "z", "type" : "z" }
Nếu chúng tôi chạy SQL được trình bày trên cùng một dữ liệu, chúng tôi sẽ nhận được kết quả sau:
a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c
Chúng ta có thể thấy rằng 2 tài liệu không khớp, và sau đó tìm ra logic của hoạt động SQL. Vì vậy, cách nói khác của nó là "Tài liệu nào cung cấp khóa của" tên " do có nhiều hơn một giá trị có thể có trong khóa "loại".
Do đó, sử dụng phương pháp mongo, chúng tôi có thể truy vấn các mục không phù hợp với điều kiện đã cho. Vì vậy, đảo ngược hiệu quả của kết quả:
db.sample.aggregate([
// Store unique documents grouped by the "name"
{$group: {
_id: "$name",
comp: {
$addToSet: {
name:"$name",
type: "$type"
}
}
}},
// Unwind the "set" results
{$unwind: "$comp"},
// Push the results back to get the unique count
// *note* you could not have done this with alongside $addtoSet
{$group: {
_id: "$_id",
comp: {
$push: {
name: "$comp.name",
type: "$comp.type"
}
},
count: {$sum: 1}
}},
// Match only what was counted once
{$match: {count: 1}},
// Unwind the array
{$unwind: "$comp"},
// Clean up to "name" and "type" only
{$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])
Thao tác này sẽ mang lại kết quả:
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
Bây giờ để có được kết quả tương tự như truy vấn SQL, chúng tôi sẽ lấy các kết quả đó và chuyển chúng thành một truy vấn khác:
db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })
Kết quả phù hợp cuối cùng sẽ đến:
{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
Vì vậy, điều này sẽ hoạt động, tuy nhiên một điều có thể làm cho điều này không thực tế là số lượng tài liệu được so sánh rất lớn, chúng tôi đã đạt đến giới hạn làm việc khi nén các kết quả đó thành một mảng.
Nó cũng bị ảnh hưởng một chút từ việc sử dụng phủ định trong thao tác tìm kiếm cuối cùng sẽ buộc phải quét bộ sưu tập. Nhưng công bằng mà nói, truy vấn SQL sử dụng cùng một phủ định tiền đề.
Chỉnh sửa
Tất nhiên điều tôi không đề cập là nếu tập hợp kết quả diễn ra theo chiều ngược lại và bạn khớp với thêm dẫn đến các mục bị loại trừ khỏi tổng hợp, sau đó chỉ cần đảo ngược logic để lấy các khóa bạn muốn. Chỉ cần thay đổi $ match như sau:
{$match: {$gt: 1}}
Và đó sẽ là kết quả, có thể không phải là các tài liệu thực tế nhưng nó là một kết quả. Vì vậy, bạn không cần một truy vấn khác để đối sánh với các trường hợp phủ định.
Và, cuối cùng thì đây là lỗi của tôi vì tôi quá tập trung vào bản dịch thành ngữ mà tôi đã không đọc dòng cuối cùng trong câu hỏi của bạn, làm ở đâu nói rằng bạn đang tìm kiếm một tài liệu.
Tất nhiên, hiện tại nếu kích thước kết quả đó lớn hơn 16MB thì bạn bị mắc kẹt. Ít nhất là cho đến 2.6 phát hành, trong đó kết quả của các phép toán tổng hợp là con trỏ
, vì vậy bạn có thể lặp lại nó giống như .find ()
.
Cũng được giới thiệu trong 2.6 là $ size
toán tử được sử dụng để tìm kích thước của một mảng trong tài liệu. Vì vậy, điều này sẽ giúp xóa $ unwind
thứ hai và $ group
được sử dụng để lấy độ dài của tập hợp. Điều này làm thay đổi truy vấn thành một dạng nhanh hơn:
db.sample.aggregate([
{$group: {
_id: "$name",
comp: {
$addToSet: {
name:"$name",
type: "$type"
}
}
}},
{$project: {
comp: 1,
count: {$size: "$comp"}
}},
{$match: {count: {$gt: 1}}},
{$unwind: "$comp"},
{$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])
Và MongoDB 2.6.0-rc0 hiện khả dụng nếu bạn đang thực hiện việc này chỉ để sử dụng cá nhân hoặc phát triển / thử nghiệm.
Đạo đức của câu chuyện. Có, bạn có thể làm điều đó, Nhưng bạn có thực sự muốn hoặc cần làm theo cách đó? Sau đó, có thể là không, và nếu bạn hỏi một câu hỏi khác về trường hợp kinh doanh cụ thể, bạn có thể nhận được một câu trả lời khác. Nhưng một lần nữa, điều này có thể chính xác phù hợp với những gì bạn muốn.
Lưu ý
Điều đáng nói là khi bạn xem kết quả từ SQL, nó sẽ nhầm trùng lặp một số mục do các tùy chọn loại có sẵn khác nếu bạn không sử dụng DISTINCT
cho các giá trị đó hoặc về cơ bản là một nhóm khác. Nhưng đó là kết quả được tạo ra bởi quá trình này bằng cách sử dụng MongoDB.
Dành cho Alexander
Đây là đầu ra của tổng hợp trong shell từ các phiên bản 2.4.x hiện tại:
{
"result" : [
{
"name" : "f",
"type" : "e"
},
{
"name" : "z",
"type" : "z"
}
],
"ok" : 1
}
Vì vậy, hãy làm điều này để lấy một var để chuyển làm đối số cho $ nor condition trong lần tìm thứ hai, như thế này:
var cond = db.sample.aggregate([ .....
db.sample.find({$nor: cond.result })
Và bạn sẽ nhận được kết quả tương tự. Nếu không, hãy tham khảo ý kiến tài xế của bạn.