使用flask_pymongo执行upsert操作时如何获取对应文档ID?
问题根因
flask-pymongo 是对pymongo的轻量封装,你调用的update_one方法返回的UpdateResult对象本身就仅在upsert触发新文档插入时,才会携带upserted_id属性;如果是命中已有文档执行更新,返回结果默认不包含文档_id,这是pymongo的标准设计。
可行解决方案
方案1:基于现有update_one逻辑补充查询(仅适合低并发场景)
先判断upsert行为是插入还是更新,更新场景下补充一次查询拿_id:
a = mongo.db.abcd.update_one( {'abcd': 'abcd1'}, {"$set": {"abcd": "abcd2"}}, upsert=True ) target_id = None # 新插入场景直接取返回的id if a.upserted_id is not None: target_id = a.upserted_id # 更新场景通过查询条件拿已有文档的id else: doc = mongo.db.abcd.find_one({"abcd": "abcd2"}, {"_id": 1}) if doc: target_id = doc["_id"] print(target_id)
该方案存在并发风险:如果update_one执行后、find_one执行前有其他请求修改了匹配的文档,可能拿到错误的_id。
方案2:使用find_one_and_update(推荐,原子操作)
直接用pymongo提供的原子操作方法find_one_and_update,执行upsert的同时直接返回文档内容,不管是插入还是更新都能直接拿到_id:
from pymongo import ReturnDocument doc = mongo.db.abcd.find_one_and_update( {'abcd': 'abcd1'}, {"$set": {"abcd": "abcd2"}}, upsert=True, # 配置返回更新后的文档 return_document=ReturnDocument.AFTER ) target_id = doc["_id"] print(target_id)
该方案全程为原子操作,不存在并发冲突问题,是生产环境的最优选择。
内容的提问来源于stack exchange,提问作者Tlaloc-ES
相关产品推荐
相关产品推荐

