Pergunta

Todos os dias, recebo um estoque de documentos (uma atualização). O que eu quero fazer é inserir cada item que ainda não existe.

  • Eu também quero acompanhar a primeira vez que os inseri, e a última vez que os vi em uma atualização.
  • Não quero ter documentos duplicados.
  • Não quero remover um documento que foi salvo anteriormente, mas não está na minha atualização.
  • 95% (estimado) dos registros não são modificados do dia a dia.

Estou usando o driver Python (Pymongo).

O que eu faço atualmente é (pseudo-código):

for each document in update:
      existing_document = collection.find_one(document)
      if not existing_document:
           document['insertion_date'] = now
      else:
           document = existing_document
      document['last_update_date'] = now
      my_collection.save(document)

Meu problema é que é muito lento (40 minutos para menos de 100.000 registros, e eu tenho milhões deles na atualização). Tenho certeza de que há algo embutido para fazer isso, mas o documento para atualização () é mmmhhh ... um pouco conciso .... (http://www.mongodb.org/display/docs/updating )

Alguém pode aconselhar como fazer isso mais rápido?

Foi útil?

Solução

Parece que você quer fazer um "upsert". O MongoDB tem suporte integrado para isso. Passe um parâmetro extra para a sua atualização () Ligue: {upSert: true}. Por exemplo:

key = {'key':'value'}
data = {'key2':'value2', 'key3':'value3'};
coll.update(key, data, upsert=True); #In python upsert must be passed as a keyword argument

Isso substitui completamente o seu bloco de atualização de if-end-else-else. Ele inserirá se a chave não existir e atualizará se o fizer.

Antes da:

{"key":"value", "key2":"Ohai."}

Depois:

{"key":"value", "key2":"value2", "key3":"value3"}

Você também pode especificar quais dados deseja escrever:

data = {"$set":{"key2":"value2"}}

Agora, o seu documento selecionado atualizará apenas o valor de "Key2" e deixará todo o resto intocado.

Outras dicas

A partir do MongoDB 2.4, você pode usar $ setoninsert (http://docs.mongodb.org/manual/reference/operator/setoninsert/)

Definir 'insertion_date' usando $ setoninsert e 'last_update_date' usando $ definido no seu comando upsert.

Para transformar seu pseudocódigo em um exemplo de funcionamento:

now = datetime.utcnow()
for document in update:
    collection.update_one(
        {"_id": document["_id"]},
        {
            "$setOnInsert": {"insertion_date": now},
            "$set": {"last_update_date": now},
        },
        upsert=True,
    )

Você sempre pode fazer um índice exclusivo, o que faz com que o MongoDB rejeite um salvamento conflitante. Considere o seguinte feito usando o Shell MongoDB:

> db.getCollection("test").insert ({a:1, b:2, c:3})
> db.getCollection("test").find()
{ "_id" : ObjectId("50c8e35adde18a44f284e7ac"), "a" : 1, "b" : 2, "c" : 3 }
> db.getCollection("test").ensureIndex ({"a" : 1}, {unique: true})
> db.getCollection("test").insert({a:2, b:12, c:13})      # This works
> db.getCollection("test").insert({a:1, b:12, c:13})      # This fails
E11000 duplicate key error index: foo.test.$a_1  dup key: { : 1.0 }

Você pode usar o UPSERT com o operador $ SEtonInsert.

db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})

1. Use a atualização.

Com base na resposta de Van Nguyen acima, use a atualização em vez de salvar. Isso oferece acesso à opção UpSert.

NOTA: Este método substitui o documento inteiro quando encontrado (Dos documentos)

var conditions = { name: 'borne' }   , update = { $inc: { visits: 1 }} , options = { multi: true };

Model.update(conditions, update, options, callback);

function callback (err, numAffected) {   // numAffected is the number of updated documents })

1.a. Use $ set

Se você deseja atualizar uma seleção do documento, mas não a coisa toda, pode usar o método $ set com atualização. (novamente, Dos documentos) ... então, se você quiser definir ...

var query = { name: 'borne' };  Model.update(query, ***{ name: 'jason borne' }***, options, callback)

Envie como ...

Model.update(query, ***{ $set: { name: 'jason borne' }}***, options, callback)

Isso ajuda a prevenir sobrescrever acidentalmente todos os seus documentos com { name: 'jason borne' }.

Eu não acho que o MongoDB suporta esse tipo de aprimoramento seletivo. Eu tenho o mesmo problema que Lemiz, e usando Atualização (Critérios, Newobj, Upsert, Multi) Não funciona bem ao lidar com um registro de data e hora 'criado' e 'atualizado'. Dada a seguinte declaração UpSert:

update( { "name": "abc" }, 
        { $set: { "created": "2010-07-14 11:11:11", 
                  "updated": "2010-07-14 11:11:11" }},
        true, true ) 

Cenário #1-Documento com 'nome' de 'ABC' não existe: novo documento é criado com 'nome' = 'abc', 'criado' = 2010-07-14 11:11:11 e 'atualizado' = = 2010-07-14 11:11:11.

Cenário #2-Documento com 'Nome' de 'ABC' já existe com o seguinte: 'Nome' = 'ABC', 'Criado' = 2010-07-12 09:09:09 e 'atualizado' = 2010-07 -13 10:10:10. Após o upsert, o documento agora seria o mesmo que o resultado no cenário nº 1. Não há como especificar em um upsert que os campos sejam definidos se inserir e quais campos serão deixados em paz se atualizar.

Minha solução foi criar um índice único no Critera Fields, execute uma inserção e imediatamente execute uma atualização apenas no campo 'atualizado'.

Resumo

  • Você tem uma coleção de registros existentes.
  • Você tem registros definidos que contêm atualizações para os registros existentes.
  • Algumas das atualizações realmente não atualizam nada, eles duplicam o que você já tem.
  • Todas as atualizações contêm os mesmos campos que já existem, apenas valores possivelmente diferentes.
  • Você deseja rastrear quando um registro foi alterado pela última vez, onde um valor realmente mudou.

Observe que estou presumindo Pymongo, mudando para se adequar ao seu idioma de escolha.

Instruções:

  1. Crie a coleção com um índice com exclusivo = true para que você não obtenha registros duplicados.

  2. Itera sobre seus registros de entrada, criando lotes deles de 15.000 registros ou mais. Para cada registro no lote, crie um ditado que consiste nos dados que você deseja inserir, presumindo que cada um seja um novo registro. Adicione os timestamps 'criados' e 'atualizados' a eles. Emitir isso como um comando de inserção em lote com o 'continuação' sinalizador '= true, portanto, a inserção de todo o resto acontece, mesmo que haja uma chave duplicada lá (o que parece que haverá). Isso vai acontecer muito rápido. Em massa, insere rock, eu recebi níveis de desempenho 15k/segundo. Outras notas sobre ContinueRorror, ver http://docs.mongodb.org/manual/core/write-operations/

    As inserções de registro acontecem muito rápido, então você terminará com essas inserções em pouco tempo. Agora, é hora de atualizar os registros relevantes. Faça isso com uma recuperação de lote, muito mais rápida do que uma de cada vez.

  3. Itera em todos os seus registros de entrada novamente, criando lotes de 15k ou mais. Extraia as chaves (melhor se houver uma chave, mas não poderá ser ajudado se não houver). Recupere esse grupo de registros de Mongo com um db.collectionNameblah.find ({Field: {$ in: [1, 2,3 ...}) Query. Para cada um desses registros, determine se há uma atualização e, em caso afirmativo, emita a atualização, incluindo a atualização do registro de data e hora 'atualizado'.

    Infelizmente, devemos observar, o MongoDB 2.4 e abaixo não inclui uma operação de atualização em massa. Eles estão trabalhando nisso.

Pontos de otimização de chaves:

  • As inserções acelerarão muito suas operações a granel.
  • A recuperação de registros em massa também acelerará as coisas.
  • As atualizações individuais são a única rota possível agora, mas a 10Gen está trabalhando nela. Presumivelmente, isso estará em 2.6, embora eu não tenha certeza se será concluído até então, há muitas coisas para fazer (eu tenho seguido o sistema JIRA).

Em geral, o uso da atualização é melhor no MongoDB, pois apenas criará o documento se ainda não existir, embora não tenha certeza de como trabalhar com o seu adaptador Python.

Segundo, se você precisar saber se existe ou não esse documento, o Count () que retorna apenas um número será uma opção melhor do que o Find_One, que supostamente transfere o documento inteiro do seu MongoDB, causando tráfego desnecessário.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top