Ale*_*nik 5 mongodb mongodb-query aggregation-framework
我在MongoDB中有大量文档,其中每个文档都有一个名为"name"的键,另一个键叫做"type".我想找到两个具有相同名称和不同类型的文档,一个简单的MongoDB对应的
SELECT ...
FROM table AS t1, table AS t2
WHERE t1.name = t2.name AND t1.type <> t2.type
Run Code Online (Sandbox Code Playgroud)
我可以想象一个人可以使用聚合来做到这一点:但是,集合非常大,处理它需要时间,我只是寻找一对这样的文档.
虽然我坚持认为您提出问题的方式实际上与您遇到的特定问题无关,但我会以某种方式解释 MongoDB 类型的解决方案中惯用的 SQL 方式。我认为你的实际解决方案会有所不同,但你没有向我们提出这个问题,而只是向我们提出了 SQL。
因此,请考虑以下文档作为示例集,为了清楚起见,删除此列表中的 _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" }
Run Code Online (Sandbox Code Playgroud)
如果我们对相同的数据运行 SQL,我们将得到以下结果:
a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c
Run Code Online (Sandbox Code Playgroud)
我们可以看到2个文档不匹配,然后推导出SQL操作的逻辑。因此,另一种说法是“给定键“名称”的哪些文档在键“类型”中确实有多个可能的值。
鉴于此,采用 mongo 方法,我们可以查询与给定条件不匹配的项目。因此,结果实际上相反:
a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c
Run Code Online (Sandbox Code Playgroud)
此操作将产生结果:
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"}}
])
Run Code Online (Sandbox Code Playgroud)
现在,为了获得与 SQL 查询相同的结果,我们将获取这些结果并将它们引导到另一个查询中:
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
Run Code Online (Sandbox Code Playgroud)
最终匹配结果为:
db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })
Run Code Online (Sandbox Code Playgroud)
所以这会起作用,但是可能使这不切实际的一件事是,当比较的文档数量非常大时,我们在将这些结果压缩为数组时遇到了工作限制。
它还因为在最终查找操作中使用负数而受到一些影响,这会强制扫描集合。但平心而论,使用相同否定前提的 SQL 查询也可以这样说。
当然,我没有提到的是,如果结果集相反,并且您在聚合中的排除项中匹配更多结果,那么只需反转逻辑即可获取您想要的键。只需更改 $match 如下:
{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
Run Code Online (Sandbox Code Playgroud)
这就是结果,也许不是实际的文件,但它是一个结果。因此,您不需要另一个查询来匹配否定案例。
而且,最终这是我的错,因为我太专注于惯用翻译,以至于我没有阅读你问题中的最后一行,在哪里可以说你正在寻找一份文档。
当然,目前如果结果大小大于 16MB,那么您就会陷入困境。至少在2.6版本之前,聚合操作的结果是一个游标,因此您可以像.find().
2.6中还介绍了$size用于查找文档中数组大小的运算符。因此,这将有助于删除第二个$unwind和$group,以便获得集合的长度。这会将查询更改为更快的形式:
{$match: {$gt: 1}}
Run Code Online (Sandbox Code Playgroud)
如果您只是为了个人使用或开发/测试而执行此操作,则当前可以使用 MongoDB 2.6.0-rc0。
故事的道德启示。是的,你可以做到,但是你真的想要或需要那样做吗?那么可能不会,如果您针对特定业务案例提出不同的问题,您可能会得到不同的答案。但话又说回来,这可能完全适合你想要的。
值得一提的是,当您查看 SQL 的结果时,如果您没有对这些值使用 a 或实质上使用其他分组,那么由于其他可用的类型选项,它会错误地重复多个项目。DISTINCT但这是使用 MongoDB 的过程产生的结果。
这是当前 2.4.x 版本的 shell 中聚合的输出:
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"}}
])
Run Code Online (Sandbox Code Playgroud)
因此,这样做可以让 var 作为参数传递给第二个查找中的 $nor 条件,如下所示:
{
"result" : [
{
"name" : "f",
"type" : "e"
},
{
"name" : "z",
"type" : "z"
}
],
"ok" : 1
}
Run Code Online (Sandbox Code Playgroud)
您应该得到相同的结果。否则请咨询您的司机。
| 归档时间: |
|
| 查看次数: |
3993 次 |
| 最近记录: |