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 () - это mmmhhh.... немного лаконично.... (http://www.mongodb.org/display/DOCS/Updating)
кто-то может посоветуйте, как это сделать быстрее?
8 ответов:
Похоже, вы хотите сделать "upsert". 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это заменяет, если найдешь еще-обновление полностью заблокировать. Он будет вставлен, если ключ не существует, и будет обновляться, если это так.
перед:
{"key":"value", "key2":"Ohai."}после:
{"key":"value", "key2":"value2", "key3":"value3"}вы также можете указать, какие данные вы хотите написать:
data = {"$set":{"key2":"value2"}}теперь ваш выбранный документ обновит только значение "key2" и оставит все остальное нетронутым.
начиная с MongoDB 2.4, вы можете использовать $setOnInsert (http://docs.mongodb.org/manual/reference/operator/setOnInsert/)
установите 'insertion_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, )
вы всегда можете создать уникальный индекс, который заставляет 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 }
вы можете использовать Upsert с оператором $setOnInsert.
db.Table.update({noExist: true}, {"$setOnInsert": {xxxYourDocumentxxx}}, {upsert: true})
1. Используйте Обновление.
опираясь на ответ Ван Нгуена выше, используйте обновление вместо сохранения. Это дает вам доступ к опции вставки.
Примечание: этот метод переопределяет весь документ при его обнаружении (документы)
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 с обновлением. (опять же,от документы)... Итак, если вы хотите установить...
var query = { name: 'borne' }; Model.update(query, ***{ name: 'jason borne' }***, options, callback)отправить его в качестве...
Model.update(query, ***{ $set: { name: 'jason borne' }}***, options, callback)это помогает предотвратить случайную перезапись всех ваших документов с помощью
{ name: 'jason borne' }.
Я не думаю, что mongodb поддерживает этот тип выборочного upserting. У меня та же проблема, что и у Лемиза, и с помощью обновление (критерии, 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 ' не существует: Новый документ создается с 'name' = 'abc', 'created' = 2010-07-14 11: 11: 11, и 'updated' = 2010-07-14 11:11:11.
Сценарий #2-документ с 'именем '' abc ' уже существует со следующим: 'name' = 'abc', 'created' = 2010-07-12 09:09:09, и 'updated' = 2010-07-13 10:10:10. После upsert документ теперь будет таким же, как результат в сценарии № 1. Невозможно указать в upsert, какие поля будут установлены при вставке, а какие поля будут оставлены в покое при обновлении.
мое решение состояло в том, чтобы создать уникальный индекс на критерии поля, проанализировать вставить, и сразу же после этого выполнить обновление только на поле "обновлено".
резюме
- у вас есть существующая коллекция записей.
- у вас есть набор записей, которые содержат обновления для существующих записей.
- некоторые обновления на самом деле ничего не обновляют, они дублируют то, что у вас уже есть.
- все обновления содержат те же поля, которые уже есть, просто, возможно, разные значения.
- вы хотите отслеживать, когда запись была изменена в последний раз, где значение на самом деле измененный.
обратите внимание, я предполагаю PyMongo, изменить на любой язык.
инструкции:
создайте коллекцию с индексом С unique=true, чтобы вы не получали повторяющиеся записи.
повторите ваши входные записи, создавая пакеты из них 15 000 записей или около того. Для каждой записи в пакете создайте dict, состоящий из данных, которые вы хотите вставить, предполагая, что каждый из них это будет новый рекорд. Добавьте к ним временные метки "создано" и "обновлено". Выполните это как команду пакетной вставки с флагом 'ContinueOnError' =true, поэтому вставка всего остального происходит, даже если там есть дубликат ключа (который, похоже, будет). ЭТО ПРОИЗОЙДЕТ ОЧЕНЬ БЫСТРО. Объемные вставки рок, я получил 15K / второй уровень производительности. Дополнительные примечания по ContinueOnError, см. http://docs.mongodb.org/manual/core/write-operations/
запись вставки происходят очень быстро, так что вы будете делать с этими вставками в кратчайшие сроки. Теперь пришло время обновить соответствующие записи. Сделать это с помощью пакетного извлечения, гораздо быстрее, чем по одному за раз.
повторите все ваши входные записи снова, создавая партии 15K или около того. Извлеките ключи (лучше всего, если есть один ключ, но ничего не поделаешь, если его нет). Получить эту кучу записей из Монго с БД.коллекционирование.find ({ field: { $in: [1, 2,3 ...}) запрашивать. Для каждой из этих записей определите, есть ли обновление, и если да, выполните обновление, включая обновление "обновленной" метки времени.
к сожалению, мы должны отметить, что MongoDB 2.4 и ниже не включают операции массового обновления. Они работают над этим.
Ключевые Точки Оптимизации:
- вставки значительно ускорят поднимите свои операции навалом.
- получение записей в массовом порядке также ускорит процесс.
- отдельные обновления являются единственным возможным маршрутом сейчас, но 10Gen работает над этим. Предположительно, это будет в 2.6, хотя я не уверен, что это будет закончено к тому времени, есть много вещей, которые нужно сделать (я следил за их системой Jira).
В общем, использование update лучше в MongoDB, поскольку он просто создаст документ, если он еще не существует, хотя я не уверен, как это работает с вашим адаптером python.
во-вторых, если вам нужно только знать, существует ли этот документ, count (), который возвращает только число, будет лучшим вариантом, чем find_one, который предположительно передает весь документ из вашего MongoDB, вызывая ненужный трафик.
Comments