具有相同文档中的字段的 Mongodb 查询

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8433046/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 12:21:40  来源:igfitidea点击:

Mongodb query with fields in the same documents

mongodb

提问by Rusty Robot

I have the following json:

我有以下 json:

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

How can I request all documents where a1and a2are not equal in the same document?

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

回答by mnemosyn

You could use $where:

你可以使用$where

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

However, be aware that this won't be very fast, because it will have to spin up the java script engine and iterate each and every document and check the condition for each.

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

If you need to do this query for large collections, or very often, it's best to introduce a denormalized flag, like areEqual. Still, such low-selectivity fields don't yield good index performance, because he candidate set is still large.

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

回答by Paul

To avoid JavaScript use the aggregation framework:

为了避免 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}}
])

On our development server the equivalent JavaScript query takes 7x longer to complete.

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

Update (10 May 2017)

更新(2017 年 5 月 10 日)

I just realized my answer didn't answer the question, which wanted values that are not equal (sometimes I'm really slow). This will work for that:

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

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

$necould be used in place of $eqif the match condition was changed to truebut I find using $eqwith falseto be more intuitive.

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

回答by Mohammed Essehemy

update

更新

using the new $exproperator available as of mongo 3.6 you can use aggregate expressions in find query like this:

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

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


Although this commentsolves the problem, I think a better match for this use case would be to use $addFieldsoperator available as of version 3.4 instead of $project.

尽管此注释解决了问题,但我认为更适合此用例的是使用$addFields操作符从 3.4 版开始可用,而不是 $project。

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

回答by Cormac Mulhall

MongoDB uses Javascript in the background, so

MongoDB 在后台使用 Javascript,所以

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

would be false.

false

So to compare each you would have to a1.a == a2.a

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

To do this in MongoDB you would use the $where operator

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

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

This assumes that each embedded document will have a property "a". If that isn't the case things get more complicated.

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

回答by Blob

Thanks all for solving my problem -- concerning the answers that use aggregate(), one thing that confused me at first is that $eq (or $in, or lots of other operators) has different meaning depending on where it is used. In a find(), or the $match phase of aggregation, $eq takes a single value, and selects matching documents:

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

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

However, in the $project phase of aggregation, $eq takes an Array of 2 expressions, and makes a new field with value true or false:

然而,在聚合的 $project 阶段, $eq 接受一个包含 2 个表达式的数组,并创建一个值为 true 或 false 的新字段:

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

In passing, here's the query I used in my project to find all items whose list of linked items (due to a bug) linked to themselves:

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

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

回答by Xavier Guihot

Starting in Mongo 4.4, for those that want to compare sub-documentsand not only primitive values (since {"a": "b"} == {"a": "b"}is false), we can use the new $functionaggregation operator that allows applying a custom javascript function:

从 开始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 } }

$functiontakes 3 parameters:

$function需要3个参数:

  • body, which is the function to apply, whose parameter are the two fields to compare.
  • args, which contains the fields from the record that the bodyfunction takes as parameter. In our case, both "$a1"and "$a2".
  • lang, which is the language in which the bodyfunction is written. Only jsis currently available.
  • body,这是要应用的函数,其参数是要比较的两个字段。
  • args,其中包含该body函数作为参数的记录字段。在我们的例子中,"$a1""$a2"
  • lang,这body是编写函数的语言。仅js当前可用。