有没有办法在 Pymongo 3.0 中跳过 insert_many 的现有 _id?

Is there a way to skip over existing _id's for insert_many in Pymongo 3.0?

我正在更新一个包含数百万文档且 _id 冲突少于 10 个的数据库。

我目前正在使用 PyMongo 模块通过 insert_many 进行批量插入:

  1. 正在查询数据库以查看 _id 是否存在
  2. 如果 _id 不存在,则将文档添加到数组中
  3. 使用 insert_many 插入数据库,一次插入 1000 个文档。

几百万个文档中只有大约 10 个冲突,我目前正在为每个 _id 查询数据库。我认为如果我可以减少查询过程,我可以将总体插入时间减少一两天。

是否有类似于 upsert 的东西,也许只插入不存在的文档?

处理此问题以及 "inserting/updating" 许多文档的有效方法是使用 Bulk Operations API 提交 "batches" 中的所有内容,并有效地发送所有内容并接收"singular response" 确认中。

这可以通过两种方式处理。

首先要忽略主键或其他索引上的任何"duplicate errors"然后你可以使用"UnOrdered"操作形式:

bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=False)
for doc in docs:
    bulk.insert(doc)

response = bulk.execute()

那里的 "UnOrdered" 或 false 参数意味着操作可以按任何顺序执行,并且 "whole" 批处理将完成,任何实际错误都只是 "reported" 在响应中。所以这是基本上 "ignore" 重复并继续前进的一种方法。

替代方法大致相同,但使用 "upsert" 功能以及 $setOnInsert:

bulk = pymongo.bulk.BulkOperationBuilder(collection,ordered=True)
for doc in docs:
    bulk.find({ "_id": doc["_id"] }).upsert().updateOne({
        "$setOnInsert": doc
    })

response = bulk.execute()

.find() 中的 "query" 部分用于使用文档的 "primary key" 或 "unique keys" 查询文档的存在。如果未找到匹配项,则会在创建新文档时出现 "upsert"。由于所有的修改内容都在$setOnInsert之内,所以只有在"upsert"时才修改这里的文档字段。否则,虽然文档是 "matched",但在此运算符下保存的数据实际上没有任何更改。

本例中的 "Ordered" 意味着每个语句实际上都是按照创建它的 "same" 顺序提交的。此外,这里的任何 "errors" 都会停止更新(在该点发生错误的地方),以便不会提交更多操作。它是可选的,但可能建议用于正常的 "dupliate" 行为,其中后面的语句 "duplicate" 前一个的数据。

因此,为了更高效的写入,一般的想法是使用 "Bulk" API 并相应地构建您的操作。这里的选择实际上取决于来源中的 "order of insertion" 对您是否重要。

当然,相同的 "ordered"=False 操作适用于 insert_many,后者在较新的驱动程序版本中实际使用 "Bulk" 操作。但是坚持使用可以 "mix" 通过简单 API.

操作的通用界面,您将获得更大的灵活性

虽然 Blakes 的回答很好,但在大多数情况下,使用 ordered=False 参数并捕获 BulkWriteError 以防重复。

try:
    collection.insert_many(data, ordered=False)
except BulkWriteError:
    logger.info('Duplicates were found.')