如何在MongoDB中为用户添加多测验分数?求实现方案及优化建议
问题描述
我开发了一款答题游戏,玩家完成测验后,分数会以如下文档结构提交至MongoDB:
{ _id: "b7db3e12161567", name: "james", score: 5 }
现在需要支持多个测验(已创建quiz1、quiz2、quiz3等唯一名称的测验),希望通过quizId记录玩家对应测验的分数,而非仅存储单一分数,设想的文档结构如下:
{ _id: "b7db3e12161567", name: "james", quiz1: 5, quiz2: 6 }
当前单一分数版本的代码如下:
try { const { _id, name, score, quizId } = req.body; const users = await mongodb.getDb() .db('database') .collection('users') .updateOne( { _id }, { $set: { _id, name }, $max: { score } }, { upsert: true } ); res.json(users); } catch(err) { console.log(err); throw new Error('Cannot add user score'); }
请问如何在MongoDB中实现上述需求?是否有更优的实现方案?
一、实现你设想的键值对结构
要将quizId作为文档字段存储对应分数,核心是利用JavaScript的计算属性语法动态构建更新字段,同时保留$max逻辑(确保只更新更高的分数)。修改后的代码如下:
try { const { _id, name, score, quizId } = req.body; // 动态生成以quizId为键的更新对象 const scoreUpdate = { [quizId]: score }; const users = await mongodb.getDb() .db('database') .collection('users') .updateOne( { _id }, { $set: { _id, name }, $max: scoreUpdate // 仅当新分数更高时,更新对应quizId的分数 }, { upsert: true } ); res.json(users); } catch(err) { console.log(err); throw new Error('Cannot add user score'); }
这段代码的逻辑:
- 若用户文档不存在,通过
upsert: true创建新文档,包含_id、name及当前quizId对应的分数 - 若用户已存在,保留
name和_id,仅当新分数高于已有分数时更新对应quizId的字段值
二、更优的嵌套数组结构方案
键值对结构虽直观,但测验数量增多时会导致文档字段膨胀,后续查询、统计操作也会更繁琐。推荐使用嵌套数组结构存储分数,示例文档结构如下:
{ _id: "b7db3e12161567", name: "james", scores: [ { quizId: "quiz1", score: 5 }, { quizId: "quiz2", score: 6 } ] }
该结构的优势:
- 字段数量固定,不会随测验数量增长而膨胀
- 便于批量查询、统计玩家的所有测验记录
- 支持扩展更多测验相关字段(如完成时间、答题时长等)
实现代码(使用数组过滤器)
try { const { _id, name, score, quizId } = req.body; // 第一步:尝试更新已有quizId的分数(取最大值) const updateResult = await mongodb.getDb() .db('database') .collection('users') .updateOne( { _id }, { $set: { _id, name }, $max: { "scores.$[elem].score": score }, $setOnInsert: { scores: [] } // 插入新文档时初始化scores数组 }, { upsert: true, arrayFilters: [{ "elem.quizId": quizId }] // 匹配对应quizId的数组元素 } ); // 第二步:若没有匹配到已有quizId条目,添加新的分数记录 if (updateResult.modifiedCount === 0 && updateResult.upsertedCount === 0) { await mongodb.getDb() .db('database') .collection('users') .updateOne( { _id }, { $push: { scores: { quizId, score } } } ); } res.json(updateResult); } catch(err) { console.log(err); throw new Error('Cannot add user score'); }
这段代码通过两步操作实现逻辑:先尝试更新已有测验的最高分,若未找到对应测验记录则添加新条目。
内容的提问来源于stack exchange,提问作者Joe Consterdine
相关产品推荐
相关产品推荐

