这里最高被接受的答案是这样的:
uniqueIds: { $addToSet: "$_id" },
这也会返回一个名为uniqueIds的新字段和其id列表。但是如果你只想要该字段及其计数呢?那么应该这样写:
db.collection.aggregate([
{$group: { _id: {name: "$name"},
count: {$sum: 1} } },
{$match: { count: {"$gt": 1} } }
]);
为了解释这个问题,如果你来自像MySQL和PostgreSQL这样的SQL数据库,你习惯于使用聚合函数(例如COUNT()、SUM()、MIN()、MAX()),它们与GROUP BY语句一起工作,让你可以找到表中某列值出现的总次数。
SELECT COUNT(*), my_type FROM table GROUP BY my_type;
+
| COUNT(*) | my_type |
+
| 3 | Contact |
| 1 | Practice |
| 1 | Prospect |
| 1 | Task |
+
正如您所看到的,我们的输出显示了每个my_type值出现的次数。要在MongoDB中查找重复项,我们将以类似的方式解决问题。MongoDB拥有聚合操作,可以将来自多个文档的值分组在一起,并可以对分组数据执行各种操作以返回单个结果。这与SQL中的聚合函数类似的概念。
假设有一个名为contacts的集合,初始设置如下:
db.contacts.aggregate([ ... ]);
这个聚合函数接受一个聚合操作符的数组,而在我们的情况下,我们需要使用 $group 操作符,因为我们的目标是按字段计数分组数据,也就是按字段值出现的次数进行分组。
db.contacts.aggregate([
{$group: {
_id: {name: "$name"}
}
}
]);
这种方法有一点特殊。使用 group by 运算符需要 _id 字段。在这种情况下,我们正在对 $name 字段进行分组。_id 中的键名可以是任何名称,但我们在这里使用 name 是因为它很直观。
仅使用 $group 运算符运行聚合,我们将获得所有名称字段的列表(无论它们在集合中出现一次还是多次):
db.contacts.aggregate([
{$group: {
_id: {name: "$name"}
}
}
]);
{ "_id" : { "name" : "John" } }
{ "_id" : { "name" : "Joan" } }
{ "_id" : { "name" : "Stephen" } }
{ "_id" : { "name" : "Rod" } }
{ "_id" : { "name" : "Albert" } }
{ "_id" : { "name" : "Amanda" } }
注意上面聚合的工作方式。它获取具有名称字段的文档,并返回提取的名称字段的新集合。
但我们想知道的是该字段值重复出现的次数。$group运算符使用$count字段,使用$sum运算符将表达式1添加到组中每个文档的总和中。因此,$group和$sum一起返回给定字段(例如名称)的所有数字值的集体总和。
db.contacts.aggregate([
{$group: {
_id: {name: "$name"},
count: {$sum: 1}
}
}
]);
{ "_id" : { "name" : "John" }, "count" : 1 }
{ "_id" : { "name" : "Joan" }, "count" : 3 }
{ "_id" : { "name" : "Stephen" }, "count" : 2 }
{ "_id" : { "name" : "Rod" }, "count" : 3 }
{ "_id" : { "name" : "Albert" }, "count" : 2 }
{ "_id" : { "name" : "Amanda" }, "count" : 1 }
由于我们的目标是要消除重复项,因此需要一个额外的步骤。为了只获取那些拥有超过一个计数的组,我们可以使用 $match 运算符来过滤结果。在 $match 运算符内部,我们将告诉它查看计数字段并使用 $gt 运算符代表“大于”和数字1来查找大于1的计数。
db.contacts.aggregate([
{$group: { _id: {name: "$name"},
count: {$sum: 1} } },
{$match: { count: {"$gt": 1} } }
]);
{ "_id" : { "name" : "Joan" }, "count" : 3 }
{ "_id" : { "name" : "Stephen" }, "count" : 2 }
{ "_id" : { "name" : "Rod" }, "count" : 3 }
{ "_id" : { "name" : "Albert" }, "count" : 2 }
作为一个旁注,如果你正在使用像Ruby的Mongoid这样的ORM来操作MongoDB,你可能会遇到这个错误:
The 'cursor' option is required, except for aggregate with the explain argument
这很可能意味着你的ORM已经过时,正在执行MongoDB不再支持的操作。因此,要么更新你的ORM,要么找到一个解决方法。对于Mongoid,以下是我的解决方法:
module Moped
class Collection
def aggregate(*pipeline)
extract_result(session.command(aggregate: name, pipeline: pipeline.flatten, cursor: {}))
end
private
def extract_result(response)
response.key?("cursor") ? response["cursor"]["firstBatch"] : response["result"]
end
end
end