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

  • 我还想跟踪我第一次插入它们的时间,以及我最后一次在更新中看到它们的时间。
  • 我不想有重复的文件。
  • 我不想删除以前保存但不在我的更新中的文档。
  • 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 进行更改以适合您选择的语言。

指示:

  1. 使用 unique=true 的索引创建集合,这样就不会获得重复的记录。

  2. 迭代您的输入记录,创建大约 15,000 条记录的批次。对于批次中的每条记录,创建一个包含要插入的数据的字典,假设每条记录都是一条新记录。将“创建”和“更新”时间戳添加到其中。使用“ContinueOnError”标志=true将其作为批量插入命令发出,因此即使其中存在重复的键(听起来好像会有),其他所有内容的插入也会发生。这会发生得非常快。批量插入岩石,我已经达到了 15k/秒的性能水平。有关ContinueOnError的更多说明,请参见 http://docs.mongodb.org/manual/core/write-operations/

    记录插入发生得非常快,因此您很快就会完成这些插入。现在,是时候更新相关记录了。通过批量检索来完成此操作,比一次检索快得多。

  3. 再次迭代所有输入记录,创建 15K 左右的批次。取出钥匙(如果有一把钥匙最好,但如果没有也无济于事)。使用 db.collectionNameBlah.find({ field :{ 美元:[ 1, 2,3 ...}) 查询。对于每条记录,确定是否有更新,如果有,则发出更新,包括更新“已更新”时间戳。

    不幸的是,我们应该注意到,MongoDB 2.4 及更低版本不包含批量更新操作。他们正在努力解决这个问题。

优化关键点:

  • 这些插入件将大大加快您的批量操作速度。
  • 批量检索记录也会加快速度。
  • 单独更新是目前唯一可能的途径,但 10Gen 正在努力解决这个问题。据推测,这将在 2.6 中完成,尽管我不确定到那时是否会完成,还有很多事情要做(我一直在关注他们的 Jira 系统)。

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

其次,如果您只需要知道该文档是否存在,则仅返回数字的 count() 将是比 find_one 更好的选择,find_one 据称会从 MongoDB 传输整个文档,从而造成不必要的流量。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top