【问题标题】:Cast to Number failed for value "NaN" (type number) at path on a computed field Mongoose在计算字段 Mongoose 上的路径中,值“NaN”(类型编号)转换为数字失败
【发布时间】:2021-06-01 05:18:07
【问题描述】:

在我的架构中,我添加了 3 个非必需参数 ratingstotalRating,它们用于自动计算 averageRating。更新现有记录时一切顺利,但单参数记录创建失败并显示Product.create error: Error: Product validation failed: averageRating: Cast to Number failed for value "NaN" (type number) at path "averageRating"。 我尝试在请求正文中添加 ratingstotalRating 并将它们的默认值设置为 0 但没有任何帮助。仍然收到错误。你明白为什么吗?

架构:


const mongoose = require('mongoose');

const productSchema = new mongoose.Schema({
    createdOnDate: { type: Number, required: true },
    name: { type: String, required: true },
    brand: { type: String, required: true },
    price: { type: Number, required: true },
    description: { type: String, required: true },
    category: { type: String, required: true },
    city: { type: String, required: true },
    region: { type: String, required: true },
    country: { type: String, required: true },
    vendor: { type: String, required: true },
    barcode: { type: String, required: true },
    imageUrl: { type: String, required: true },
    fullImages: { type: Array, required: true },
    thumbNails: { type: Array, required: true },
    // productImage: Uint8Array,
    minimumStock: { type: Number, required: true },
    availableQuantity: { type: Number, required: true },
    soldQuantity: { type: Number, required: true },
    totalRating: { type: Number, required: false, default: 0 },
    ratings: { type: Number, required: false, default: 0 },
    averageRating: {
        type: Number, required: false, default: function () {
            return (this.totalRating / this.ratings)
        }
    },

},
    { timestamps: true });

module.exports = mongoose.model('Product', productSchema, 'Products');

发送 json:

Mongoos createProduct req.body:  {
  name: 'someName',
  quantity: 5,
  price: 5.5,
  city: 'Bologna',
  region: 'Emilia-Romagna',
  country: 'Italy',
  category: 'bikes',
  vendor: 'zazza zenigata',
  createdOnDate: 132468754,
  availableQuantity: 5,
  soldQuantity: 0,
  minimumStock: 10,
  imageUrl: 'someUrl',
  barcode: 'someCode',
  description: 'someDescription',
  brand: 'someBrand',
  totalRating: 0,
  ratings: 0
}


错误:

Product.create error: Error: Product validation failed: averageRating: Cast to Number failed for value "NaN" (type number) at path "averageRating"
    at ValidationError.inspect (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/error/validation.js:47:26)
    at formatValue (node:internal/util/inspect:757:19)
    at inspect (node:internal/util/inspect:336:10)
    at formatWithOptionsInternal (node:internal/util/inspect:1999:40)
    at formatWithOptions (node:internal/util/inspect:1881:10)
    at console.value (node:internal/console/constructor:327:14)
    at console.log (node:internal/console/constructor:363:61)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/api/src/controllers/product.controller.js:79:17
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/helpers/promiseOrCallback.js:16:11
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4892:21
    at _done (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3118:16)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3133:18
    at callbackWrapper (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3087:20)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4868:18
    at processTicksAndRejections (node:internal/process/task_queues:76:11) {
  errors: {
    averageRating: CastError: Cast to Number failed for value "NaN" (type number) at path "averageRating"
        at SchemaNumber.cast (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schema/number.js:373:11)
        at SchemaNumber.SchemaType.applySetters (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schematype.js:1106:12)
        at SchemaNumber.SchemaType.getDefault (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schematype.js:1052:25)
        at $__applyDefaults (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/document.js:437:24)
        at model.Document (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/document.js:165:5)
        at model.Model (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:105:12)
        at new model (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4706:15)
        at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3094:22
        at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3130:7
        at Array.forEach (<anonymous>)
        at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3129:15
        at promiseOrCallback (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/helpers/promiseOrCallback.js:9:12)
        at Mongoose._promiseOrCallback (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/index.js:1149:10)
        at Function.create (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3064:23)
        at exports.createProduct (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/api/src/controllers/product.controller.js:42:11)
        at handleReturn (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/express-promise-router/lib/express-promise-router.js:24:23) {
      stringValue: '"NaN"',
      messageFormat: undefined,
      kind: 'Number',
      value: NaN,
      path: 'averageRating',
      reason: AssertionError [ERR_ASSERTION]: The expression evaluated to a falsy value:
      
        assert.ok(!isNaN(val))
      
          at castNumber (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/cast/number.js:28:10)
          at SchemaNumber.cast (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schema/number.js:371:12)
          at SchemaNumber.SchemaType.applySetters (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schematype.js:1106:12)
          at SchemaNumber.SchemaType.getDefault (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/schematype.js:1052:25)
          at $__applyDefaults (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/document.js:437:24)
          at model.Document (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/document.js:165:5)
          at model.Model (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:105:12)
          at new model (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4706:15)
          at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3094:22
          at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3130:7
          at Array.forEach (<anonymous>)
          at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3129:15
          at promiseOrCallback (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/helpers/promiseOrCallback.js:9:12)
          at Mongoose._promiseOrCallback (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/index.js:1149:10)
          at Function.create (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3064:23)
          at exports.createProduct (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/api/src/controllers/product.controller.js:42:11) {
        generatedMessage: true,
        code: 'ERR_ASSERTION',
        actual: false,
        expected: true,
        operator: '=='
      },
      valueType: 'number'
    }
  },
  _message: 'Product validation failed'
}
Product.create: undefined
/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/helpers/promiseOrCallback.js:19
            throw error;
            ^

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at new NodeError (node:internal/errors:329:5)
    at ServerResponse.setHeader (node:_http_outgoing:579:11)
    at ServerResponse.header (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/express/lib/response.js:771:10)
    at ServerResponse.send (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/express/lib/response.js:170:12)
    at ServerResponse.json (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/express/lib/response.js:267:15)
    at ServerResponse.send (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/express/lib/response.js:158:21)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/api/src/controllers/product.controller.js:85:23
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/helpers/promiseOrCallback.js:16:11
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4892:21
    at _done (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3118:16)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3133:18
    at callbackWrapper (/Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:3087:20)
    at /Volumes/ProjectsSSD/FixitServer/fixit_server_node/node_modules/mongoose/lib/model.js:4868:18
    at processTicksAndRejections (node:internal/process/task_queues:76:11) {
  code: 'ERR_HTTP_HEADERS_SENT'
}

【问题讨论】:

  • 似乎代码正常!我认为“问题”是您的默认值解析为 NaN 值,即 0/0 = NaN。并且 mongoose 期望类型是数字,而不是 NaN 值。
  • 0/0,为什么不保留默认0,
  • 哦,我明白了。所以也许默认值 1 将是修复

标签: node.js mongodb mongoose


【解决方案1】:

ratings 和 averageRating 都应该是虚拟字段:否则,/0 分区将在您的集合中有 product.ratings = 0 条目时使您的架构崩溃。所以我要做的是,首先,通过 getter 函数为虚拟字段定义评级:

productSchema.virtual("numberRatings").get(function() {
    return this.ratings.length;
});

假设this.ratings 是一个数组,其中存储了此特定产品的所有评级。这样,每次访问product.numerRatings 时,都会显示该数组中更新的条目数量。 考虑到这一点,您只需在 averageRating 中添加一点条件逻辑:

productSchema.virtual("averageRating").get(function() {
    return this.ratings.length > 0 ? this.totalRating / this.ratings.length : 0;
});

只要数组中没有评级,product.averageRating 就会产生 0,而不会尝试任何有问题的除法。数组开始包含评分的那一刻,将计算平均值。

【讨论】:

  • 我明白了,ratings 是收到的评分数,totalRatingratings 用于计算 averageRating 的值。因此,如果我将 3 个参数设为虚拟,我应该可以将它们保持为 Number 对吗?如果将它们更改为 ratings 数组,我将不得不修改 update 方法。
  • 我没有接受你的回答,只是因为它建议了一种基于数组的方法,而不是使用我所拥有的作为模式,但我赞成它,因为它为我指明了正确的方向。非常感谢
  • 乐于助人。主要问题是 de /0 除法。其余的真的取决于您的需求
  • 确实是问题所在。再次感谢,我仍然想知道为什么使用averageRating 作为真实参数而.pre 没有更新它。
【解决方案2】:

正如@amda 指出的那样,使用virtual 我可以在更新记录时计算averageRating。 他的回答提出了一种基于数组的方法,但我想使用我已经没有的模式来重构我已经拥有的更新方法。

我尝试使用.preaverageRating 属性设置为:

const productSchema = new mongoose.Schema({
    createdOnDate: { type: Number, required: true },
    name: { type: String, required: true },
    brand: { type: String, required: true },
    price: { type: Number, required: true },
    description: { type: String, required: true },
    category: { type: String, required: true },
    city: { type: String, required: true },
    region: { type: String, required: true },
    country: { type: String, required: true },
    vendor: { type: String, required: true },
    barcode: { type: String, required: true },
    imageUrl: { type: String, required: true },
    fullImages: { type: Array, required: true },
    thumbNails: { type: Array, required: true },
    // productImage: Uint8Array,
    minimumStock: { type: Number, required: true },
    availableQuantity: { type: Number, required: true },
    soldQuantity: { type: Number, required: true, default: 0 },

    totalRating: { type: Number, required: false, default: 0 },
    ratings: { type: Number, required: false, default: 0 },

    // using .pre => doesn't update averageRating
    averageRating: { type: Number, required: false, default: 0 },
},
    {
        timestamps: true
    });

productSchema.pre('save', function (next) {
    // this.averageRating = this.totalRating > 0 ? this.totalRating / this.ratings : 0;
    this.averageRating = this.totalRating / this.ratings

    next();
});

averageRating 永远不会更新..

使用virtual 确实有效,我只需添加返回的 toObject/toJson 以包含虚拟对象,现在可以按预期工作:

const productSchema = new mongoose.Schema({
    createdOnDate: { type: Number, required: true },
    name: { type: String, required: true },
    brand: { type: String, required: true },
    price: { type: Number, required: true },
    description: { type: String, required: true },
    category: { type: String, required: true },
    city: { type: String, required: true },
    region: { type: String, required: true },
    country: { type: String, required: true },
    vendor: { type: String, required: true },
    barcode: { type: String, required: true },
    imageUrl: { type: String, required: true },
    fullImages: { type: Array, required: true },
    thumbNails: { type: Array, required: true },
    // productImage: Uint8Array,
    minimumStock: { type: Number, required: true },
    availableQuantity: { type: Number, required: true },
    soldQuantity: { type: Number, required: true, default: 0 },

    totalRating: { type: Number, required: false, default: 0 },
    ratings: { type: Number, required: false, default: 0 },

    // using .pre doesn't update
    // averageRating: { type: Number, required: false, default: 0 },

},
    {
        timestamps: true,
        toObject: { virtuals: true },
        toJSON: { virtuals: true }
    });

productSchema.virtual("averageRating").get(function () {
    return this.totalRating > 0 ? this.totalRating / this.ratings : 0;
});

【讨论】:

    猜你喜欢
    • 2021-05-07
    • 2018-10-21
    • 2020-07-28
    • 1970-01-01
    • 1970-01-01
    • 2017-03-26
    • 2020-04-18
    • 2017-05-26
    • 1970-01-01
    相关资源
    最近更新 更多