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的语言进行更改。
说明:
-
用unique = true的索引创build集合,这样就不会有重复的logging。
-
迭代您的inputlogging,创build15000个logging左右的批处理。 对于批处理中的每个logging,创build一个包含要插入的数据的字典,假定每个logging将成为新logging。 添加“创build”和“更新”时间戳到这些。 以“ContinueOnError”标志= true的forms发出批量插入命令,因此即使在那里有一个重复的键(这听起来好像会有),插入的其他事情也会发生。 这将会非常快。 散装插入摇滚,我已经得到15k /秒的性能水平。 有关ContinueOnError的更多说明,请参见http://docs.mongodb.org/manual/core/write-operations/
logging插入发生的速度非常快,所以你将很快完成这些插入。 现在是更新相关logging的时候了。 用批量检索来做到这一点,比一次更快。
-
迭代所有inputlogging,创build15K左右的批次。 提取出键(如果有一个键,最好,但如果没有,则不能提供帮助)。 使用db.collectionNameBlah.find({field:{$ in:[1,2,3 …})查询从Mongo中检索这组logging。 对于这些logging中的每一个,确定是否有更新,如果是,则发出更新,包括更新“更新”的时间戳。
不幸的是,我们应该注意到,MongoDB 2.4及以下版本不包含批量更新操作。 他们正在努力。
关键优化点:
- 插入将大大加快您的批量操作。
- 大量检索logging也会加快速度。
- 个人更新是现在唯一可能的路线,但是10Gen正在努力。 据推测,这将在2.6,但我不知道是否会完成,有很多东西要做(我一直在追随他们的Jira系统)。