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

升级MongoDB 5.0与Mongoose 6.0.12后executeLegacyOperation报错排查

问题:升级MongoDB 5.0与Mongoose 6.0.12后Jest测试报错Final argument to executeLegacyOperation must be a callback

问题背景

我们正在升级至MongoDB 5.0,同步将Mongoose版本更新至6.0.12。运行Jest批量测试时触发错误,提示:Final argument to executeLegacyOperation must be a callback,完整错误信息为包含GraphQLError的数组,报错路径为upsertBatchRole。

当前环境参数:

  • GraphQL版本:15.5.1(曾尝试升级至15.8.0,更高版本会引发其他错误)
  • Node版本:14.21.3

失败测试用例示例

test('should update existing items and return UPDATED status', async () => {
    const {
        data: { upsertBatchRole },
    } = await gqlClient.fetch<{ upsertBatchRole: GQLUpsertBatchRoleOutput }>(upsertBatchRoleMutation, {
        condition: {},
        input: [
            {
                name: 'new',
                id: '57',
            },
        ],
    });

    expect(upsertBatchRole).not.toBeNull();
    const { status, data } = upsertBatchRole as GQLUpsertBatchRoleOutput;

    const unitedData = uniteCategorizedArrays<GQLRole>({ ...data });

    expect(status).toBe(ms.UPDATED);
    expect(unitedData.length).toBe(1);
    expect(unitedData[0]).toHaveProperty('name');
    expect(unitedData[0]?.name).toBe('new');
    _.forEach(unitedData, (item) => {
        expect(item).toHaveProperty('id');
        expect(item).toHaveProperty('name');
    });

    const {
        data: { roleList },
    } = await gqlClient.fetch(`
        roleList {
            name
        }
    `);
    const orderedList = _.sortBy(roleList, 'name');
    expect(orderedList).toHaveLength(1);
    expect(orderedList[0].name).toBe('new');
});

问题代码片段(upsertBatchEntity.js)

async function updateBatchEntity(Collection, entitiesToUpdate, gqlInfo) {
    if (_.isEmpty(entitiesToUpdate)) return { status: ms.UPDATED, data: [] };

    const transactionSession = getCurrentTransactionSessionFromGQLInfo(gqlInfo);

    const bulkUpdate = Collection.collection.initializeOrderedBulkOp({
        session: transactionSession,
    });

    _.forEach(entitiesToUpdate, (entity) => {
        const _id = new mongoose.Types.ObjectId(entity._id);
        const entityProperties = _.omit(entity, ['id', '_id']);

        const convertedEntityProperties = convertEntityPropertiesToObjectIdWhereNeeded(Collection, entityProperties);

        bulkUpdate.find({ _id }).updateOne({ $set: { ...convertedEntityProperties, updatedAt: new Date() } });
    });

    await bulkUpdate.execute(null, { session: transactionSession });
    clearEntityLoadersCache(Collection.modelName, gqlInfo);

    return {
        status: ms.UPDATED,
        data: entitiesToUpdate,
    };
}

解决方案

错误原因

这个报错和GraphQL版本无关,是Mongoose 6.x升级后,底层MongoDB驱动对批量操作API的调用规则发生变化导致的:

  • initializeOrderedBulkOp属于遗留API,其返回的Bulk操作的execute方法不再支持传入非回调类型的参数作为第一个参数
  • 代码中await bulkUpdate.execute(null, { session: transactionSession });的null被驱动识别为回调参数,但它不是一个函数,因此触发错误

修复方案

推荐使用Mongoose官方推荐的现代Promise风格API bulkWrite替代遗留的initializeOrderedBulkOp,修改后的代码如下:

async function updateBatchEntity(Collection, entitiesToUpdate, gqlInfo) {
    if (_.isEmpty(entitiesToUpdate)) return { status: ms.UPDATED, data: [] };

    const transactionSession = getCurrentTransactionSessionFromGQLInfo(gqlInfo);

    // 构建bulkWrite所需的操作数组
    const updateOperations = entitiesToUpdate.map(entity => {
        const _id = new mongoose.Types.ObjectId(entity._id);
        const entityProperties = _.omit(entity, ['id', '_id']);
        const convertedEntityProperties = convertEntityPropertiesToObjectIdWhereNeeded(Collection, entityProperties);

        return {
            updateOne: {
                filter: { _id },
                update: { $set: { ...convertedEntityProperties, updatedAt: new Date() } }
            }
        };
    });

    // 调用bulkWrite并传入会话参数
    await Collection.bulkWrite(updateOperations, { session: transactionSession });

    clearEntityLoadersCache(Collection.modelName, gqlInfo);

    return {
        status: ms.UPDATED,
        data: entitiesToUpdate,
    };
}

备选修复方案(保留遗留API)

如果暂时不想替换API,可修改execute的调用方式——删除多余的null参数,且会话已在初始化Bulk操作时传入,无需重复传递:

// 原代码
// await bulkUpdate.execute(null, { session: transactionSession });
// 修改为
await bulkUpdate.execute();

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.02 20:28:21