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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 16:10:29