【发布时间】:2011-12-08 15:05:31
【问题描述】:
我有以下 json:
{
"a1": {"a": "b"},
"a2": {"a": "c"}
}
如何请求同一文档中a1 和a2 不相等的所有文档?
【问题讨论】:
-
这个问题在这里得到解答:stackoverflow.com/questions/4442453/…
标签: mongodb
我有以下 json:
{
"a1": {"a": "b"},
"a2": {"a": "c"}
}
如何请求同一文档中a1 和a2 不相等的所有文档?
【问题讨论】:
标签: mongodb
你可以使用$where:
db.myCollection.find( { $where: "this.a1.a != this.a2.a" } )
但是,请注意,这不会很快,因为它必须启动 java 脚本引擎并迭代每个文档并检查每个文档的条件。
如果您需要对大型集合执行此查询,或者需要经常执行此查询,最好引入一个非规范化标志,例如areEqual。尽管如此,这种低选择性字段并不能产生良好的索引性能,因为他的候选集仍然很大。
【讨论】:
使用从 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 运算符
为了避免 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 倍的时间才能完成。
我刚刚意识到我的答案没有回答这个问题,它想要不相等的值(有时我真的很慢)。这将适用于:
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,但我发现使用$eq 和false 更直观。
【讨论】:
$addFields 直到 v3.4 才引入,当我回答时我们仍在使用 v3.2。如果您有时间使用$addFields 写答案,请这样做!不幸的是,我们仍在使用 v3.2。
MongoDB在后台使用Javascript,所以
{"a": "b"} == {"a": "b"}
应该是false。
所以要比较每个,你必须 a1.a == a2.a
要在 MongoDB 中执行此操作,您需要使用 $where 运算符
db.myCollection.find({$where: "this.a1.a != this.a2.a"});
这假设每个嵌入的文档都有一个属性“a”。如果不是这样,事情就会变得更加复杂。
【讨论】:
从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 可用。【讨论】:
感谢大家解决我的问题——关于使用聚合()的答案,一开始让我感到困惑的一件事是 $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}}])
【讨论】: