MongoDB
 sql >> Base de Dados >  >> NoSQL >> MongoDB

Documentos duplicados do MongoDB mesmo depois de adicionar uma chave exclusiva


Parabéns, parece que você encontrou um bug. Isso só acontece com o MongoDB 3.0.0 em meus testes, ou pelo menos não está presente no MongoDB 2.6.6. Bug agora registrado em SERVER-17599

OBSERVAÇÃO :Não é realmente um "problema", mas confirmado "por design". Descartou a opção para a versão 3.0.0. Ainda listado na documentação embora.

O problema é que o índice não está sendo criado e erros ao tentar criá-lo em uma coleção com duplicatas existentes nos campos "chave composta". Acima, a criação do índice deve render isso no shell:
{
    "createdCollectionAutomatically" : false,
    "numIndexesBefore" : 1,
    "errmsg" : "exception: E11000 duplicate key error dup key: { : 15.0, : 1.0 }",
    "code" : 11000,
    "ok" : 0
}

Quando não houver duplicatas presentes, você pode criar o índice como está tentando no momento e ele será criado.

Então, para contornar isso, primeiro remova as duplicatas com um procedimento 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})

Então, outras inserções contendo dados duplicados não serão inseridas e o erro apropriado será registrado.

A nota final aqui é que "dropDups" é/não era uma solução muito elegante para remover dados duplicados. Você realmente quer algo com mais controle, como demonstrado acima.

Para a segunda parte, em vez de usar .insert() use o .update() método. Tem uma opção "upsert"
$collection->update(
    array( "uid" => 1, "sid" => 1 ),
    array( '$set' => $someData ),
    array( 'upsert' => true )
);

Assim, os documentos "encontrados" são "modificados" e os documentos não encontrados são "inseridos". Veja também $setOnInsert para uma maneira de criar apenas determinados dados quando o documento é realmente inserido e não quando modificado.

Para sua tentativa específica, a sintaxe correta de .update() é três argumentos. "consulta", "atualização" e "opções":
$collection->update(
    array( "uid" => 1, "sid" => 1 ),
    array(
        '$set' => array( "field" => "this" ),
        '$inc' => array( "counter" => 1 ),
        '$setOnInsert' => array( "newField" => "another" )
   ),
   array( "upsert" => true )
);

Nenhuma das operações de atualização tem permissão para "acessar o mesmo caminho" usado em outra operação de atualização nessa seção de documento "atualização".