mongodb:如果不存在则插入
每天,我都会收到一批文件(更新)。我想做的是把每个还不存在的文件插入进去。
- 我还想记录下我第一次插入这些文件的时间,以及我最后一次在更新中看到它们的时间。
- 我不想有重复的文件。
- 我不想删除那些之前已经保存过的文件,即使它们在这次更新中没有出现。
- 估计有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)
我的问题是,这个过程非常慢(处理不到10万个记录需要40分钟,而我更新的数据有几百万条)。我很确定有一些内置的方法可以做到这一点,但关于update()的文档有点简略.... (http://www.mongodb.org/display/DOCS/Updating )
有没有人能给我一些建议,让这个过程更快一些?
10 个回答
27
你可以创建一个唯一索引,这样一来,如果有重复的数据,MongoDB 就会拒绝保存。下面是一个在 mongodb 命令行中完成的示例:
> 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 }
103
从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(
filter={
'_id': document['_id'],
},
update={
'$setOnInsert': {
'insertion_date': now,
},
'$set': {
'last_update_date': now,
},
},
upsert=True,
)
193
听起来你想要做一个叫做 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
这样就完全替代了你之前的查找-更新的步骤。如果这个键不存在,它会插入一个新记录;如果存在,它会更新这个记录。
之前的做法:
{"key":"value", "key2":"Ohai."}
现在的做法:
{"key":"value", "key2":"value2", "key3":"value3"}
你还可以指定想要写入的数据:
data = {"$set":{"key2":"value2"}}
这样,你选择的文档只会更新 key2
的值,其他的内容都不会改变。