La forma más rápida de eliminar documentos duplicados en mongodb

Tengo aproximadamente 1,7 millones de documentos en mongodb (en el futuro 10 m +). Algunos de ellos representan entradas duplicadas que no quiero. La estructura del documento es algo como esto:

{ _id: 14124412, nodes: [ 12345, 54321 ], name: "Some beauty" } 

El documento está duplicado si tiene al menos un nodo igual que otro documento con el mismo nombre . ¿Cuál es la forma más rápida de eliminar duplicados?

Suponiendo que desea eliminar de forma permanente los documentos que contienen una entrada duplicada de name + nodes de la colección, puede agregar un índice unique con la opción dropDups: true :

 db.test.ensureIndex({name: 1, nodes: 1}, {unique: true, dropDups: true}) 

Como dicen los documentos, tenga mucho cuidado con esto, ya que eliminará los datos de su base de datos. Haga una copia de seguridad de su base de datos primero en caso de que no funcione exactamente como espera.

ACTUALIZAR

Esta solución solo es válida a través de MongoDB 2.x ya que la opción dropDups ya no está disponible en 3.0 ( documentos ).

dropDups: true opción dropDups: true no está disponible en 3.0.

Tengo una solución con un marco de agregación para recolectar duplicados y luego eliminarlos de una vez.

Puede ser algo más lento que los cambios de “índice” del nivel del sistema. Pero es bueno considerando la forma en que desea eliminar los documentos duplicados.

a. Eliminar todos los documentos de una vez

 var duplicates = []; db.collectionName.aggregate([ { $match: { name: { "$ne": '' } // discard selection criteria }}, { $group: { _id: { name: "$name"}, // can be grouped on multiple properties dups: { "$addToSet": "$_id" }, count: { "$sum": 1 } }}, { $match: { count: { "$gt": 1 } // Duplicates considered as count greater than one }} ], {allowDiskUse: true} // For faster processing if set is larger ) // You can display result until this and check duplicates .forEach(function(doc) { doc.dups.shift(); // First element skipped for deleting doc.dups.forEach( function(dupId){ duplicates.push(dupId); // Getting all duplicate ids } ) }) // If you want to Check all "_id" which you are deleting else print statement not needed printjson(duplicates); // Remove all duplicates in one go db.collectionName.remove({_id:{$in:duplicates}}) 

segundo. Puede borrar documentos uno por uno.

 db.collectionName.aggregate([ // discard selection criteria, You can remove "$match" section if you want { $match: { source_references.key: { "$ne": '' } }}, { $group: { _id: { source_references.key: "$source_references.key"}, // can be grouped on multiple properties dups: { "$addToSet": "$_id" }, count: { "$sum": 1 } }}, { $match: { count: { "$gt": 1 } // Duplicates considered as count greater than one }} ], {allowDiskUse: true} // For faster processing if set is larger ) // You can display result until this and check duplicates .forEach(function(doc) { doc.dups.shift(); // First element skipped for deleting db.collectionName.remove({_id : {$in: doc.dups }}); // Delete remaining duplicates }) 

Crear un volcado de colección con mongodump

Colección clara

Agregar índice único

Restaurar colección con mongorestre

Encontré esta solución que funciona con MongoDB 3.4: supongo que el campo con duplicados se llama campoX

 db.collection.aggregate([ { // only match documents that have this field // you can omit this stage if you don't have missing fieldX $match: {"fieldX": {$nin:[null]}} }, { $group: { "_id": "$fieldX", "doc" : {"$first": "$$ROOT"}} }, { $replaceRoot: { "newRoot": "$doc"} } ], {allowDiskUse:true}) 

Siendo nuevo en mongoDB, pasé mucho tiempo y usé otras soluciones largas para encontrar y eliminar duplicados. Sin embargo, creo que esta solución es ordenada y fácil de entender.

Funciona al hacer coincidir primero los documentos que contienen el campo X (tuve algunos documentos sin este campo, y obtuve un resultado vacío adicional).

La siguiente etapa agrupa documentos por campoX, y solo inserta el primer documento $ en cada grupo usando $$ ROOT . Finalmente, reemplaza todo el grupo agregado por el documento encontrado usando $ first y $$ ROOT.

Tuve que agregar allowDiskUse porque mi colección es grande.

Puede agregar esto después de cualquier número de interconexiones, y aunque la documentación de $ first menciona una etapa de clasificación antes de usar $ primero , funcionó sin mi. “No se pudo publicar un enlace aquí, mi reputación es menor a 10 :(”

Puede guardar los resultados en una nueva colección agregando una etapa $ out …

Alternativamente , si uno solo está interesado en algunos campos, por ejemplo, campo1, campo2, y no en todo el documento, en la etapa de grupo sin replaceRoot:

 db.collection.aggregate([ { // only match documents that have this field $match: {"fieldX": {$nin:[null]}} }, { $group: { "_id": "$fieldX", "field1": {"$first": "$$ROOT.field1"}, "field2": { "$first": "$field2" }} } ], {allowDiskUse:true}) 
  1. La idea general es utilizar findOne https://docs.mongodb.com/manual/reference/method/db.collection.findOne/ para recuperar un id aleatorio de los registros duplicados en la colección.

  2. Elimine todos los registros en la colección que no sean la identificación aleatoria que recuperamos de la opción findOne.

Puedes hacer algo como esto si estás intentando hacerlo en Pymongo.

 def _run_query(): try: for record in (aggregate_based_on_field(collection)): if not record: continue _logger.info("Working on Record %s", record) try: retain = db.collection.find_one(find_one({'fie1d1': 'x', 'field2':'y'}, {'_id': 1})) _logger.info("_id to retain from duplicates %s", retain['_id']) db.collection.remove({'fie1d1': 'x', 'field2':'y', '_id': {'$ne': retain['_id']}}) except Exception as ex: _logger.error(" Error when retaining the record :%s Exception: %s", x, str(ex)) except Exception as e: _logger.error("Mongo error when deleting duplicates %s", str(e)) def aggregate_based_on_field(collection): return collection.aggregate([{'$group' : {'_id': "$fieldX"}}]) 

Desde el caparazón:

  1. Reemplazar find_one para encontrar One
  2. El mismo comando de eliminación debería funcionar.

Aquí hay una forma un poco más ‘manual’ de hacerlo:

Básicamente, primero, obtenga una lista de todas las claves únicas que le interesan.

Luego realice una búsqueda con cada una de esas teclas y elimine si esa búsqueda devuelve más de una.

  db.collection.distinct("key").forEach((num)=>{ var i = 0; db.collection.find({key: num}).forEach((doc)=>{ if (i) db.collection.remove({key: num}, { justOne: true }) i++ }) });