Tôi có một bộ sưu tập T, với 2 trường: Grade1và Grade2, và tôi muốn chọn những trường có điều kiện Grade1 > Grade2, làm cách nào tôi có thể nhận được một truy vấn như trong MySQL?
Select * from T Where Grade1 > Grade2
Tôi có một bộ sưu tập T, với 2 trường: Grade1và Grade2, và tôi muốn chọn những trường có điều kiện Grade1 > Grade2, làm cách nào tôi có thể nhận được một truy vấn như trong MySQL?
Select * from T Where Grade1 > Grade2
Câu trả lời:
Bạn có thể sử dụng $ ở đâu. Chỉ cần lưu ý rằng nó sẽ khá chậm (phải thực thi mã Javascript trên mọi bản ghi) vì vậy hãy kết hợp với các truy vấn được lập chỉ mục nếu bạn có thể.
db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );
hoặc nhỏ gọn hơn:
db.T.find( { $where : "this.Grade1 > this.Grade2" } );
bạn có thể sử dụng $exprnhư được mô tả trong câu trả lời gần đây
$where: function() { return this.Grade1 - this.Grade2 > variable }?
db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});nó không trả lại gì, ngay cả một trong những tài liệu trong bộ sưu tập cũng vậy ISODate("2017-01-20T10:55:08.000Z"). Nhưng <=và >=có vẻ hiệu quả. bất kỳ ý tưởng?
this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
Bạn có thể sử dụng $ expr (toán tử phiên bản 3.6 mongo) để sử dụng các hàm tổng hợp trong truy vấn thông thường.
Hãy so sánh query operatorsvs aggregation comparison operators.
Truy vấn thông thường:
db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})
Truy vấn Tổng hợp:
db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
Nếu truy vấn của bạn chỉ bao gồm $wheretoán tử, bạn có thể chỉ chuyển vào biểu thức JavaScript:
db.T.find("this.Grade1 > this.Grade2");
Để có hiệu suất cao hơn, hãy chạy một hoạt động tổng hợp có $redactđường dẫn để lọc các tài liệu thỏa mãn điều kiện đã cho.
Các $redactđường ống kết hợp các chức năng của $projectvà $matchđể thực hiện sân soạn thảo nơi mà nó sẽ trả lại tất cả các văn bản phù hợp với điều kiện sử dụng $$KEEPvà loại bỏ từ các kết quả đường ống dẫn những người không phù hợp bằng cách sử dụng $$PRUNEbiến.
Chạy thao tác tổng hợp sau sẽ lọc tài liệu hiệu quả hơn so với việc sử dụng $wherecho các bộ sưu tập lớn vì thao tác này sử dụng một đường ống đơn và các toán tử MongoDB gốc, thay vì đánh giá JavaScript $wherecó thể làm chậm truy vấn:
db.T.aggregate([
{
"$redact": {
"$cond": [
{ "$gt": [ "$Grade1", "$Grade2" ] },
"$$KEEP",
"$$PRUNE"
]
}
}
])
là một phiên bản đơn giản hơn của việc kết hợp hai đường ống $projectvà $match:
db.T.aggregate([
{
"$project": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
"Grade1": 1,
"Grade2": 1,
"OtherFields": 1,
...
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Với MongoDB 3.4 và mới hơn:
db.T.aggregate([
{
"$addFields": {
"isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
}
},
{ "$match": { "isGrade1Greater": 1 } }
])
Trong trường hợp hiệu suất quan trọng hơn khả năng đọc và miễn là điều kiện của bạn bao gồm các phép toán số học đơn giản, bạn có thể sử dụng đường ống tổng hợp. Đầu tiên, sử dụng $ project để tính toán bên trái của điều kiện (lấy tất cả các trường ở bên tay trái). Sau đó, sử dụng $ match để so sánh với một hằng số và bộ lọc. Bằng cách này, bạn tránh thực thi javascript. Dưới đây là thử nghiệm của tôi trong python:
import pymongo
from random import randrange
docs = [{'Grade1': randrange(10), 'Grade2': randrange(10)} for __ in range(100000)]
coll = pymongo.MongoClient().test_db.grades
coll.insert_many(docs)
Sử dụng tổng hợp:
%timeit -n1 -r1 list(coll.aggregate([
{
'$project': {
'diff': {'$subtract': ['$Grade1', '$Grade2']},
'Grade1': 1,
'Grade2': 1
}
},
{
'$match': {'diff': {'$gt': 0}}
}
]))
1 vòng lặp, tốt nhất là 1: 192 ms mỗi vòng
Sử dụng find và $ where:
%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))
1 vòng lặp, tốt nhất là 1: 4,54 giây mỗi vòng lặp