sql >> Base de Datos >  >> NoSQL >> MongoDB

Documentos duplicados de MongoDB incluso después de agregar una clave única

Felicitaciones, parece que ha encontrado un error. Esto solo sucede con MongoDB 3.0.0 en mis pruebas, o al menos no está presente en MongoDB 2.6.6. Error ahora registrado en SERVER-17599

NOTA :No es realmente un "problema" sino confirmado "por diseño". Se eliminó la opción para la versión 3.0.0. Sin embargo, todavía figura en la documentación.

El problema es que el índice no se crea y se producen errores cuando intenta crearlo en una colección con duplicados existentes en los campos de "clave compuesta". En lo anterior, la creación del índice debería generar esto en el shell:

{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "errmsg" : "exception: E11000 duplicate key error dup key: { : 15.0, : 1.0 }",
    "code" : 11000,
    "ok" : 0
}

Cuando no haya duplicados presentes, puede crear el índice como lo está intentando actualmente y se creará.

Entonces, para evitar esto, primero elimine los duplicados con un procedimiento como este:

db.events.aggregate([
    { "$group": {
        "_id": { "uid": "$uid", "sid": "$sid" },
        "dups": { "$push": "$_id" },
        "count": { "$sum": 1 }
    }},
    { "$match": { "count": { "$gt": 1 } }}
]).forEach(function(doc) {
    doc.dups.shift();
    db.events.remove({ "_id": {"$in": doc.dups }});
});

db.events.createIndex({"uid":1 , "sid": 1},{unique:true})

Entonces no se insertarán más inserciones que contengan datos duplicados y se registrará el error correspondiente.

La nota final aquí es que "dropDups" no es/no era una solución muy elegante para eliminar datos duplicados. Realmente quieres algo con más control como se demostró anteriormente.

Para la segunda parte, en lugar de usar .insert() usa .update() método. Tiene una opción de "upsert"

$collection->update(
    array( "uid" => 1, "sid" => 1 ),
    array( '$set' => $someData ),
    array( 'upsert' => true )
);

Entonces los documentos "encontrados" son "modificados" y los documentos no encontrados son "insertados". Consulte también $setOnInsert para encontrar una forma de crear solo ciertos datos cuando el documento se inserta realmente y no cuando se modifica.

Para su intento específico, la sintaxis correcta de .update() son tres argumentos. "consulta", "actualizar" y "opciones":

$collection->update(
    array( "uid" => 1, "sid" => 1 ),
    array(
        '$set' => array( "field" => "this" ),
        '$inc' => array( "counter" => 1 ),
        '$setOnInsert' => array( "newField" => "another" )
   ),
   array( "upsert" => true )
);

Ninguna de las operaciones de actualización puede "acceder a la misma ruta" que se usó en otra operación de actualización en esa sección de documento de "actualización".