Kondisi kueri MongoDb saat membandingkan 2 bidang

108

Saya memiliki koleksi T, dengan 2 bidang: Grade1dan Grade2, dan saya ingin memilih yang kondisinya Grade1 > Grade2, bagaimana saya bisa mendapatkan kueri seperti di MySQL?

Select * from T Where Grade1 > Grade2
Diego Cheng
sumber

Jawaban:

120

Anda bisa menggunakan $ where. Perlu diketahui bahwa ini akan cukup lambat (harus mengeksekusi kode Javascript pada setiap record) jadi gabungkan dengan query yang diindeks jika Anda bisa.

db.T.find( { $where: function() { return this.Grade1 > this.Grade2 } } );

atau lebih kompak:

db.T.find( { $where : "this.Grade1 > this.Grade2" } );

UPD untuk mongodb v.3.6 +

Anda dapat menggunakan $exprseperti yang dijelaskan dalam jawaban terbaru

Ian
sumber
Ups, saya mengerti, cukup gunakan javascript atau skrip shell lainnya, terima kasih kalian berdua!
Diego Cheng
16
Anda juga dapat melakukan ini sedikit lebih ringkas ...> db.T.find ({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins
bagaimana saya bisa $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González
ketika saya mencobanya db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});tidak mengembalikan apa-apa, bahkan salah satu dokumen dalam koleksi tersebut ISODate("2017-01-20T10:55:08.000Z"). Tapi <=dan >=sepertinya berhasil. ada ide?
cateyes
@cateyes Mungkin agak terlambat ... tapi javascript murni selalu mengembalikan nilai salah saat melakukan == bandingkan antara 2 tanggal. Mongo, bagaimanapun, memungkinkan Anda mencari kecocokan persis antara tanggal dalam kueri. Salah satu solusinya adalah menggunakan .getTime () untuk mengonversi ke milidetik atau apa pun:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC
53

Anda dapat menggunakan $ expr (operator versi 3.6 mongo) untuk menggunakan fungsi agregasi dalam kueri reguler.

Bandingkan query operatorsvs.aggregation comparison operators .

Pertanyaan Reguler:

db.T.find({$expr:{$gt:["$Grade1", "$Grade2"]}})

Kueri Agregasi:

db.T.aggregate({$match:{$expr:{$gt:["$Grade1", "$Grade2"]}}})
svr
sumber
39

Jika kueri Anda hanya terdiri dari $whereoperator, Anda dapat meneruskan hanya ekspresi JavaScript:

db.T.find("this.Grade1 > this.Grade2");

Untuk performa yang lebih baik, jalankan operasi agregat yang memiliki $redactpipeline untuk memfilter dokumen yang memenuhi kondisi yang diberikan.

The $redactpipa menggabungkan fungsi $projectdan $matchuntuk menerapkan tingkat redaksi bidang di mana ia akan mengembalikan semua dokumen yang cocok kondisi menggunakan $$KEEPdan menghapus dari hasil pipa mereka yang tidak cocok menggunakan $$PRUNEvariabel.


Menjalankan operasi agregat berikut memfilter dokumen secara lebih efisien daripada menggunakan $whereuntuk koleksi besar karena ini menggunakan pipeline tunggal dan operator MongoDB asli, bukan dengan evaluasi JavaScript $where, yang dapat memperlambat kueri:

db.T.aggregate([
    {
        "$redact": {
            "$cond": [
                { "$gt": [ "$Grade1", "$Grade2" ] },
                "$$KEEP",
                "$$PRUNE"
            ]
        }
    }
])

yang merupakan versi yang lebih sederhana dari menggabungkan dua pipeline $projectdan $match:

db.T.aggregate([
    {
        "$project": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] },
            "Grade1": 1,
            "Grade2": 1,
            "OtherFields": 1,
            ...
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])

Dengan MongoDB 3.4 dan yang lebih baru:

db.T.aggregate([
    {
        "$addFields": {
            "isGrade1Greater": { "$cmp": [ "$Grade1", "$Grade2" ] }
        }
    },
    { "$match": { "isGrade1Greater": 1 } }
])
chridam
sumber
yang terakhir sepertinya tidak berhasil dengan saya. Bidang isGrade1Greater ditambahkan dan dievaluasi dengan benar, tetapi untuk beberapa alasan kueri cocok dengan semua baris terlepas dari nilai isGrade1Greater. Apa yang bisa menjadi penyebab perilaku ini? EDIT: Sudahlah, saya tidak meneruskan array ke aggregate () melainkan setiap agregasi sebagai parameter itu sendiri, melewatkannya.
ThatBrianDude
13

Jika performa lebih penting daripada keterbacaan dan selama kondisi Anda terdiri dari operasi aritmatika sederhana, Anda dapat menggunakan pipeline agregasi. Pertama, gunakan $ project untuk menghitung sisi kiri kondisi (ambil semua bidang ke sisi kiri). Kemudian gunakan $ match untuk membandingkan dengan konstanta dan filter. Dengan cara ini Anda menghindari eksekusi javascript. Di bawah ini adalah pengujian saya dengan 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)

Menggunakan agregat:

%timeit -n1 -r1 list(coll.aggregate([
    {
        '$project': {
            'diff': {'$subtract': ['$Grade1', '$Grade2']},
            'Grade1': 1,
            'Grade2': 1
        }
    },
    {
        '$match': {'diff': {'$gt': 0}}
    }
]))

1 loop, terbaik 1: 192 ms per loop

Menggunakan find dan $ where:

%timeit -n1 -r1 list(coll.find({'$where': 'this.Grade1 > this.Grade2'}))

1 loop, terbaik 1: 4,54 d per loop

Sina
sumber