Tôi có một bộ sưu tập T
, với 2 trường: Grade1
và 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: Grade1
và 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 $expr
như đượ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 operators
vs 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 $where
toá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 $project
và $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 $$KEEP
và 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 $$PRUNE
biế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 $where
cho 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 $where
có 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 $project
và $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