MongoDB更新嵌套数组遇Cast to ObjectId失败问题求助
问题:MongoDB位置操作符$更新数组字段触发CastError
我知道Stack Overflow上有很多类似问题,但严格按照MongoDB官方文档操作还是解决不了。我想用位置操作符$更新Book文档里userbookrates数组中指定用户的rate字段,结果触发了CastError:无法将数值3转换为ObjectId。下面是我的模型代码、示例数据、执行代码和完整报错信息,求帮忙解决。
参考MongoDB官方示例
{ _id: 4, grades: [ { grade: 80, mean: 75, std: 8 }, { grade: 85, mean: 90, std: 5 }, { grade: 85, mean: 85, std: 8 } ] }
db.students.updateOne( { _id: 4, "grades.grade": 85 }, { $set: { "grades.$.std" : 6 } } )
我的执行代码
const { id, rateValue } = req.body Book.updateOne({ _id: id, "userbookrates.user":ObjectId("62d1fbfa5a0a6281de43b528") }, {$set:{"userbookrates.$.rate": rateValue}}) .then(r => { console.log(r, 'result log----') }) .catch(e=> console.log(e))
Book模型定义
const bookSchema = new Schema ( { title: { type:String, required: [true, 'Mongoose - name is required'] }, author: { type:String, required: [true, 'Mongoose - author is required'] }, plot: { type:String, required: [true, 'Mongoose - plot is required'] }, genre: { type:String, required: [true, 'Mongoose - genre is required'] }, image: { type: String, default: 'catty' }, owner: { type: Schema.Types.ObjectId, ref: 'User' }, userbookrates: [{ type: Schema.Types.ObjectId, ref: 'Bookrate' }], usersaved: [{ type: Schema.Types.ObjectId, ref: 'User' }] }, { timestamps: true } ); module.exports = model('Book', bookSchema);
Bookrate模型定义
const bookrateSchema = new Schema ({ user: { type: Schema.Types.ObjectId, ref: 'User' }, rate: { type: Number, required: [true, 'Mongoose - Rate is a MUST'], }, }, { timestamps: true } );
示例Book数据
{ _id: new ObjectId("62d1fbd46dca32eab6e2f280"), title: 'The Leisure Seeker', author: 'Paolo Virzì', plot: 'A runaway couple goes on an unforgettable journey in the faithful old RV they call The Leisure Seeker, traveling from Boston to The Ernest Hemingway Home in Key West. They recapture their passion for life and their love for each other on a road trip that provides revelation and surprise right up to the very end.', genre: 'drama', image: 'https://image.jpg', userbookrates: [ { _id: new ObjectId("62d218c7c93acc30d3075f79"), user: new ObjectId("62d1fbfa5a0a6281de43b528"), rate: 5, createdAt: 2022-07-16T01:47:51.874Z, updatedAt: 2022-07-16T01:47:51.874Z, __v: 0 } ], usersaved: [], createdAt: 2022-07-15T23:44:20.100Z, updatedAt: 2022-07-16T02:21:11.581Z, __v: 0 }
报错信息
**CastError: Cast to ObjectId failed for value "3" (type number) at path "userbookrates.$"** at model.Query.exec (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/query.js:4650:21) at model.Query.Query.then (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/query.js:4749:15) at /home/titoih/Code/api-opinoos/routes/user/user.routes.js:29:10 at Layer.handle [as handle_request] (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/layer.js:95:5) at next (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/route.js:137:13) at module.exports.checkAuth (/home/titoih/Code/api-opinoos/mid/mid.secure.js:4:9) at Layer.handle [as handle_request] (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/layer.js:95:5) at next (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/route.js:137:13) at Route.dispatch (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/route.js:112:3) at Layer.handle [as handle_request] (/home/titoih/Code/api-opinoos/node_modules/express/lib/router/layer.js:95:5) { messageFormat: undefined, stringValue: "3", kind: 'ObjectId', value: 3, path: 'userbookrates.$', **reason: BSONTypeError: Argument passed in must be a string of 12 bytes or a string of 24 hex characters** at new BSONTypeError (/home/titoih/Code/api-opinoos/node_modules/bson/lib/error.js:41:28) at new ObjectId (/home/titoih/Code/api-opinoos/node_modules/bson/lib/objectid.js:65:23) at castObjectId (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/cast/objectid.js:24:12) at ObjectId.cast (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/schema/objectid.js:247:12) at ObjectId.SchemaType.applySetters (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/schematype.js:1179:12) at ObjectId.SchemaType._castForQuery (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/schematype.js:1613:15) at ObjectId.SchemaType.castForQuery (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/schematype.js:1603:15) at ObjectId.SchemaType.castForQueryWrapper (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/schematype.js:1580:20) at castUpdateVal (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/helpers/query/castUpdate.js:527:19) at walkUpdatePath (/home/titoih/Code/api-opinoos/node_modules/mongoose/lib/helpers/query/castUpdate.js:344:24), valueType: 'number' }
问题原因及解决方案
核心原因
Book模型里把userbookrates定义成了ObjectId数组(引用Bookrate),但实际数据库里存储的是嵌套文档数组,两者不匹配。Mongoose按照模型定义会尝试把更新操作中的userbookrates.$(位置操作符匹配的数组项)转换成ObjectId,但实际数组项是完整的Bookrate文档,更新时传递的rate值(比如3)不符合ObjectId格式,因此触发CastError。
解决步骤
修正Book模型的
userbookrates字段定义
把原来的ObjectId引用数组,改成嵌套的Bookrate文档数组:// 替换Book模型中的userbookrates字段 userbookrates: [bookrateSchema], // 直接使用Bookrate的Schema作为嵌套文档修正后,Mongoose会正确识别数组项为嵌套文档,更新路径
userbookrates.$.rate能正常解析,不会再触发类型转换错误。如果坚持用引用关系
若要保持Bookrate作为独立集合的引用关系,需单独更新Bookrate文档,而非在Book文档内操作:// 先从Book文档获取关联的Bookrate ID列表,再更新对应文档 Book.findById(id) .then(book => { return Bookrate.updateOne( { user: ObjectId("62d1fbfa5a0a6281de43b528"), _id: { $in: book.userbookrates } }, { $set: { rate: rateValue } } ) }) .then(r => console.log(r)) .catch(e => console.log(e))
内容的提问来源于stack exchange,提问作者titoih
相关产品推荐
相关产品推荐

