Pymongo调用update_one时未插入完整文档的问题求助
MongoDB 6.0 批量文档插入/更新:时间戳处理及字段丢失问题
问题背景
我有大量文档需要批量处理,核心需求:
- 首次插入文档时自动添加
insert_date时间戳 - 基于
searchable字段判断重复项,重复时更新update_date时间戳
无searchable字段时流程正常:先插入所有文档,再批量添加insert_date,最终文档包含所有原始字段和时间戳:
data_inserted = collection.insert_many(results) for doc_id in data_inserted.inserted_ids: collection.update_many( filter={'_id': doc_id}, update={'$set': {'insert_date': now, }, }, upsert=True)
但当指定searchable字段(例如IR NUMBER)时,使用update_one+upsert操作后,MongoDB仅保留searchable字段和时间戳,原始文档的其他字段全部丢失:
# q_statement.searchable == 'IR NUMBER' for document in results: collection.update_one( filter={q_statement.searchable: document[q_statement.searchable], }, update={'$setOnInsert': {'insert_date': now, }, '$set': {'update_date': now, }}, upsert=True)
结果文档只有_id、IR NUMBER和insert_date,查看UpdateResult显示部分是新插入、部分是更新,但都未写入完整字段。
使用版本:
- python==3.10.3
- Django==4.0.4
- pymongo==4.2.0
解决方案
问题根源是当前update参数仅设置了时间戳,未同步原始文档的全部字段。调整update逻辑,将完整文档内容加入更新操作即可。
单文档处理(修正版)
from datetime import datetime now = datetime.utcnow() # 推荐使用UTC时间保证一致性 search_key = q_statement.searchable for document in results: # 组合更新操作:$set写入全部字段+update_date,$setOnInsert仅在插入时设置insert_date update_ops = { '$set': {**document, 'update_date': now}, '$setOnInsert': {'insert_date': now} } collection.update_one( filter={search_key: document[search_key]}, update=update_ops, upsert=True )
批量处理(高性能版)
针对大量文档,用bulk_write替代循环调用update_one,大幅提升效率:
from datetime import datetime from pymongo import UpdateOne now = datetime.utcnow() search_key = q_statement.searchable operations = [] for document in results: filter_query = {search_key: document[search_key]} update_ops = { '$set': {**document, 'update_date': now}, '$setOnInsert': {'insert_date': now} } operations.append(UpdateOne(filter_query, update_ops, upsert=True)) # 执行批量操作 result = collection.bulk_write(operations) print(f"新插入文档数: {result.upserted_count}, 更新文档数: {result.modified_count}")
逻辑说明
$set: 无论插入还是更新,都会同步原始文档的所有字段,并更新update_date。更新时会覆盖旧字段值,保证文档是最新状态;插入时则写入完整内容。$setOnInsert: 仅在**首次插入(upsert)**时生效,设置insert_date,后续更新不会修改这个字段,保留首次插入的时间戳。
内容的提问来源于stack exchange,提问作者Bammer
相关产品推荐
相关产品推荐

