如果不存在则插入

每天,我都会收到大量的文件(更新)。我要做的是插入每个不存在的项。

  • 我还想记录我第一次插入它们的时间,以及最后一次在更新中看到它们的时间。
  • 我不想有重复的文件。
  • 我不想删除以前保存的文档,但不在我的更新中。
  • 95% (估计)的记录每天都没有被修改。

I am using the Python driver (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分钟不到100000条记录,我有数百万条记录在更新中)。 我非常确定有一些内置的东西可以做到这一点,但是 update ()的文档是 mmmhhh..。(http://www.mongodb.org/display/DOCS/Updating)

有人能告诉我怎样做得更快吗?

249934 次浏览

一般来说,在 MongoDB 中使用 update 更好,因为如果文档还不存在,它只会创建文档,尽管我不确定如何在 Python 适配器中使用它。

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

听起来你想做 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."}

After:

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

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

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

现在,您选择的文档将只更新 key2的值,其他内容保持不变。

我不认为蒙哥布支持这种选择性翻转。我和 LeMiz 有同样的问题,当处理“创建”和“更新”的时间戳时,使用 更新(条件,newObj,upsert,multi)不能正确工作。根据下面的上述声明:

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

场景 # 1-不存在‘名称’为‘ abc’的文档: 创建新文档时使用‘ name’= ‘ abc’、‘ create’= 2010-07-1411:11:11和‘ update’= 2010-07-1411:11:11。

场景 # 2-“ name”为“ abc”的文档已经存在,具体如下: ‘ name’= ‘ abc’,‘ create’= 2010-07-1209:09:09,and‘ update’= 2010-07-1310:10:10:10。 插入之后,文档现在将与场景 # 1中的结果相同。没有办法在 upsert 中指定插入时设置哪些字段,更新时留下哪些字段。

我的解决方案是在 准则字段上创建一个惟一的索引,执行一个插入操作,然后立即在“ update”字段上执行一个更新。

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

使用 $set

如果希望更新文档的某个选项,但不希望更新整个选项,则可以使用 $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 拒绝冲突的保存。请考虑使用 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 }

摘要

  • 您有一个现有的记录集合。
  • 您有一组包含对现有记录的更新的记录。
  • 有些更新实际上并没有更新任何东西,它们只是复制了你已经拥有的东西。
  • 所有更新都包含已经存在的相同字段,只是可能有不同的值。
  • 您希望跟踪记录上次更改的时间,以及实际更改的值。

注意,我假设 PyMongo,改变以适应您选择的语言。

说明:

  1. 创建一个索引为 only = true 的集合,这样就不会得到重复的记录。

  2. 对输入记录进行迭代,创建大约15,000条记录的批处理。对于批处理中的每个记录,创建一个由要插入的数据组成的 dict,假设每个记录都将是一个新记录。将“创建”和“更新”的时间戳添加到这些。以带有‘ ContinueOnError’标志 = true 的批处理插入命令发出此命令,这样即使其中有一个重复的键(听起来似乎会有) ,也会执行其他所有内容的插入。这会很快发生的。大块石头插入,我已经得到了15k/秒的性能水平。关于 ContinueOnError 的进一步说明,请参见 < a href = “ http://docs.monGodb.org/Manual/core/write-operation/”rel = “ norefrer”> http://docs.mongodb.org/manual/core/write-operations/

    记录插入发生得非常快,因此您将很快完成这些插入。现在,是时候更新相关记录了。使用批处理检索执行此操作,比一次检索一个批处理快得多。

  3. 再次遍历所有输入记录,创建15K 左右的批处理。提取出键(最好是有一个键,但不能帮助,如果没有)。使用 db.colectionNameBlah.find ({ field: { $in: [1,2,3... })查询从 Mongo 检索这组记录。对于这些记录中的每一条,确定是否有更新,如果有,则发出更新,包括更新“更新”的时间戳。

    不幸的是,我们应该注意到,MongoDB 2.4及以下版本没有包含批量更新操作。他们正在处理这个问题。

优化要点:

  • 插入将大大加快您的批量操作。
  • 集体检索记录也会加快进度。
  • 个人更新是现在唯一可能的途径,但10Gen 正在努力。据推测,这将是在2.6,虽然我不知道它是否会完成,然后,有很多事情要做(我一直在遵循他们的 Jira 系统)。

在 MongoDB 2.4中,可以使用 $setOnInsert(http://docs.mongodb.org/manual/reference/operator/setOnInsert/)

upsert命令中使用 $set设置 insertion_date,使用 $setOnInsertlast_update_date

将伪代码转换为一个工作示例:

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

你可以使用 Upsert 和 ABc0操作员。

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

皮蒙哥的方法

Python 的 MongoDB 正式驱动程序

5% 的时间你可能想要更新和覆盖,而其他时间你想插入一个新的行,这是与 updateOneupsert完成

  • 95% (估计)的记录每天都没有被修改。

这个 Core mongoDB函数的解决方案如下:

db.collection.updateOne(filter, update, options)

根据筛选器更新集合中的单个文档。

这是通过 Pymongo 的函数 update_one(filter, new_values, upsert=True)完成的

代码示例:

# importing pymongo's MongoClient
from pymongo import MongoClient
 

conn = MongoClient('localhost', 27017)
db = conn.databaseName
 

# Filter by appliances called laptops
filter = { 'user_id': '4142480', 'question_id': '2801008' }
 

# Update number of laptops to
new_values = { "$set": { 'votes': 1400 } }
 

# Using update_one() method for single update with upsert.
db.collectionName.update_one(filter, new_values, upsert=True)

upsert=True做什么?

  • 如果没有与筛选器匹配的文档,则创建新文档。
  • 更新与筛选器匹配的单个文档。

我建议现在使用等待。