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

通过Mongoose向MongoDB写入数据时的BMI计算问题排查

问题分析与解决方案

核心问题

  1. 箭头函数的this绑定错误:你在bmi的get方法中使用了箭头函数,箭头函数不会绑定当前文档实例的this,导致无法访问profile下的weight和height属性。
  2. 字段定义逻辑错误:如果要将BMI值存入数据库,仅靠getter无法实现——getter是在读取文档时动态计算,不会把值持久化到MongoDB中;如果只是需要读取时获取BMI,应该用虚拟字段,而非定义实际的数据库字段。

方案1:用虚拟字段实现BMI动态计算(不存入数据库)

适合不需要把BMI存到数据库,仅在查询时获取的场景:

const mongoose = require('mongoose');

const usersSchema = mongoose.Schema({
    username:{
        type: String,
        required: true
    },
    first:{
        type: String,
        required: true
    },
    last:{
        type: String,
        required: true
    },
    occupation:String,
    profile:{
        age:{
            type: Number,
            required: true
        },
        sex:{
            type: String,
            required: true
        },
        height:{
            type: Number,
            required: true
        },
        weight:{
            type: Number,
            required: true
        },
        eyeColor: String,
        incomePM: Number,
        interestedIn: String
    }
});

// 定义虚拟字段bmi
usersSchema.virtual('profile.bmi').get(function() {
    // 必须用普通函数,才能通过this访问文档实例
    const heightInM = this.profile.height / 100; // 若身高单位是厘米,需转成米
    return (this.profile.weight / (heightInM ** 2)).toFixed(2); // 保留两位小数
});

// 确保查询时返回虚拟字段
usersSchema.set('toJSON', { virtuals: true });
usersSchema.set('toObject', { virtuals: true });

module.exports = mongoose.model('users', usersSchema);

方案2:用预保存钩子将BMI存入数据库

如果需要把BMI值持久化到MongoDB中,使用pre('save')钩子在保存文档前计算BMI:

const mongoose = require('mongoose');

const usersSchema = mongoose.Schema({
    username:{
        type: String,
        required: true
    },
    first:{
        type: String,
        required: true
    },
    last:{
        type: String,
        required: true
    },
    occupation:String,
    profile:{
        age:{
            type: Number,
            required: true
        },
        sex:{
            type: String,
            required: true
        },
        height:{
            type: Number,
            required: true
        },
        weight:{
            type: Number,
            required: true
        },
        bmi: { // 定义实际存储的bmi字段
            type: Number
        },
        eyeColor: String,
        incomePM: Number,
        interestedIn: String
    }
});

// 预保存钩子,保存前计算BMI
usersSchema.pre('save', function(next) {
    const heightInM = this.profile.height / 100; // 单位转换,根据实际输入调整
    this.profile.bmi = parseFloat((this.profile.weight / (heightInM ** 2)).toFixed(2));
    next();
});

// 支持更新时重新计算BMI
usersSchema.pre('findOneAndUpdate', function(next) {
    const update = this.getUpdate();
    if (update.profile?.height || update.profile?.weight) {
        const height = update.profile.height || this._update.profile.height;
        const weight = update.profile.weight || this._update.profile.weight;
        const heightInM = height / 100;
        update.profile.bmi = parseFloat((weight / (heightInM ** 2)).toFixed(2));
    }
    next();
});

module.exports = mongoose.model('users', usersSchema);

额外注意事项

  • 确认身高单位:若用户输入的是厘米,必须转换成米再计算,否则BMI值会异常偏大。
  • 方案1中,POST请求返回数据时,需确保返回转换后的JSON(虚拟字段默认不会包含在JSON输出中,因此要设置toJSON和toObject的virtuals: true)。
  • 方案2中,使用findOneAndUpdate这类更新方法时,需添加对应的预钩子,否则更新身高/体重时BMI不会自动更新。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 09:33:21