蒙戈数据库:如果不存在则插入
-
25-09-2019 - |
题
每天,我都会收到大量文件(更新)。我想要做的是插入每个尚不存在的项目。
- 我还想跟踪我第一次插入它们的时间,以及我最后一次在更新中看到它们的时间。
- 我不想有重复的文件。
- 我不想删除以前保存但不在我的更新中的文档。
- 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 分钟,而我的更新中有数百万条记录)。我很确定有内置的东西可以做到这一点,但是 update() 的文档是 mmmhhh....有点简洁....(http://www.mongodb.org/display/DOCS/Updating )
有人可以建议如何做得更快吗?
解决方案
听起来你想做一个“更新插入”。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/)
在 upsert 命令中使用 $setOnInsert 设置“insertion_date”,并使用 $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})
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
如果你想更新文档的一部分,而不是整个文档,你可以使用带有 update 的 $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 支持这种类型的选择性更新插入。我和 LeMiz 有同样的问题,并使用 更新(标准、newObj、upsert、多) 在处理“创建”和“更新”时间戳时无法正常工作。鉴于以下 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' 的文档已存在,其中包含以下内容:“名称”=“abc”,“创建”= 2010-07-12 09:09:09,“更新”= 2010-07-13 10:10:10。更新插入后,文档现在将与场景 #1 中的结果相同。无法在 upsert 中指定插入时设置哪些字段,以及更新时保留哪些字段。
我的解决方案是创建一个唯一索引 标准 字段,执行插入,然后立即对“已更新”字段执行更新。
概括
- 您有一个现有的记录集合。
- 您有一组记录,其中包含对现有记录的更新。
- 有些更新并没有真正更新任何内容,它们会重复您已有的内容。
- 所有更新都包含已经存在的相同字段,只是可能有不同的值。
- 您想要跟踪记录最后一次更改的时间以及值实际更改的位置。
注意,我假设 PyMongo 进行更改以适合您选择的语言。
指示:
使用 unique=true 的索引创建集合,这样就不会获得重复的记录。
迭代您的输入记录,创建大约 15,000 条记录的批次。对于批次中的每条记录,创建一个包含要插入的数据的字典,假设每条记录都是一条新记录。将“创建”和“更新”时间戳添加到其中。使用“ContinueOnError”标志=true将其作为批量插入命令发出,因此即使其中存在重复的键(听起来好像会有),其他所有内容的插入也会发生。这会发生得非常快。批量插入岩石,我已经达到了 15k/秒的性能水平。有关ContinueOnError的更多说明,请参见 http://docs.mongodb.org/manual/core/write-operations/
记录插入发生得非常快,因此您很快就会完成这些插入。现在,是时候更新相关记录了。通过批量检索来完成此操作,比一次检索快得多。
再次迭代所有输入记录,创建 15K 左右的批次。取出钥匙(如果有一把钥匙最好,但如果没有也无济于事)。使用 db.collectionNameBlah.find({ field :{ 美元:[ 1, 2,3 ...}) 查询。对于每条记录,确定是否有更新,如果有,则发出更新,包括更新“已更新”时间戳。
不幸的是,我们应该注意到,MongoDB 2.4 及更低版本不包含批量更新操作。他们正在努力解决这个问题。
优化关键点:
- 这些插入件将大大加快您的批量操作速度。
- 批量检索记录也会加快速度。
- 单独更新是目前唯一可能的途径,但 10Gen 正在努力解决这个问题。据推测,这将在 2.6 中完成,尽管我不确定到那时是否会完成,还有很多事情要做(我一直在关注他们的 Jira 系统)。
一般来说,在 MongoDB 中使用 update 更好,因为它只会创建文档(如果文档尚不存在),尽管我不确定如何使用 python 适配器来使用它。
其次,如果您只需要知道该文档是否存在,则仅返回数字的 count() 将是比 find_one 更好的选择,find_one 据称会从 MongoDB 传输整个文档,从而造成不必要的流量。