【问题标题】:MongoDb query condition on comparing 2 fields比较 2 个字段的 MongoDb 查询条件
【发布时间】:2010-12-14 18:00:37
【问题描述】:

我有一个集合 T,有 2 个字段:Grade1Grade2,我想选择条件为 Grade1 > Grade2 的那些,我怎样才能获得类似 MySQL 中的查询?

Select * from T Where Grade1 > Grade2

【问题讨论】:

    标签: mongodb compare


    【解决方案1】:

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

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

    或更紧凑:

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

    mongodb v.3.6+的UPD

    您可以按照recent answer 中的说明使用$expr

    【讨论】:

    • 哎呀,我明白了,只是联合使用javascript或其他shell脚本,谢谢你们!
    • 你也可以做得更紧凑一点... > db.T.find({ $where : "this.Grade1 > this.Grade2" });
    • 我怎么能$where: function() { return this.Grade1 - this.Grade2 > variable }
    • 当我尝试db.T.find({$where: function() {return this.startDate == ISODate("2017-01-20T10:55:08.000Z");}}); 时,它什么也没返回,甚至集合中的一个文档也是ISODate("2017-01-20T10:55:08.000Z")。但是<=>= 似乎有效。有什么想法吗?
    • @cateyes 可能有点晚了...但是纯 javascript 在两个日期之间进行 == 比较时总是返回 false。然而,Mongo 允许您在查询中搜索日期之间的完全匹配。一种解决方法是使用 .getTime() 转换为毫秒或其他:this.startDate.getTime() == ISODate("2017-01-20T10:55:08.000Z").getTime()
    【解决方案2】:

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

    比较 query operatorsaggregation comparison operators

    常规查询:

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

    聚合查询:

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

    【讨论】:

      【解决方案3】:

      如果您的查询仅包含 $where 运算符,您可以只传入 JavaScript 表达式:

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

      为了获得更高的性能,请运行具有 $redact 管道的聚合操作来过滤满足给定条件的文档。

      $redact 管道结合了 $project$match 的功能来实现字段级别的编辑,它将返回所有使用 $$KEEP 匹配条件的文档,并从管道结果中删除使用 $$PRUNE 变量不匹配的文档。


      运行以下聚合操作过滤文档比使用 $where 更有效地过滤大型集合,因为它使用单个管道和本机 MongoDB 运算符,而不是使用 $where 进行 JavaScript 评估,这会减慢查询速度:

      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 的值如何,查询都会匹配所有行。这种行为的原因可能是什么?编辑:没关系,我没有将数组传递给聚合(),而是将每个聚合本身作为参数传递,错过了。
      【解决方案4】:

      如果性能比可读性更重要,并且只要您的条件包含简单的算术运算,您就可以使用聚合管道。首先,使用 $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 秒

      【讨论】:

        猜你喜欢
        • 2012-02-08
        • 2015-01-31
        • 1970-01-01
        • 2019-08-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-02-12
        相关资源
        最近更新 更多