2012-02-29 70 views
46

Supongamos que tengo una colección con algún conjunto de documentos. algo como esto.Buscar todos los documentos duplicados en una colección MongoDB por un campo de clave

{ "_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". P.ej. "foo" aparece dos veces y "bar" aparece 3 veces.

+0

para la eliminación de duplicados se pueden utilizar [esta solución] (http: // stackoverflow.com/a/33364353/1045444) –

Respuesta

16

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

Puede utilizar 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}); 
5

Para una solución genérica Mongo, ver el MongoDB cookbook recipe for finding duplicates using group. Tenga en cuenta que la agregación es más rápida y más potente ya que puede devolver el _id de los registros duplicados.

Para , la respuesta aceptada (usando mapReduce) no es tan eficiente. En su lugar, podemos usar el método group:

$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 "<pre>"; 
print_r($g); 

de salida será la siguiente:

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. De nuevo, consulte MongoDB cookbook recipe for finding duplicates using group para la solución canónica usando group.

+0

El enlace al libro de cocina MongoDB está obsoleto y devuelve 404. – udachny

131

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

La agregación es mucho más rápido 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 tubería de agregación, el operador $group agregados de documentos por el campo name y tiendas en uniqueIds cada _id valor de la registros agrupados. El operador $sum suma 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 count.

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

Luego, ordenar los duplicados más frecuentes primero, y limitar los resultados a los 10 primeros

Esta consulta es la salida hasta $limit registros 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 
} 
+0

Para eliminar duplicados, puede utilizar [esta solución] (http://stackoverflow.com/a/33364353/1045444) –

+0

Ahora, ¿cómo puede hacerlo? Yo llamo esto usando C#? – blueprintChris

+0

¿Utiliza esta solución el índice existente en la clave? Mi preocupación es ejecutar esto en colecciones muy grandes, donde los documentos agrupados no caben en la memoria. – Iravanchi

Cuestiones relacionadas