mongodb: вставить, если не существует

Каждый день я получаю запас документов (обновление). Я хочу вставить каждый элемент, который еще не существует.

  • Я также хочу отслеживать первый раз, когда я их вставил, и последний раз, когда я видел их в обновлении.
  • Я не хочу иметь дубликаты документов.
  • Я не хочу удалять документ, который был ранее сохранен, но отсутствует в моем обновлении.
  • 95% (по оценкам) записей не изменяются изо дня в день.

Я использую драйвер Python (pymongo).

В настоящее время я делаю (псевдокод):

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)

Моя проблема в том, что это очень медленно (40 минут для менее 100 000 записей, и у меня их миллионы в обновлении). Я почти уверен, что для этого есть что-то встроенное, но документ для update () - это мммхххх ... немного кратко .... DOCS /Обновление" rel =" noreferrer "> http://www.mongodb.org/display/DOCS/Updating )

Может кто-нибудь посоветовать, как это сделать быстрее?

107 голосов | спросил LeMiz 10 Mayam10 2010, 11:33:32

8 ответов


0

Звучит так, будто ты хочешь сделать "упертость". MongoDB имеет встроенную поддержку для этого. Передайте дополнительный параметр в вызов update (): {upsert: true}. Например:

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

Это полностью заменит ваш блок if-find-else-update. Он будет вставлен, если ключ не существует, и обновится, если он существует.

До:

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

После:

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

Вы также можете указать, какие данные вы хотите записать:

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

Теперь выбранный вами документ обновит значение только «key2» и оставит все остальное без изменений.

ответил Van Nguyen 27 Maypm10 2010, 22:17:44
0

Начиная с MongoDB 2.4, вы можете использовать $ setOnInsert ( http: //docs. mongodb.org/manual/reference/operator/setOnInsert/)

Установите 'inserttion_date', используя $ setOnInsert и 'last_update_date', используя $ set в вашей команде upsert.

Чтобы превратить ваш псевдокод в рабочий пример:

now = datetime.utcnow()
for document in update:
    collection.update_one(
        {"_id": document["_id"]},
        {
            "$setOnInsert": {"insertion_date": now},
            "$set": {"last_update_date": now},
        },
        upsert=True,
    )
ответил andy 8 J000000Monday13 2013, 22:18:36
0

Вы всегда можете создать уникальный индекс, который заставит MongoDB отклонить конфликтующее сохранение. Рассмотрим следующее с использованием оболочки 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 }
ответил Ram Rajamony 13 ThuEurope/Moscow2012-12-13T00:10:47+04:00Europe/Moscow12bEurope/MoscowThu, 13 Dec 2012 00:10:47 +0400 2012, 00:10:47
0

Вы можете использовать Upsert с оператором $ setOnInsert.

db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})
ответил YulCheney 14 thEurope/Moscowp30Europe/Moscow09bEurope/MoscowSun, 14 Sep 2014 19:53:40 +0400 2014, 19:53:40
0

1. Используйте обновление.

Рисуя ответ Ван Нгуена выше, используйте обновление вместо сохранения. Это дает вам доступ к опции upsert.

ПРИМЕЧАНИЕ . Этот метод переопределяет весь документ при его обнаружении ( из документов . )

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.а. Использовать $ set

Если вы хотите обновить выбранный документ, но не весь документ, вы можете использовать метод $ set с update. (опять же, из документов ) ... Итак, если вы хотите установить ...

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

Отправить как ...

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

Это помогает предотвратить случайную перезапись всех ваших документов с помощью { name: 'jason borne' }.

ответил Meshach Jackson 24 PMpTue, 24 Apr 2012 21:25:01 +040025Tuesday 2012, 21:25:01
0

Я не думаю, что mongodb поддерживает этот тип выборочного апсертирования. У меня та же проблема, что и у LeMiz, и использование update (критерии, newObj, upsert, multi) не работает правильно, когда речь идет как о «созданной», так и «обновленной» временной метке. Учитывая следующее утверждение upsert:

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

Сценарий № 1 - документ с «именем» из «abc» не существует: Новый документ создается с именем «=» abc »,« создан »= 2010-07-14 11:11:11 и« обновлен »= 2010-07-14 11:11:11.

Сценарий № 2 - документ с «именем» из «abc» уже существует со следующим: 'name' = 'abc', 'creat' = 2010-07-12 09:09:09 и 'updated' = 2010-07-13 10:10:10. После отката документ теперь будет таким же, как результат в сценарии № 1. В upsert нет способа указать, какие поля будут установлены при вставке, а какие поля останутся одними при обновлении.

Мое решение состояло в том, чтобы создать уникальный индекс в полях critera , выполнить вставку и сразу после этого выполнить обновление только в поле «обновлено».

ответил Yonsink 16 J000000Friday10 2010, 01:38:21
0

Резюме

  • У вас есть существующая коллекция записей.
  • У вас есть набор записей, которые содержат обновления существующих записей.
  • Некоторые обновления на самом деле ничего не обновляют, они дублируют то, что у вас уже есть.
  • Все обновления содержат те же поля, которые уже есть, возможно, разные значения.
  • Вы хотите отслеживать, когда запись в последний раз изменялась, где значение действительно изменилось.

Обратите внимание, я предполагаю, что PyMongo измените в соответствии с вашим языком.

Инструкция:

  1. Создайте коллекцию с индексом с уникальным = true, чтобы вы не получили повторяющиеся записи.

  2. Итерируйте свои входные записи, создавая их из 15 000 записей или около того. Для каждой записи в пакете создайте dict, состоящий из данных, которые вы хотите вставить, предполагая, что каждая будет новой записью. Добавьте к ним «созданные» и «обновленные» временные метки. Выполните это как команду пакетной вставки с флагом 'ContinueOnError' = true, чтобы вставка всего остального происходила, даже если там есть дубликат ключа (который, как кажется, будет). ЭТО БУДЕТ ОЧЕНЬ БЫСТРО. Массовая вставка рок, я получил 15k /секунду уровней производительности. Дополнительные примечания по ContinueOnError см. В http://docs.mongodb.org/manual/core/написать-операции /

    Вставка записей происходит ОЧЕНЬ быстро, так что с этими вставками вы быстро закончите. Теперь пришло время обновить соответствующие записи. Делайте это с пакетным извлечением, намного быстрее, чем по одному за раз.

  3. Повторяйте все входные записи снова, создавая пакеты по 15 КБ или около того. Извлеките ключи (лучше всего, если есть один ключ, но ничего не поделаешь, если его нет). Получите этот набор записей из Mongo с помощью запроса db.collectionNameBlah.find ({field: {$ in: [1, 2,3 ...}). Для каждой из этих записей определите, есть ли обновление, и если да, выпустите обновление, включая обновление «обновленной» метки времени.

    К сожалению, мы должны отметить, что MongoDB 2.4 и ниже НЕ включает в себя операцию массового обновления. Они работают над этим.

Ключевые точки оптимизации:

  • Вставки значительно ускорят ваши операции.
  • Массовое извлечение записей также ускорит процесс.
  • Индивидуальные обновления - это единственный возможный маршрут, но 10Gen работает над ним. Предположительно, это будет в версии 2.6, хотя я не уверен, будет ли она завершена к тому времени, есть много вещей, которые нужно сделать (я следую их системе Jira).
ответил Kevin J. Rice 9 J0000006Europe/Moscow 2013, 20:55:28
0

В общем, использовать обновление лучше в MongoDB, так как оно просто создаст документ, если он еще не существует, хотя я не уверен, как работать с вашим адаптером python.

Во-вторых, если вам нужно только узнать, существует ли этот документ, лучше использовать функцию count (), которая возвращает только число, чем find_one, которая предположительно передает весь документ из вашей MongoDB, вызывая ненужный трафик.

ответил Thomas R. Koll 10 Maypm10 2010, 13:34:38

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132