【问题标题】:Mongodb query with fields in the same documents具有相同文档中的字段的 Mongodb 查询
【发布时间】:2011-12-08 15:05:31
【问题描述】:

我有以下 json:

{
  "a1": {"a": "b"},
  "a2": {"a": "c"}
}

如何请求同一文档中a1a2 不相等的所有文档?

【问题讨论】:

标签: mongodb


【解决方案1】:

你可以使用$where:

db.myCollection.find( { $where: "this.a1.a != this.a2.a" } )

但是,请注意,这不会很快,因为它必须启动 java 脚本引擎并迭代每个文档并检查每个文档的条件。

如果您需要对大型集合执行此查询,或者需要经常执行此查询,最好引入一个非规范化标志,例如areEqual。尽管如此,这种低选择性字段并不能产生良好的索引性能,因为他的候选集仍然很大。

【讨论】:

    【解决方案2】:

    更新

    使用从 mongo 3.6 开始可用的新 $expr 运算符,您可以在 find 查询中使用聚合表达式,如下所示:

      db.myCollection.find({$expr: {$ne: ["$a1.a", "$a2.a"] } });
    

    虽然this comment 解决了这个问题,但我认为更适合这个用例的是使用$addFields 从3.4 版开始提供的运算符,而不是$project。

    db.myCollection.aggregate([
         {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
         {"$addFields": {
               "aEq": {"$eq":["$a1.a","$a2.a"]}
             }
         },
         {"$match":{"aEq": false}} 
      ]);
    

    【讨论】:

    • $addFields "way" 对我有用,因为我使用的 DocumentDB 不支持 $expr$where 运算符
    【解决方案3】:

    为了避免 JavaScript 使用聚合框架:

    db.myCollection.aggregate([
      {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
      {"$project": {
          "a1":1,
          "a2":1,
          "aCmp": {"$cmp":["$a1.a","$a2.a"]}
        }
      },
      {"$match":{"aCmp":0}}
    ])
    

    在我们的开发服务器上,等效的 JavaScript 查询需要 7 倍的时间才能完成。

    更新(2017 年 5 月 10 日)

    我刚刚意识到我的答案没有回答这个问题,它想要不相等的值(有时我真的很慢)。这将适用于:

    db.myCollection.aggregate([
      {"$match":{"a1":{"$exists":true},"a2":{"$exists":true}}},
      {"$project": {
          "a1":1,
          "a2":1,
          "aEq": {"$eq":["$a1.a","$a2.a"]}
        }
      },
      {"$match":{"aEq": false}}
    ])
    

    如果匹配条件更改为true,则可以使用$ne 代替$eq,但我发现使用$eqfalse 更直观。

    【讨论】:

    • 对于这个用例,我认为 $addFields 会比 $project 更好。
    • 你可能是对的。 $addFields 直到 v3.4 才引入,当我回答时我们仍在使用 v3.2。如果您有时间使用$addFields 写答案,请这样做!不幸的是,我们仍在使用 v3.2。
    • 我已经使用 $addFields 运算符提交了编辑请求。
    • 感谢您的编辑,但我拒绝了。您的编辑应该是一个新的答案,因为它是一种不同的回答问题的方式。
    【解决方案4】:

    MongoDB在后台使用Javascript,所以

    {"a": "b"} == {"a": "b"}
    

    应该是false

    所以要比较每个,你必须 a1.a == a2.a

    要在 MongoDB 中执行此操作,您需要使用 $where 运算符

    db.myCollection.find({$where: "this.a1.a != this.a2.a"});
    

    这假设每个嵌入的文档都有一个属性“a”。如果不是这样,事情就会变得更加复杂。

    【讨论】:

    • $where not allowed in this atlas tier
    【解决方案5】:

    Mongo 4.4开始,对于那些想要比较子文档而不仅仅是原始值(因为{"a": "b"} == {"a": "b"}false),我们可以使用新的$function聚合允许应用自定义 javascript 函数的运算符:

    // { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 1, "y" : 2 } }
    // { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 3, "y" : 2 } }
    db.collection.aggregate(
      { $match:
        { $expr:
          { $function: {
              body: function(a1, a2) { return JSON.stringify(a1) != JSON.stringify(a2); },
              args: ["$a1", "$a2"],
              lang: "js"
          }}
        }
      }
    )
    // { "a1" : { "x" : 1, "y" : 2 }, "a2" : { "x" : 3, "y" : 2 } }
    

    $function 接受 3 个参数:

    • body,就是要应用的函数,其参数是要比较的两个字段。
    • args,其中包含 body 函数作为参数的记录中的字段。在我们的例子中,"$a1""$a2"
    • lang,这是编写 body 函数的语言。目前只有 js 可用。

    【讨论】:

      【解决方案6】:

      感谢大家解决我的问题——关于使用聚合()的答案,一开始让我感到困惑的一件事是 $eq(或 $in,或许多其他运算符)根据它的位置而具有不同的含义用过的。在 find() 或聚合的 $match 阶段,$eq 采用单个值,并选择匹配的文档:

      db.items.aggregate([{$match: {_id: {$eq: ObjectId("5be5feb45da16064c88e23d4")}}}])
      

      但是,在聚合的 $project 阶段,$eq 接受一个由 2 个表达式组成的 Array,并创建一个值为 true 或 false 的新字段:

      db.items.aggregate([{$project: {new_field: {$eq: ["$_id", "$foreignID"]}}}])
      

      顺便说一句,这是我在项目中使用的查询,用于查找链接项目列表(由于错误)链接到自身的所有项目:

      db.items.aggregate([{$project: {idIn: {$in: ["$_id","$header.links"]}, "header.links": 1}}, {$match: {idIn: true}}])
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-01-27
        • 2013-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多