比较两个字段的 MongoDb 查询条件

我有一个集合 T,有2个字段: Grade1Grade2,我想选择那些与条件 Grade1 > Grade2,我怎样才能得到一个像在 MySQL 查询?

Select * from T Where Grade1 > Grade2
158199 次浏览

您可以使用 $where。只要注意它会相当慢(必须在每个记录上执行 Javascript 代码) ,所以如果可以的话,结合索引查询。

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

或更紧凑:

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

UPD for mongodb v. 3.6 +

您可以按照 最近的回答中的描述使用 $expr

如果查询只包含 强 > $where运算符,那么只需传入 JavaScript 表达式:

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

为了获得更好的性能,运行一个具有 < a href = “ https://docs.mongodb.com/Manual/reference/Operate/redact/# pipe. _ S _ redact”rel = “ noReferrer”> $redact 管道的聚合操作来筛选满足给定条件的文档。

< a href = “ https://docs.mongodb.com/Manual/reference/Operate/redact/# pipe. _ S _ redact”rel = “ noReferrer”> $redact 流水线整合了 < a href = “ https://docs.monGodb.com/Manual/reference/Operator/ project/# pipe. _ S _ project”rel = “ norefrer”> $project < a href = “ https://docs.mongodb.com/Manual/reference/Operator/together/match/# pipe. _ S _ match”rel = “ noReferrer”> $match 的功能,以实现字段级编校,其中它将返回使用 $$KEEP 匹配条件的所有文档,并从流水线中删除那些使用 < a href = “ https://docs.mongodb.org/Manual/reference/Operator/redact/# prune”rel = “ noReferrer”> $$PRUNE 变量不匹配的结果。


对于大型集合,运行以下聚合操作比使用 强 > $where更有效地过滤文档,因为它使用单一管道和本地 MongoDB 操作符,而不是使用 强 > $where的 JavaScript 计算,后者会降低查询的速度:

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

这是结合 < a href = “ https://docs.monGodb.com/Manual/reference/Operator/ project/# pipe. _ S _ project”rel = “ norefrer”> $project < a href = “ https://docs.mongodb.com/Manual/reference/Operator/together/match/# pipe. _ S _ match”rel = “ noReferrer”> $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 } }
])

如果性能比可读性更重要,并且只要您的条件包含简单的算术操作,您就可以使用聚合管道。首先,使用 $project 来计算条件的左侧(将所有字段放在左侧)。然后使用 $match 与常量和过滤器进行比较。这样可以避免 javascript 的执行。下面是我对 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毫秒每圈

使用 find 和 $where:

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

1回路,最好的1:4.54秒每回路

可以使用 $expr(3.6 mongo 版本操作符)在常规查询中使用聚合函数。

比较 query operatorsaggregation comparison operators

常规查询:

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

聚合查询:

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