เงื่อนไขแบบสอบถาม MongoDb ในการเปรียบเทียบ 2 ฟิลด์


108

ฉันมีคอลเลกชันT2 ช่อง: Grade1และGrade2และฉันต้องการเลือกสิ่งที่มีเงื่อนไขGrade1 > Grade2ฉันจะรับแบบสอบถามใน MySQL ได้อย่างไร

Select * from T Where Grade1 > Grade2

คำตอบ:


121

คุณสามารถใช้ $ where โปรดทราบว่ามันจะค่อนข้างช้า (ต้องรันโค้ด Javascript ในทุกเร็กคอร์ด) ดังนั้นรวมกับเคียวรีที่จัดทำดัชนีหากทำได้

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

หรือกะทัดรัดกว่า:

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

UPD สำหรับ mongodb v.3.6 +

คุณสามารถใช้$exprตามที่อธิบายไว้ในคำตอบล่าสุด


อ๊ะฉันเข้าใจแล้วแค่ใช้จาวาสคริปต์ร่วมกันหรือเชลล์สคริปต์อื่น ๆ ขอบคุณทั้งสองคน!
Diego Cheng

16
คุณสามารถทำสิ่งนี้ให้เล็กลงได้เช่นกัน ... > db.T.find ({$ where: "this.Grade1> this.Grade2"});
Justin Jenkins

ฉันจะทำ $where: function() { return this.Grade1 - this.Grade2 > variable }อย่างไร?
Luis González

เมื่อฉันพยายามที่จะส่งกลับไม่มีอะไรแม้หนึ่งของเอกสารในการเก็บรวบรวมเป็นdb.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}}); ISODate("2017-01-20T10:55:08.000Z")แต่<=และ>=ดูเหมือนการทำงาน มีความคิดอย่างไร
cateyes

@cateyes อาจจะช้าไปหน่อย ... แต่ javascript บริสุทธิ์จะส่งคืนเท็จเสมอเมื่อทำการเปรียบเทียบ == ระหว่าง 2 วันที่ อย่างไรก็ตาม Mongo ช่วยให้คุณสามารถค้นหาการจับคู่ที่ตรงกันระหว่างวันที่ในข้อความค้นหา วิธีแก้ปัญหาอย่างหนึ่งคือการใช้. getTime () เพื่อแปลงเป็นมิลลิวินาทีหรืออะไรก็ตาม:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
leinaD_natipaC

55

คุณสามารถใช้$ expr (ตัวดำเนินการเวอร์ชัน 3.6 mongo) เพื่อใช้ฟังก์ชันการรวมในแบบสอบถามปกติ

เปรียบเทียบquery operatorsกับaggregation comparison operators.

คำถามปกติ:

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

แบบสอบถามการรวม:

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

39

หากข้อความค้นหาของคุณประกอบด้วยตัว$whereดำเนินการเท่านั้นคุณสามารถส่งผ่านได้ด้วยนิพจน์ JavaScript:

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

เพื่อประสิทธิภาพที่ดียิ่งขึ้นให้รันการดำเนินการรวมที่มี$redactไปป์ไลน์เพื่อกรองเอกสารที่ตรงตามเงื่อนไขที่กำหนด

$redactท่อรวมเอาการทำงานของ$projectและ$matchจะใช้สนาม redaction ระดับที่มันจะคืนเอกสารทั้งหมดที่ตรงกับเงื่อนไขการใช้$$KEEPและลบจากผลการท่อเหล่านั้นที่ไม่ตรงกับการใช้$$PRUNEตัวแปร


การเรียกใช้การดำเนินการรวมต่อไปนี้จะกรองเอกสารได้อย่างมีประสิทธิภาพมากกว่าการใช้$whereสำหรับคอลเลกชันขนาดใหญ่เนื่องจากใช้ไปป์ไลน์เดียวและตัวดำเนินการ MongoDB ดั้งเดิมแทนที่จะใช้การประเมิน JavaScript ด้วย$whereซึ่งอาจทำให้การสืบค้นช้าลง:

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

ซึ่งเป็นเวอร์ชันที่ง่ายกว่าในการรวมท่อทั้งสอง$projectและ$match:

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

ด้วยMongoDB 3.4และใหม่กว่า:

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

อันสุดท้ายดูเหมือนจะใช้ไม่ได้กับฉัน ฟิลด์ isGrade1Greater ได้รับการเพิ่มและประเมินผลอย่างเหมาะสม แต่ด้วยเหตุผลบางประการแบบสอบถามจะจับคู่แถวทั้งหมดไม่ว่าค่าของ isGrade1Greater จะเป็นอย่างไร อะไรคือสาเหตุของพฤติกรรมนี้? แก้ไข: ไม่เป็นไรฉันไม่ได้ส่งอาร์เรย์ไปยังการรวม () แต่การรวมแต่ละครั้งเป็นพารามิเตอร์เองก็พลาดไป
ThatBrianDude

13

ในกรณีที่ประสิทธิภาพมีความสำคัญมากกว่าความสามารถในการอ่านและตราบใดที่เงื่อนไขของคุณประกอบด้วยการคำนวณทางคณิตศาสตร์อย่างง่ายคุณสามารถใช้ไปป์ไลน์การรวมได้ ขั้นแรกให้ใช้ $ project เพื่อคำนวณด้านซ้ายมือของเงื่อนไข (นำฟิลด์ทั้งหมดไปทางซ้ายมือ) จากนั้นใช้ $ match เพื่อเปรียบเทียบกับค่าคงที่และตัวกรอง ด้วยวิธีนี้คุณจะหลีกเลี่ยงการเรียกใช้จาวาสคริปต์ ด้านล่างนี้คือการทดสอบของฉันใน 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)

ใช้การรวม:

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

1 ลูปที่ดีที่สุดคือ 1: 192 ms ต่อลูป

ใช้ find และ $ where:

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

1 ลูปดีที่สุดคือ 1: 4.54 วินาทีต่อลูป

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.