通过Mongoose向MongoDB写入数据时的BMI计算问题排查
问题分析与解决方案
核心问题
- 箭头函数的this绑定错误:你在
bmi的get方法中使用了箭头函数,箭头函数不会绑定当前文档实例的this,导致无法访问profile下的weight和height属性。 - 字段定义逻辑错误:如果要将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
相关产品推荐
相关产品推荐

