每天,我收到一份文件(更新).我想要做的是插入每个尚不存在的项目.
我还想跟踪我第一次插入它们,以及最后一次在更新中看到它们.
我不想要有重复的文件.
我不想删除以前保存过的文档,但不在我的更新中.
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)
我的问题是它非常慢(少于100 000条记录需要40分钟,而且我在更新中有数百万条记录).我很确定有内置的东西可以做到这一点,但更新()的文件是mmmhhh ....有点简洁....(http://www.mongodb.org/display/DOCS/Updating)
有人可以建议如何更快地做到这一点?
听起来你想做一个"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
这将完全取代您的if-find-else-update块.如果密钥不存在,它将插入,如果密钥不存在则会更新.
之前:
{"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/)
使用$ setOnInsert设置'insertion_date',使用upsert命令中的$ set设置'last_update_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, )
您总是可以创建一个唯一索引,这会导致MongoDB拒绝冲突的保存.考虑使用mongodb shell完成以下操作:
> 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})
根据Van Nguyen的上述答案,使用更新而不是保存.这使您可以访问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 })
如果要更新文档的选择而不是整个文档,可以使用$ 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' }
.
我不认为mongodb支持这种类型的选择性upserting.我和LeMiz有同样的问题,并且在处理'created'和'updated'时间戳时,使用update(criteria,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 - 'name'为'abc'的文档不存在:使用'name'='abc'创建新文档,'created'= 2010-07-14 11:11:11,'updated'= 2010-07-14 11:11:11
场景#2 - 'name'为'abc'的文档已经存在以下内容:'name'='abc','created'= 2010-07-12 09:09:09,'updated'= 2010-07 -13 10:10:10 在upsert之后,文档现在将与场景#1中的结果相同.如果插入,则无法在upsert中指定要设置的字段,如果更新,则不保留哪些字段.
我的解决方案是在critera字段上创建一个唯一索引,执行插入,然后立即在'updated'字段上执行更新.
摘要
您有一个现有的记录集合.
您有一组包含现有记录更新的记录.
有些更新并没有真正更新任何内容,它们会复制您已经拥有的内容.
所有更新都包含已存在的相同字段,可能只是不同的值.
您希望跟踪上次更改记录的时间,实际更改的值.
注意,我假设PyMongo,改为适合您选择的语言.
说明:
使用unique = true的索引创建集合,这样您就不会获得重复记录.
迭代输入记录,创建大约15,000条记录的批次.对于批处理中的每个记录,创建一个由要插入的数据组成的字典,假设每个记录都是新记录.将"已创建"和"已更新"时间戳添加到这些时间戳.将此问题作为带有'ContinueOnError'标志= true的批量插入命令发出,因此即使其中存在重复键(它听起来会有),也会发生其他所有内容的插入.这将非常快.批量插入摇滚,我获得了15k /秒的性能水平.有关ContinueOnError的更多说明,请参阅http://docs.mongodb.org/manual/core/write-operations/
记录插入非常快,因此您可以立即完成这些插入操作.现在,是时候更新相关记录了.通过批量检索执行此操作,比一次快一个.
再次迭代所有输入记录,创建15K左右的批次.提取密钥(如果有一个密钥,则最好,但如果没有,则无法帮助).使用db.collectionNameBlah.find({field:{$ in:[1,2,3 ...})查询从Mongo检索这一组记录.对于每个记录,确定是否有更新,如果是,则发出更新,包括更新"更新的"时间戳.
不幸的是,我们应该注意,MongoDB 2.4及更低版本不包含批量更新操作.他们正在努力.
关键优化点:
刀片将大大加快您的操作速度.
整体检索记录也会加快速度.
个人更新现在是唯一可能的路线,但10Gen正在努力.据推测,这将是2.6,虽然我不确定它是否会在那时完成,还有很多事要做(我一直在关注他们的Jira系统).