GraphQL中批量Upsert订单子文档后关联更新主订单的实现困惑
问题根因
- 数组
map搭配async回调时,会将每个回调返回的Promise收集为数组,既不具备Promise实例的链式调用能力,也不能直接将这个数组作为已resolve的业务值传给后续数据库操作。 - 原map回调中,
await OrderItem.findByIdAndUpdate(...)执行后没有将结果return,即便等待异步完成也拿不到实际的条目文档。 - 原生
bulkWrite默认仅返回操作计数,不直接返回修改/新增的文档,无法直接拿到可用于关联的条目数据。
常规场景直接修复实现
不需要引入bulkWrite的情况下,仅需要两处调整即可实现预期逻辑:
- 给map内的异步回调补上return,返回upsert后的OrderItem文档
- 用*
Promise.all*等待所有条目异步操作全部完成,拿到真实的条目数组后再执行订单更新
updateOrder: async (_, { order: { id, items, ...orderFields } }, { req }) => { // 等待所有条目upsert完成,获取真实的条目文档数组 const upsertedItems = await Promise.all( items.map(async ({ id: itemId, ...itemFields }) => { // 显式返回upsert后的条目文档 return OrderItem.findByIdAndUpdate( itemId, { ...itemFields }, { new: true, upsert: true, setDefaultsOnInsert: true } ); }) ); // 将条目关联写入对应订单 const updatedOrder = await Order.findByIdAndUpdate( id, { items: upsertedItems, ...orderFields }, { new: true } ); return updatedOrder; }
之前尝试链式调用.then报错的核心原因是items.map()返回的是Promise数组,不是单个Promise实例,根本不存在then方法可供调用。
条目量较大时的bulkWrite优化方案
如果单订单的条目数量较多,单条upsert会产生较多数据库交互,此时可用bulkWrite降低开销。需要明确:bulkWrite本身不会返回操作后的文档,要拿到关联所需的条目数据,需要在批量操作完成后额外做一次批量查询:
updateOrder: async (_, { order: { id, items, ...orderFields } }, { req }) => { // 构造批量upsert的操作队列 const bulkOps = items.map(({ id: itemId, ...itemFields }) => ({ updateOne: { filter: { _id: itemId }, update: { $set: itemFields }, upsert: true } })); // 批量执行所有条目upsert await OrderItem.bulkWrite(bulkOps); // 批量查回所有操作对应的条目文档 const itemIds = items.map(item => item.id); const upsertedItems = await OrderItem.find({ _id: { $in: itemIds } }); // 更新订单的条目关联关系 const updatedOrder = await Order.findByIdAndUpdate( id, { items: upsertedItems, ...orderFields }, { new: true } ); return updatedOrder; }
实践建议:单订单条目量少于10条时,优先选择Promise.all包裹单条upsert的实现,代码逻辑更直观;条目量更大时再切换到bulkWrite方案减少数据库交互开销。
内容的提问来源于stack exchange,提问作者hellocng
相关产品推荐
相关产品推荐

