虽然我支持 cmets,但我认为您提出问题的方式实际上与您遇到的特定问题无关,但我会以某种方式解释 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" }
如果我们对相同的数据运行 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" }
所以这会起作用,但是可能使这不切实际的一件事是,当被比较的文档数量非常大时,我们在将这些结果压缩为数组时遇到了工作限制。
在最终的查找操作中使用 negative 也会带来一些影响,这会强制扫描集合。但平心而论,对于使用相同 否定 前提的 SQL 查询也是如此。
编辑
当然,我没有提到的是,如果结果集相反,并且您匹配 more 结果从聚合中排除项目,那么只需反转逻辑即可获得你想要的钥匙。只需将 $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 })
你应该得到相同的结果。否则请咨询您的司机。