You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Mongoengine实现原子化的创建或修改文档操作?

问题描述

我用Mongoengine定义了如下MongoDB文档类:

class Archive( mongoengine.Document ):
    user_id         = mongoengine.StringField()
    transaction_ids = mongoengine.ListField( default=list )

我希望该类能实现类似Python对象archive = collections.defaultdict(list)的功能,可轻松执行archive[a_user_id].append(a_transaction_id)操作,关键是要让这个操作在MongoDB中具备原子性。

我设想的伪代码如下:

def atomically_archive_transaction( a_user_id, a_transaction_id ):
    Archive.atomically_create_or_modify(
        query = { user_id=a_user_id },
        create_if_does_not_exist = { user_id=a_user_id, transaction_ids=[a_transaction_id,] },
        modify_if_does_exist = { push__transaction_id=a_transaction_id },
        write_to_disk_afterwards = 'all in the same atomic transaction',
        )

是否有可行的实现方案?

另一种思路是尝试为指定a_user_id创建新文档,若文档已存在则创建失败,随后执行原子化push操作以避免竞态条件,但我不清楚如何确保save与push在同一原子操作中完成。

可行实现方案

直接使用Mongoengine的update_one()方法配合upsert=True参数即可实现需求,这是MongoDB原生支持的原子操作,完全符合预期:

def atomically_archive_transaction(a_user_id, a_transaction_id):
    # 注意字段名是transaction_ids,你伪代码里写错成transaction_id了
    Archive.objects(user_id=a_user_id).update_one(
        push__transaction_ids=a_transaction_id,
        upsert=True
    )

原理说明

  • upsert=True参数会让MongoDB自动处理两种情况:
    1. 如果不存在user_id=a_user_id的文档,会自动创建一个新文档,其中user_id为指定值,transaction_ids初始化为包含a_transaction_id的列表;
    2. 如果文档已存在,则原子性地将a_transaction_id追加到transaction_ids列表中。
  • 整个操作是MongoDB端的单原子操作,不会出现竞态条件,完全满足对原子性的要求,不需要分开执行创建和push操作。

内容的提问来源于stack exchange,提问作者IronPillow2

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 05:25:57