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知道这些值变化了呢? 非常感谢! 调了一个上午,始终不行,求救!!!


8 回复

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' };

目标

找到 MySchemareplymsg 数组里 rid2 的记录,并将其中的 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, '更新成功');
        });
    });
};

解释

  1. 遍历数组:使用 for 循环遍历 replymsg 数组,查找符合条件的记录。
  2. 条件判断:使用 obj.rid.equals(params.childid) 来确保正确比较 ObjectId
  3. 修改值:找到匹配项后,修改 rmsg 字段。
  4. 保存更改:调用 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')

http://cnodejs.org/topic/516ab9c96d38277306376cad

这个要马克~

在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);
    });
};

解释

  1. 模型定义:定义了两个Schema,一个是主Schema MySchema,另一个是嵌入的子Schema ReplyPerson
  2. 查询与更新逻辑:在findOne回调中遍历replymsg数组,找到符合条件的元素并更新其rmsg属性。
  3. 使用markModified:为了确保Mongoose能检测到数组中的更改,使用markModified方法显式地标记路径已更改。

注意点

  • 确保你使用的是Mongoose的正确版本。
  • 在比较ObjectId时使用equals方法而不是简单地使用=====
  • 如果你在使用异步函数或Promise,请考虑用更现代的方式来处理错误和回调,例如使用.then.catch

希望这可以帮助你解决问题。

回到顶部