Encuentre todos los documentos duplicados en una colección MongoDB por un campo clave

Supongamos que tengo una colección con algunos conjuntos de documentos. algo como esto.

{ "_id" : ObjectId("4f127fa55e7242718200002d"), "id":1, "name" : "foo"} { "_id" : ObjectId("4f127fa55e7242718200002d"), "id":2, "name" : "bar"} { "_id" : ObjectId("4f127fa55e7242718200002d"), "id":3, "name" : "baz"} { "_id" : ObjectId("4f127fa55e7242718200002d"), "id":4, "name" : "foo"} { "_id" : ObjectId("4f127fa55e7242718200002d"), "id":5, "name" : "bar"} { "_id" : ObjectId("4f127fa55e7242718200002d"), "id":6, "name" : "bar"} 

Quiero encontrar todas las entradas duplicadas en esta colección por el campo “nombre”. Por ejemplo, “foo” aparece dos veces y “bar” aparece 3 veces.

Nota: esta solución es la más fácil de entender, pero no la mejor.

Puede usar mapReduce para averiguar cuántas veces un documento contiene un determinado campo:

 var map = function(){ if(this.name) { emit(this.name, 1); } } var reduce = function(key, values){ return Array.sum(values); } var res = db.collection.mapReduce(map, reduce, {out:{ inline : 1}}); db[res.result].find({value: {$gt: 1}}).sort({value: -1}); 

La respuesta aceptada es terriblemente lenta en colecciones grandes y no devuelve los _id de los registros duplicados.

La agregación es mucho más rápida y puede devolver los _id s:

 db.collection.aggregate([ { $group: { _id: { name: "$name" }, // replace `name` here twice uniqueIds: { $addToSet: "$_id" }, count: { $sum: 1 } } }, { $match: { count: { $gte: 2 } } }, { $sort : { count : -1} }, { $limit : 10 } ]); 

En la primera etapa de la canalización de agregación, el operador de $ group agrega documentos por el campo de name y almacena en uniqueIds cada valor de _id de los registros agrupados. El operador $ sum sum los valores de los campos pasados ​​a él, en este caso la constante 1 – contando así el número de registros agrupados en el campo de count .

En la segunda etapa de la canalización, utilizamos $ match para filtrar documentos con un count de al menos 2, es decir, duplicados.

Luego, primero ordenamos los duplicados más frecuentes y limitamos los resultados al top 10.

Esta consulta generará hasta $limit registros de $limit con nombres duplicados, junto con sus _id s. Por ejemplo:

 { "_id" : { "name" : "Toothpick" }, "uniqueIds" : [ "xzuzJd2qatfJCSvkN", "9bpewBsKbrGBQexv4", "fi3Gscg9M64BQdArv", ], "count" : 3 }, { "_id" : { "name" : "Broom" }, "uniqueIds" : [ "3vwny3YEj2qBsmmhA", "gJeWGcuX6Wk69oFYD" ], "count" : 2 } 

Para obtener una solución genérica de Mongo, consulte la receta del libro de cocina de MongoDB para encontrar duplicados con el group . Tenga en cuenta que la agregación es más rápida y más potente ya que puede devolver los _id de los registros duplicados.

Para pymongo , la respuesta aceptada (usando mapReduce) no es tan eficiente. En cambio, podemos usar el método de grupo :

 $connection = 'mongodb://localhost:27017'; $con = new Mongo($connection); // mongo db connection $db = $con->test; // database $collection = $db->prb; // table $keys = array("name" => 1); Select name field, group by it // set intial values $initial = array("count" => 0); // JavaScript function to perform $reduce = "function (obj, prev) { prev.count++; }"; $g = $collection->group($keys, $initial, $reduce); echo "
"; print_r($g);

La salida será esta:

 Array ( [retval] => Array ( [0] => Array ( [name] => [count] => 1 ) [1] => Array ( [name] => MongoDB [count] => 2 ) ) [count] => 3 [keys] => 2 [ok] => 1 ) 

La consulta SQL equivalente sería: SELECT name, COUNT(name) FROM prb GROUP BY name . Tenga en cuenta que todavía tenemos que filtrar elementos con un recuento de 0 de la matriz. Una vez más, consulte la receta del libro de cocina de MongoDB para encontrar duplicados usando el group para la solución canónica utilizando el group .

Encontré información útil en el blog oficial de mongo lab: http://blog.mongolab.com/2014/03/finding-duplicate-keys-with-the-mongodb-aggregation-framework/