Điều kiện truy vấn MongoDb khi so sánh 2 trường


108

Tôi có một bộ sưu tập T, với 2 trường: Grade1Grade2, 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:


120

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" } );

UPD cho mongodb v.3.6 +

bạn có thể sử dụng $exprnhư được mô tả trong câu trả lời gần đây


Rất tiếc, tôi hiểu rồi, chỉ cần sử dụng chung javascript hoặc các script shell khác, cảm ơn cả hai bạn!
Diego Cheng

16
Bạn cũng có thể làm điều này nhỏ gọn hơn một chút ...> db.T.find ({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins

làm thế nào tôi có thể $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González

khi tôi thử 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 <=>=có vẻ hiệu quả. bất kỳ ý tưởng?
cateyes

@cateyes Có lẽ hơi muộn ... nhưng javascript thuần luôn trả về false khi thực hiện so sánh == giữa 2 ngày. Tuy nhiên, Mongo cho phép bạn tìm kiếm các kết quả khớp chính xác giữa các ngày trong các truy vấn. Một cách giải quyết là sử dụng .getTime () để chuyển đổi sang mili giây hoặc bất cứ điều gì:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

53

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"]}}})

39

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 $project$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 $project$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 } }
])

cái cuối cùng dường như không làm việc với tôi. Trường isGrade1Greater được thêm và đánh giá đúng cách nhưng vì một số lý do mà truy vấn khớp với tất cả các hàng bất kể giá trị của isGrade1Greater. Điều gì có thể là nguyên nhân của hành vi này? CHỈNH SỬA: Đừng bận tâm, tôi đã không truyền một mảng cho tổng hợp () mà là mỗi tập hợp dưới dạng chính tham số, đã bỏ lỡ điều đó.
ThatBrianDude

13

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.