我在 MongoDB 中有大量文档,每个文档都有一个名为“name”的键和另一个名为“type”的键。我想找到两个同名不同类型的文档,一个简单的 MongoDB 对应文件
SELECT ...
FROM table AS t1, table AS t2
WHERE t1.name = t2.name AND t1.type <> t2.type
我可以想象使用聚合可以做到这一点:但是,集合非常大,处理它需要时间,而我只是在寻找一对这样的文档。
最佳答案
虽然我支持这样的评论,我认为您提出问题的方式实际上与您遇到的特定问题无关,但我将在 MongoDB 类型的解决方案中以某种方式解释惯用的 SQL 方式。我认为您的实际解决方案会有所不同,但您没有向我们提出该问题,而只是向我们提出了 SQL。
因此,请考虑以下文档作为样本集,为清楚起见,删除此 list 中的 _id 字段:
{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
{ "name" : "z", "type" : "z" }
如果我们在相同的数据上运行 SQL,我们会得到这样的结果:
a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c
我们可以看到2个文档不匹配,然后算出SQL操作的逻辑。因此,另一种说法是“哪些文档给定了“名称”键 做 在键“类型”中具有超过 一个 的可能值。
鉴于此,采用 mongo 方法,我们可以查询 的项目。不要匹配给定的条件。如此有效 反向 结果:
db.sample.aggregate([
// Store unique documents grouped by the "name"
{$group: {
_id: "$name",
comp: {
$addToSet: {
name:"$name",
type: "$type"
}
}
}},
// Unwind the "set" results
{$unwind: "$comp"},
// Push the results back to get the unique count
// *note* you could not have done this with alongside $addtoSet
{$group: {
_id: "$_id",
comp: {
$push: {
name: "$comp.name",
type: "$comp.type"
}
},
count: {$sum: 1}
}},
// Match only what was counted once
{$match: {count: 1}},
// Unwind the array
{$unwind: "$comp"},
// Clean up to "name" and "type" only
{$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])
此操作将产生以下结果:
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
现在为了获得与 SQL 查询相同的结果,我们将获取这些结果并将它们引导到另一个查询中:
db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })
作为最终匹配结果到达:
{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
所以这会起作用,但是可能使这种情况不切实际的一件事是被比较的文档数量非常大,我们在将这些结果压缩到数组时遇到了工作限制。
它也受到使用 的影响。负 在最终的查找操作中,这将强制扫描集合。但平心而论,使用相同 的 SQL 查询也是如此。负 前提。
编辑
当然,我没有提到的是,如果结果集相反,而您正在匹配 更多 导致从聚合中排除项目,然后只需反转逻辑即可获得所需的键。简单地改变 $match 如下:
{$match: {$gt: 1}}
这就是结果,也许不是实际的文件,但它是结果。所以你不需要另一个查询来匹配否定的情况。
而且,归根结底,这是我的错,因为我太专注于惯用语翻译而没有 阅读 您问题中的最后一行,在哪里 做 说你在找 一 文档。
当然,当前 如果结果大小大于 16MB,那么你就被卡住了。至少直到 2.6 发布,其中聚合操作的结果是 cursor ,所以你可以像
.find()
一样迭代它.在 中也有介绍2.6 是
$size
运算符,用于查找文档中数组的大小。所以这将有助于删除第二个 $unwind
和 $group
用于获取集合的长度。这将查询更改为更快的形式:db.sample.aggregate([
{$group: {
_id: "$name",
comp: {
$addToSet: {
name:"$name",
type: "$type"
}
}
}},
{$project: {
comp: 1,
count: {$size: "$comp"}
}},
{$match: {count: {$gt: 1}}},
{$unwind: "$comp"},
{$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])
如果您只是为了个人使用或开发/测试,MongoDB 2.6.0-rc0 目前可用。
故事的道德启示。是你可以 做,但你做吗真的想要或需要这样做吗?那么可能不会,如果您针对特定业务案例提出不同的问题,您可能会得到不同的答案。但话又说回来,这可能完全适合您想要的。
笔记
值得一提的是,当您查看 SQL 的结果时,会错误地显示 重复 如果您没有使用
DISTINCT
,则由于其他可用的类型选项而导致的几个项目对于这些值或本质上是另一个分组。但这就是这个过程使用 MongoDB 产生的结果。对于亚历山大
这是当前 2.4.x 版本 shell 中聚合的输出:
{
"result" : [
{
"name" : "f",
"type" : "e"
},
{
"name" : "z",
"type" : "z"
}
],
"ok" : 1
}
因此,这样做是为了让 var 作为参数传递给第二次查找中的 $nor 条件,如下所示:
var cond = db.sample.aggregate([ .....
db.sample.find({$nor: cond.result })
你应该得到相同的结果。否则请咨询您的司机。
关于mongodb - 在 MongoDB 中查找共享一个键值的两个文档,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22115296/