examples - La forma más rápida de eliminar documentos duplicados en mongodb
mongodb reduce (6)
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.
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:
- Reemplazar find_one para encontrar One
- El mismo comando de eliminación debería funcionar.
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?
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++
})
});
Crear un volcado de colección con mongodump
Colección clara
Agregar índice único
Restaurar colección con mongorestore
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 $first documento $first 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})
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 ( docs ).
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
})