Nodejs Bug? mongoose不能更新嵌入的数组中的值?
Nodejs Bug? mongoose不能更新嵌入的数组中的值?
大家好,不知为什么mongoose不能更新嵌入的数组中的数据。 ’’’ var MySchema=new Schema({ id:ObjectId, msgbody:String, replymsg:[ReplyPerson] });
var ReplyPerson=new Schema({ rid:ObjectId, parentid:ObjectId, rmsg:String, rimgurls:[String], });
传调参数示例: params:{schemaid:1,childid:2};
就是MySchema中的replymsg数组中找rid为2的记录,并把其中的rmsg改为’cccccccc’
exports.myupdate = function (params, data, callback) { MySchema.findOne({_id:params.schemaid}, function (err, mySchema) { if (err) { console.log(‘Err:’ + err); return callback(err); } if (!mySchema) { return callback(new Error(‘无可更新内容!’)); } //如果直接这样赋值是可以改变的。 mySchema.replymsg=[‘aaaa’,‘bbbb’]; for(var key in mySchema.replymsg){ // console.log(‘key:’+key); var obj=mySchema.replymsg[key]; // console.log(‘obj:’+obj); if(obj.parentid==params.schemaid && obj.rid=params.childid){ //通过调试,这是是会执行到的,但最终mongodb 中值还是原来的值。 mySchema.replymsg[key].rmsg=‘cccccccccccccc’; break; } } mySchema.save(callback); }); }; ’’' 但当我执行这段代码后,mongodb中记录一直没改变。 可如果我不是通过改变数组中的记录内容,直接把整个数组赋值mySchema.replymsg=[‘aaaa’,‘bbbb’];是会改变值的。 难道mongoose不能自动检测到数组中的值已经变化过,如果是这样,那么有没有什么值可以手动设置让mongoose知道这些值变化了呢? 非常感谢! 调了一个上午,始终不行,求救!!!
Node.js Bug? Mongoose 无法更新嵌入的数组中的值?
大家好,我在使用 Mongoose 更新嵌入的数组中的值时遇到了问题。我试图在一个嵌套的数组中找到特定的记录并修改其字段,但是 MongoDB 中的数据并没有发生变化。
数据模型定义
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test', { useNewUrlParser: true, useUnifiedTopology: true });
var ObjectId = mongoose.Schema.Types.ObjectId;
var ReplyPerson = new mongoose.Schema({
rid: ObjectId,
parentid: ObjectId,
rmsg: String,
rimgurls: [String],
});
var MySchema = new mongoose.Schema({
id: ObjectId,
msgbody: String,
replymsg: [ReplyPerson]
});
var MyModel = mongoose.model('MyModel', MySchema);
调用参数示例
var params = { schemaid: '5f9c3e4b8d7a3f2b3c4d5e6f', childid: '5f9c3e4b8d7a3f2b3c4d5e70' };
目标
找到 MySchema
中 replymsg
数组里 rid
为 2
的记录,并将其中的 rmsg
修改为 'cccccccc'
。
更新函数
exports.myupdate = function (params, data, callback) {
MyModel.findOne({ _id: params.schemaid }, function (err, mySchema) {
if (err) {
console.log('Err:', err);
return callback(err);
}
if (!mySchema) {
return callback(new Error('无可更新内容!'));
}
// 遍历数组,找到符合条件的记录
for (var i = 0; i < mySchema.replymsg.length; i++) {
var obj = mySchema.replymsg[i];
if (obj.rid.equals(params.childid)) {
// 修改 rmsg 字段
mySchema.replymsg[i].rmsg = 'cccccccccccccc';
break;
}
}
// 保存更改
mySchema.save(function (err) {
if (err) {
return callback(err);
}
callback(null, '更新成功');
});
});
};
解释
- 遍历数组:使用
for
循环遍历replymsg
数组,查找符合条件的记录。 - 条件判断:使用
obj.rid.equals(params.childid)
来确保正确比较ObjectId
。 - 修改值:找到匹配项后,修改
rmsg
字段。 - 保存更改:调用
mySchema.save()
方法保存更改。
注意事项
- 确保
ObjectId
比较时使用.equals()
方法,而不是简单的==
或===
。 - 在修改嵌套对象时,Mongoose 可能不会自动检测到这些更改。因此,确保在修改对象后调用
save()
方法来保存更改。
希望这可以帮助你解决问题。如果有任何疑问或需要进一步的帮助,请随时提问。
首先MySchema.findById(params.schemaid,function(err,reply){
如果reply有值, 那么reply.replymsg就是子文档的值 通过一个子文档查询的方法:var hasId = reply.replymsg.id(params.childid); 如果这个hasId为真。那就是在子文档中查到了这个childid 如果要update子文档。 目前只有一种办法 就是: if(hasId){ hasId.remove(); //或者reply.replymsg.pull({rid: params.childid}); } 然后再执行reply.replymsg.addToSet(新的对象是由原来的rid和其它不改变的值以及,新的update的值组成);
这个新的对象,也可以在前面把它查出来,把查到的值给这个新的对象,同时把要改变的比如:子文档中的rmsg设定为 你要update的值
});
感谢sogego的回复,我又仔细看了一遍官网和FAQ,现终于有点明白,做了个示例,供讨论。 <a href=“http://mongoosejs.com/docs/faq.html” target="_blank">官方FAQ文档</a>
Model文件 DParent.js <code> var mongoose=require(‘mongoose’); var Schema=mongoose.Schema; var ObjectId = Schema.ObjectId; var DChild=require(’./DChild’);
var DParentSchema=new Schema({ msgbody:String, childs:[DChild.DChildSchema] });
mongoose.model(‘DParent’,DParentSchema); </code>
DChild.js <code> var mongoose=require(‘mongoose’); var Schema=mongoose.Schema; var ObjectId = Schema.ObjectId;
var DChildSchema=new Schema({ parentid:ObjectId, childmsg:String, substrings:[String] });
exports.DChildSchema=DChildSchema; mongoose.model(‘DChild’,DChildSchema); </code>
DUpdate.js <code> var models = require(’…/models’); var DParent = models.DParent; var DChild = models.DChild; exports.updateaction = function (req, res,next) { // var dp=new DParent(); // dp.msgbody=‘parent msg1’; // var dchild0=new DChild(); // dchild0.parentid=dp._id; // dchild0.childmsg=‘children msg’; // dchild0.substrings.push(‘str1’); // dchild0.substrings.push(‘str2’); // dchild0.substrings.push(‘str3’); // dp.childs.push(dchild0); // dp.save(function(err,result){ // res.send(’’); // return next(); // }); DParent.findOne({_id:‘52e526f36dbf1d94255e39a8’}, function (err, dparent) { if (err) { console.log(‘Err:’ + err); } if (!dparent) { console.log(‘no document’); } dchild0=dparent.childs[0]; dchild0.childmsg=‘from parent’; //对于不是数组类型的字段直接通过这种方法来修改 // dchild0.substrings.splice(0,1); //在子文档child0的substrings这个数组中删除第一个记录 //dchild0.substrings.push(‘push22’); //在子文档child0的substrings这个数组中增加一个记录 //这种方式不行 dchild0.substrings[0]=‘up0000’; //这种方法不能够修改数组中的值。。。。。。 dchild0.substrings.set(0,‘updated by parent1’); //如要修改数组中的值,需用这个set方法,"0"表示要修改substrings数据的第1条记录 dparent.save(function(err,result){ if(err){ console.log(‘save error!’); } console.log(result); res.send(’’); return next(); }); }); }; </code>
调用方法: var dts=require(’./action/DUpdate’); server.get(’/api_v1/ts’,dts.updateaction);
数据库结构:
{ "__v" : 10, "_id" : ObjectId(“52e526f36dbf1d94255e39a8”), “childs” : [{ "_id" : ObjectId(“52e526f36dbf1d94255e39a9”), “childmsg” : “from parent”, “parentid” : ObjectId(“52e526f36dbf1d94255e39a8”), “substrings” : [“updated by parent1”, “sub1”, “sub2”, “sub3”] }], “msgbody” : “parent msg1” }
还有忘记说一点,定义父类的Schema时,数组中的子类也要是子类的Schema,不能是子类的Model var DParentSchema=new Schema({ msgbody:String, childs:[DChild.DChildSchema] //不能定义成 childs:[DChild] ,这里DChild是Model,虽然大多数情况没问题,但遇到要更改数组中的值就不行了。 });
此坑我也遇到过,在执行这句mySchema.save(callback);
之前加一句mySchema.markModified('replymsg')
这个要马克~
在Mongoose中更新嵌入式文档或数组中的特定字段时,需要确保Mongoose能够识别到更改。默认情况下,直接修改嵌套对象可能不会触发Mongoose的变更检测机制。你可以使用markModified
方法来显式地告诉Mongoose某个路径已经被修改。
以下是修改后的代码示例:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ReplyPerson = new Schema({
rid: { type: Schema.Types.ObjectId },
parentid: { type: Schema.Types.ObjectId },
rmsg: String,
rimgurls: [{ type: String }]
});
const MySchema = new Schema({
id: { type: Schema.Types.ObjectId },
msgbody: String,
replymsg: [ReplyPerson]
});
const MyModel = mongoose.model('MyModel', MySchema);
exports.myupdate = function (params, data, callback) {
MyModel.findOne({ _id: params.schemaid }, function (err, mySchema) {
if (err) {
return callback(err);
}
if (!mySchema) {
return callback(new Error('无可更新内容!'));
}
for (let i = 0; i < mySchema.replymsg.length; i++) {
let obj = mySchema.replymsg[i];
if (obj.parentid.equals(params.schemaid) && obj.rid.equals(params.childid)) {
mySchema.replymsg[i].rmsg = 'cccccccccccccc';
// 使用 markModified 方法告诉 Mongoose 路径已修改
mySchema.markModified(`replymsg.${i}`);
break;
}
}
mySchema.save(callback);
});
};
解释
- 模型定义:定义了两个Schema,一个是主Schema
MySchema
,另一个是嵌入的子SchemaReplyPerson
。 - 查询与更新逻辑:在
findOne
回调中遍历replymsg
数组,找到符合条件的元素并更新其rmsg
属性。 - 使用
markModified
:为了确保Mongoose能检测到数组中的更改,使用markModified
方法显式地标记路径已更改。
注意点
- 确保你使用的是Mongoose的正确版本。
- 在比较
ObjectId
时使用equals
方法而不是简单地使用==
或===
。 - 如果你在使用异步函数或Promise,请考虑用更现代的方式来处理错误和回调,例如使用
.then
和.catch
。
希望这可以帮助你解决问题。