mongodb:插入如果不存在

我每天都收到一份文件(更新)。 我想要做的是插入每个不存在的项目。

  • 我也想跟踪我第一次插入他们,以及我最后一次看到他们的更新。
  • 我不想有重复的文件。
  • 我不想删除以前保存的文档,但不在我的更新中。
  • 95%(估计)的logging是每天不变的。

我正在使用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) 

我的问题是速度很慢(不到10万条logging需要40分钟,而且我有数百万条logging)。 我很确定这样做有内置的东西,但更新()的文件是mmmhhh ….有点简洁….( http://www.mongodb.org/display/DOCS/Updating )

有人可以build议如何更快地做到这一点?

听起来像你想做一个“upsert”。 MongoDB内置了对此的支持。 将更多parameter passing给update()调用:{upsert:true}。 例如:

 key = {'key':'value'} data = {'key2':'value2', 'key3':'value3'}; coll.update(key, data, {upsert:true}); 

这完全取代了你的if-find-else-update块。 如果密钥不存在,将会被插入,如果有密码,将会被更新。

之前:

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

后:

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

您还可以指定要写入的数据:

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

现在,您所select的文档将仅更新“key2”的值,并保持其他所有内容不变。

从MongoDB 2.4开始,你可以使用$ setOnInsert( http://docs.mongodb.org/manual/reference/operator/setOnInsert/

在upsert命令中使用$ set,使用$ setOnInsert和'last_update_date'来设置'insertion_date'。

把你的伪代码变成一个工作的例子:

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

你总是可以创build一个唯一的索引,这会导致MongoDB拒绝一个冲突的保存。 考虑使用mongodbshell完成以下操作:

 > 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 } 

您可以使用$ setOnInsert运算符的Upsert。

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

1.使用更新。

从Van Nguyen的回答可以看出,使用update而不是save。 这使您可以访问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.A 使用$ set

如果你想更新文档的select,但不是全部,你可以使用更新的$ 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支持这种select性插入。 我和LeMiz有同样的问题,使用update(criteria,newObj,upsert,multi)在处理“已创build”和“已更新”时间戳时不起作用。 鉴于下面的upsert声明:

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

场景#1 – “名称”为“abc”的文档不存在:新build文档的名称是'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中指定哪些字段被设置,以及哪些字段在更新的时候被保留。

我的解决scheme是在标准字段上创build一个唯一的索引,执行一个插入,然后立即在“更新”字段上执行更新。

一般来说,在MongoDB中使用update会更好,因为如果它还不存在,它只会创build文档,尽pipe我不知道如何使用python适配器来工作。

其次,如果您只需要知道该文档是否存在,那么只返回一个数字的count()将比find_one更好,它可能会从MongoDB传输整个文档,导致不必要的stream量。

概要

  • 您有一个现有的logging集合。
  • 您有一组logging包含对现有logging的更新。
  • 有些更新不会真正更新任何内容,而是重复您已有的内容。
  • 所有更新包含已经存在的相同字段,可能只有不同的值。
  • 您想要跟踪上次更改logging的时间,实际值更改的位置。

请注意,我假定PyMongo,根据您select的语言进行更改。

说明:

  1. 用unique = true的索引创build集合,这样就不会有重复的logging。

  2. 迭代您的inputlogging,创build15000个logging左右的批处理。 对于批处理中的每个logging,创build一个包含要插入的数据的字典,假定每个logging将成为新logging。 添加“创build”和“更新”时间戳到这些。 以“ContinueOnError”标志= true的forms发出批量插入命令,因此即使在那里有一个重复的键(这听起来好像会有),插入的其他事情也会发生。 这将会非常快。 散装插入摇滚,我已经得到15k /秒的性能水平。 有关ContinueOnError的更多说明,请参见http://docs.mongodb.org/manual/core/write-operations/

    logging插入发生的速度非常快,所以你将很快完成这些插入。 现在是更新相关logging的时候了。 用批量检索来做到这一点,比一次更快。

  3. 迭代所有inputlogging,创build15K左右的批次。 提取出键(如果有一个键,最好,但如果没有,则不能提供帮助)。 使用db.collectionNameBlah.find({field:{$ in:[1,2,3 …})查询从Mongo中检索这组logging。 对于这些logging中的每一个,确定是否有更新,如果是,则发出更新,包括更新“更新”的时间戳。

    不幸的是,我们应该注意到,MongoDB 2.4及以下版本不包含批量更新操作。 他们正在努力。

关键优化点:

  • 插入将大大加快您的批量操作。
  • 大量检索logging也会加快速度。
  • 个人更新是现在唯一可能的路线,但是10Gen正在努力。 据推测,这将在2.6,但我不知道是否会完成,有很多东西要做(我一直在追随他们的Jira系统)。