当前位置:  开发笔记 > 编程语言 > 正文

mongodb:如果不存在则插入

如何解决《mongodb:如果不存在则插入》经验,为你挑选了7个好方法。

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

我还想跟踪我第一次插入它们,以及最后一次在更新中看到它们.

我不想要有重复的文件.

我不想删除以前保存过的文档,但不在我的更新中.

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)

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



1> Van Nguyen..:

听起来你想做一个"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"的值,并保持其他所有内容不变.


你可以举一个例子,只在第一次插入时设置一个字段,如果存在则不更新它吗?@VanNguyen
你应该使用$ setOnInsert运算符!如果找到查询,Upsert甚至会更新文档.
-1这个答案很危险.您可以通过"键"的值找到然后删除"键",以便随后您将无法再找到它.这是一个非常不可能的用例.
我认为你答案的第一部分是错误的.coll.update将*替换*数据,除非你使用$ set.所以After实际上是:{'key2':'value2','key3':'value3'}
这几乎是我想要的!如果对象已经存在,我怎么能不触摸insertion_date字段?

2> andy..:

从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,
    )


这是正确的,您可以使用$ setOnInsert检查是否有与过滤器匹配的文档,如果找不到,则插入一些内容。请注意,尽管存在一个错误,您无法使用_id字段$ setOnInsert-它会说类似“无法修改_id字段”的内容。这是一个错误,已在v2.5.4或更高版本中修复。如果您看到此消息或问题,请获取最新版本。

3> Ram Rajamony..:

您总是可以创建一个唯一索引,这会导致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 }



4> YulCheney..:

您可以使用Upsert和$ setOnInsert运算符.

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


对于任何用pymongo查询的人来说,第三个参数应该是true或upsert = True,而不是dict

5> 小智..:

1.使用更新.

根据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 })

表1.A 使用$ 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' }.



6> 小智..:

我不认为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'字段上执行更新.



7> Kevin J. Ric..:

摘要

您有一个现有的记录集合.

您有一组包含现有记录更新的记录.

有些更新并没有真正更新任何内容,它们会复制您已经拥有的内容.

所有更新都包含已存在的相同字段,可能只是不同的值.

您希望跟踪上次更改记录的时间,实际更改的值.

注意,我假设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系统).

推荐阅读
135369一生真爱_890
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有