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

如何关联MongoDB三个集合生成指定结构的考试成绩CSV表格

现有MongoDB集合结构
  • users 集合:
{
  "_id": {
    "$oid": String
  },
  "username": String,
}
  • marks 集合:
{
  "_id": {
    "$oid": String
  },
  "user_id": String,
  "exam_id": String,
  "question_id": String,
  "mark": String
}
  • questions 集合:
{
  "_id": {
    "$oid": String
  },
  "exam_id": String,
  "text": String,
}
需求说明

已知exam_id的取值,需要导出如下结构的CSV文件:

question1.textquestion2.text...
user1.usernamemarkmark...
user2.usernamemarkmark...
............
现有实现代码
const ws = fs.createWriteStream("result.csv");
const fastcsv = require("fast-csv");
const db = MongoClient.connect('mongodb://localhost:27017', { useUnifiedTopology: true }).db('exam');

db.collection('users')
     .aggregate([
         {
             $lookup:
             {
                 from: 'marks',
                 localField: '_id',
                 foreignField: 'user_id',
                 as: 'marks_doc'
             }
         }
     ])
    //.find({'exam_id': exam_id})
    .toArray(function(err, doc) {
        if (err) throw err;
        console.log(doc);

        fastcsv.write(doc, {headers: true}).on("finish", function() {
            console.log('done!');
        }).pipe(ws);

        db.close();
    });
调整方案

核心调整点

  1. 先查询目标exam_id对应的所有题目,建立question_id到题目标题text的映射,同时确定CSV表头顺序
  2. 关联marks集合时增加过滤条件,只保留当前exam_id对应的分数记录
  3. 将查询到的用户+分数数据转换为CSV要求的行格式,每行第一列为用户名,剩余列按题目顺序对应分数,未作答题目默认填空值避免列错位

完整调整后代码

const fs = require('fs');
const ws = fs.createWriteStream("result.csv");
const fastcsv = require("fast-csv");
const { MongoClient } = require('mongodb');

// 替换为实际的exam_id取值
const TARGET_EXAM_ID = '你的目标exam_id';

MongoClient.connect('mongodb://localhost:27017', { useUnifiedTopology: true }, async (err, client) => {
  if (err) throw err;
  const db = client.db('exam');

  // 1. 获取当前考试所有题目,生成表头和id到题目的映射
  const questions = await db.collection('questions').find({ exam_id: TARGET_EXAM_ID }).toArray();
  const qIdToText = {};
  const headers = ['用户名'];
  questions.forEach(q => {
    const qId = q._id.toString();
    qIdToText[qId] = q.text;
    headers.push(q.text);
  });

  // 2. 关联查询用户和对应考试的分数
  const userMarks = await db.collection('users').aggregate([
    {
      $lookup: {
        from: 'marks',
        let: { userId: '$_id' },
        pipeline: [
          {
            $match: {
              $expr: { $eq: ['$user_id', '$$userId'] },
              exam_id: TARGET_EXAM_ID
            }
          }
        ],
        as: 'marks_doc'
      }
    }
  ]).toArray();

  // 3. 转换为CSV要求的行格式
  const csvRows = userMarks.map(user => {
    const row = { '用户名': user.username };
    user.marks_doc.forEach(mark => {
      const qText = qIdToText[mark.question_id.toString()];
      if (qText) row[qText] = mark.mark;
    });
    // 补全未作答题目为空值
    headers.slice(1).forEach(qText => {
      if (!row[qText]) row[qText] = '';
    });
    return row;
  });

  // 4. 写入CSV文件
  fastcsv.write(csvRows, { headers: headers }).on("finish", function() {
    console.log('done!');
    client.close();
  }).pipe(ws);
});

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 00:09:00