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

Express.js中Mongoose子文档数组遍历访问问题

问题根因

你踩了三个Mongoose查询的基础错误,和子文档本身的定义没有关系:

  • Model.find() 始终返回匹配查询条件的文档数组,哪怕按唯一_id查询只会匹配到1条数据,返回结果也是长度为1的数组,数组内的唯一元素才是你查到的Question文档本身。你直接把这个返回数组命名为answers,从变量定义开始就混淆了「匹配到的Question文档列表」和「Question内的answers子文档数组」两个完全不同的对象,后续的索引、长度读取自然全错。你看到控制台输出里有2条子文档,是因为这2条子文档存在于那唯一的Question文档的answers属性里,不是存在于find返回的顶层数组里,所以你读顶层数组的长度永远是1,访问索引1自然返回undefined。
  • Mongoose/MongoDB投影语法写错了。投影规则是传1表示返回对应字段,传0表示排除对应字段,你写{"answers":2}虽然因为非0值会被判定为真值、也能返回answers字段,但属于无意义的错误写法。
  • 后续调试的属性访问逻辑全错:你写的answers.answers[0]、answers[0].length、answers[0].size都不符合JS对象访问规则——answers是find返回的文档数组,必须先取到数组内的Question文档(即answers[0]),再访问这个文档上的answers属性,才能拿到真正的子文档数组。你直接在Question文档对象上调length、size方法,当然拿不到正确结果。
修复方案

两种写法,更推荐第二种,逻辑更清晰不容易出错:

方案1:修正现有find逻辑

先拿到匹配的文档列表,取出目标文档后再读取子文档数组:

const { typeOf } = require('mathjs');
const Question = require('../models/Question')

module.exports = async (req,res) =>{
    console.log("You are inside displayAnswers controller!")
    const thisQuestionID = req.body.questionID;
    console.log("Display answers ID: " + thisQuestionID);
    
    if (thisQuestionID == 0) {
        console.log("Inside thisQuestionID == 0")
        res.redirect("index");
    } else {    
        // 修正投影语法,1表示返回对应字段
        const questionList = await Question.find({"_id" : thisQuestionID}, {"answers": 1});   
        // 取出匹配到的唯一Question文档
        const targetQuestion = questionList[0];
        // 从文档上读取answers子文档数组
        const answers = targetQuestion.answers;
        // 此时日志输出完全正常
        console.log("Answers子文档数组: ", answers)
        console.log("第一条回答: ", answers[0])
        console.log("回答总数: ", answers.length)
        res.render('displayAnswers', {
            thisQuestionID,
            answers
        })
    }
}

方案2:用findById替代find(推荐)

按_id查询单个文档时直接用Mongoose内置的findById方法,它会直接返回匹配到的单个文档对象,不需要处理数组下标,代码更简洁:

const { typeOf } = require('mathjs');
const Question = require('../models/Question')

module.exports = async (req,res) =>{
    console.log("You are inside displayAnswers controller!")
    const thisQuestionID = req.body.questionID;
    console.log("Display answers ID: " + thisQuestionID);
    
    if (thisQuestionID == 0) {
        console.log("Inside thisQuestionID == 0")
        res.redirect("index");
    } else {    
        // findById直接返回单个文档,第二个参数传字段字符串即可指定返回字段
        const targetQuestion = await Question.findById(thisQuestionID, "answers");   
        const answers = targetQuestion.answers;
        console.log("Answers子文档数组: ", answers)
        console.log("第一条回答: ", answers[0])
        console.log("回答总数: ", answers.length)
        res.render('displayAnswers', {
            thisQuestionID,
            answers
        })
    }
}

额外注意:你当前Schema里datePosted的默认值写法有隐患,不要直接写default: new Date(),这样会把Schema加载时的时间固定为所有新文档的默认创建时间,正确写法是default: () => new Date(),用函数返回执行时的当前时间,避免所有文档创建时间一致的bug。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 02:27:22