mongodb:如果不存在则插入

194 投票
10 回答
279289 浏览
提问于 2025-04-15 22:32

每天,我都会收到一批文件(更新)。我想做的是把每个还不存在的文件插入进去。

  • 我还想记录下我第一次插入这些文件的时间,以及我最后一次在更新中看到它们的时间。
  • 我不想有重复的文件。
  • 我不想删除那些之前已经保存过的文件,即使它们在这次更新中没有出现。
  • 估计有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 的值,其他的内容都不会改变。

撰写回答