2 alanın karşılaştırılmasına ilişkin MongoDb sorgu koşulu


108

T2 alanlı bir koleksiyonum var : Grade1ve Grade2koşullu olanları seçmek Grade1 > Grade2istiyorum, MySQL'deki gibi bir sorguyu nasıl alabilirim?

Select * from T Where Grade1 > Grade2

Yanıtlar:


120

$ Nerede kullanabilirsiniz. Sadece oldukça yavaş olacağını unutmayın (her kayıtta Javascript kodu çalıştırması gerekir), bu yüzden eğer yapabiliyorsanız indekslenmiş sorgularla birleştirin.

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

veya daha kompakt:

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

Mongodb v.3.6 + için UPD

son cevapta$expr açıklandığı gibi kullanabilirsiniz


Oops, anladım, sadece ortak kullanımlı javascript veya diğer kabuk betikleri, ikinize de teşekkürler çocuklar!
Diego Cheng

16
Bunu biraz daha derli toplu yapabilirsiniz ...> db.T.find ({$ burada: "this.Grade1> this.Grade2"});
Justin Jenkins

nasıl yapabilirdim $where: function() { return this.Grade1 - this.Grade2 > variable }?
Luis González

denediğimde db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}});hiçbir şey döndürmüyor, koleksiyondaki doktorlardan biri bile ISODate("2017-01-20T10:55:08.000Z"). Ama <=ve >=iş gibi görünüyor. Herhangi bir fikir?
cateyes

@cateyes Belki biraz geç ... ama saf javascript, 2 tarih arasında == karşılaştırma yaparken her zaman yanlış döndürür. Bununla birlikte Mongo, sorgulardaki tarihler arasında tam eşleşmeleri aramanıza izin verir. Bir geçici çözüm milisaniyeye dönüştürmek için .getTime () kullanmaktır:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

53

Normal sorguda toplama işlevlerini kullanmak için $ ifade (3.6 mongo sürüm operatörü) kullanabilirsiniz.

Karşılaştırma query operatorsvs aggregation comparison operators.

Normal Sorgu:

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

Toplama Sorgusu:

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

39

Sorgunuz yalnızca $whereoperatörden oluşuyorsa, yalnızca JavaScript ifadesini iletebilirsiniz:

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

Daha yüksek performans için, $redactverilen koşulu karşılayan belgeleri filtrelemek için bir ardışık düzen içeren bir toplu işlem çalıştırın .

$redactBoru hattı işlevselliğini içerir $projectve $matchbunun kullanarak durumu uyan tüm belgeleri döndürecektir saha seviyesi redaksiyon uygulamak için $$KEEPboru hattı sonuçlarından kullanarak eşleşmiyor olanlar ve kaldırır $$PRUNEdeğişkeni.


Aşağıdaki toplu işlem filtresini çalıştırmak, belgeleri $wherebüyük koleksiyonlar için kullanmaktan daha verimli bir şekilde filtrelemektedir, çünkü bu $where, sorguyu yavaşlatabilen JavaScript değerlendirmelerinden ziyade tek bir ardışık düzen ve yerel MongoDB işleçlerini kullanır :

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

hangi iki boru hattının birleşmeyle daha basitleştirilmiş bir versiyonudur $projectve $match:

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

İle MongoDB 3.4 ve daha yeni:

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

sonuncusu benimle çalışmıyor gibi görünüyor. İsGrade1Greater alanı düzgün bir şekilde eklenir ve değerlendirilir, ancak bazı nedenlerden dolayı sorgu, isGrade1Greater değerinden bağımsız olarak tüm satırlarla eşleşir. Bu davranışın nedeni ne olabilir? DÜZENLEME: Boşver, aggregate () için bir dizi geçirmiyordum, bunun yerine her bir toplamanın kendisi bir parametre olarak, bunu kaçırdım.
ThatBrianDude

13

Performans okunabilirlikten daha önemliyse ve koşulunuz basit aritmetik işlemlerden oluştuğu sürece toplama işlem hattını kullanabilirsiniz. İlk olarak, koşulun sol tarafını hesaplamak için $ project kullanın (tüm alanları sol tarafa alın). Daha sonra bir sabitle karşılaştırmak ve filtrelemek için $ match kullanın. Bu şekilde javascript yürütmesini engellersiniz. Aşağıda python'daki testim:

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)

Toplam kullanarak:

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

1 döngü, döngü başına en iyi 1: 192 ms

Find ve $ where kullanarak:

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

1 döngü, döngü başına en iyi 1: 4,54 sn

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.