【问题标题】:MongooseJS modify document during pre hookMongooseJS 在 pre hook 期间修改文档
【发布时间】:2013-08-15 19:12:00
【问题描述】:

我遇到了一些关于猫鼬的问题。我的目标是,在预保存期间,我将能够修改对象,如果需要,可以进行拆分标签等操作,或者在另一种情况下计算子文档持续时间的总和并在主文档中更新它。

我发现如果我加载一个模型,然后调用 doc.update 传递新数据,只有schema.pre('update', ...) 触发器,并且我的中间件中对this 的任何更改都不会更新。我还尝试在我的更新中间件中使用this.set('...', ....);,但无济于事。

如果我改为使用doc.save(...),那么schema.pre('save', ...) 内对this 的更改会按预期附加。除了将发布的变量扩展到我的模型的属性和保存之外,我没有看到任何利用 doc.update 来实现此目的的方法。

我的目标: - 通过doc.update(properties, ....) 更新现有文档 - 保存时使用中间件修改文档,做高级验证,更新相关文档 - 更新时使用中间件修改文档,做高级验证,更新相关文档 - 可互换使用 model.findByIdAndUpdate、model.save、model.findById->doc.update、model.findById->doc.save 并全部使用我的保存/更新中间件。

一些任意示例代码:

function loadLocation(c) {
    var self = this;
    c.Location.findById(c.params.id, function(err, location) {
        c.respondTo(function(format) {
            if (err | !location) {
                format.json(function() {
                    c.send(err ? {
                        code: 500,
                        error: err
                    } : {
                        code: 404,
                        error: 'Location Not Found!'
                    });
                });
                format.html(function() {
                    c.redirect(c.path_to.admin_locations);
                });
            } else {
                self.location = location;
                c.next();
            }
        });
    });
}

LocationController.prototype.update = function update(c) {
    var location = this.location;
    this.title = 'Edit Location Details';

    location.update(c.body.Location, function(err) {
        c.respondTo(function(format) {
            format.json(function() {
                c.send(err ? {
                    code: 500,
                    error: location && location.errors || err
                } : {
                    code: 200,
                    location: location.toObject()
                });
            });
            format.html(function() {
                if (err) {
                    c.flash('error', JSON.stringify(err));
                } else {
                    c.flash('info', 'Location updated');
                }
                c.redirect(c.path_to.admin_location(location.id));
            });
        });
    });
};

module.exports = function(compound) {
    var schema = mongoose.Schema({
        name: String,
        address: String,
        tags: [{ type: String, index: true }],
        geo: {
            type: {
                type: String,
            default:
                "Point"
            },
            coordinates: [Number] // Longitude, Latitude
        }
    });
    schema.index({
        geo: '2dsphere'
    });
    var Location = mongoose.model('Location', schema);
    Location.modelName = 'Location';
    compound.models.Location = Location;

    schema.pre('save', function(next) {
        if(typeof this.tags === 'string') {
            this.tags = this.tags.split(',');
        }
    });
};

==== * 修改样本 * ====

module.exports = function(compound) {
    var schema = mongoose.Schema({
        name: String,
        bio: String
    });

    schema.pre('save', function(next) {
        console.log('Saving...');
        this.bio = "Tristique sed magna tortor?"; 
        next();
    });

    schema.pre('update', function(next) {
        console.log('Updating...');
        this.bio = "Quis ac, aenean egestas?"; 
        next();
    });

    var Author = mongoose.model('Author', schema);
    Author.modelName = 'Author';
    compound.models.Location = Author;
};

【问题讨论】:

    标签: node.js mongodb express mongoose compoundjs


    【解决方案1】:

    Mongoose 不支持模型更新 API 的挂钩。但是,可以通过 Monkey-patch 完成更新挂钩。 Hooker NPM 包是一种干净利落的好方法。

    RESTeasy 项目是 Node REST API 的样板,其中包含演示如何执行此操作的代码:

    var hooker = require('hooker');
    
    var BaseSchema = new mongoose.Schema({
      sampleString: {
        type: String
      }
    });
    
    var BaseModel = mongoose.model('Base', BaseSchema);
    
    // Utilize hooks for update operations. We do it in this way because MongooseJS
    // does not natively support update hooks at the Schema level. This is a way
    // to support it.
    hooker.hook (BaseModel, 'update', {
      pre: function () {
        // Insert any logic you want before updating to occur here
        console.log('BaseModel pre update');
      },
      post: function () {
        // Insert any logic you want after updating to occur here
        console.log('BaseModel post update');
      }
    });
    
    // Export the Mongoose model
    module.exports = BaseModel;
    

    【讨论】:

    • 现在 update 钩子在 Mongoose 4.0 中本机工作,但默认情况下它们是关闭的。这篇博文解释:mongodb.com/blog/post/…
    【解决方案2】:

    pre 钩子适用于 doc.save()doc.update()。在这两种情况下,this 指的是文档本身。

    请注意,在编译模型之前,需要将钩子添加到您的架构中。

    schema.pre('save', function(next) {
        if(typeof this.tags === 'string') {
            this.tags = this.tags.split(',');
        }
    });
    var Location = mongoose.model('Location', schema);
    

    【讨论】:

    • 嗯,我创建了糟糕的示例代码并没有帮助。我用一个更简单的案例尝试了您的建议,但仍然得到相同的结果:使用Author.findById 查找文档后,当我调用author.update(c.req.body, ...) 时,schema.pre('save', ...) 不会触发,但是schema.pre('update', ...) 会触发并且对@ 的更改987654331@ 未被应用。 schema.pre('save', ...) 不应该触发吗?它看起来确实像 author.save 触发器 schema.pre('save', ...) 并且正在应用所做的更改。这里的解决方案是完全放弃author.update 吗?
    • 正确。 doc.update() 不会触发任何其他方法的钩子,但被调用的方法除外。
    • 好的,但这仍然回答了原来的问题。 schema.pre('update', ...) 期间的更改仍未附加。这是一个错误,是有意的,还是我应该只使用 doc.save 并使用某种方法来迭代我的 req.body 和合并属性?
    • 不是错误。 document.update(doc, cb) 为传递的文档发送更新,而不是当前更改的值。这与document.save() 不同。
    • 如果你查看文档:mongoosejs.com/docs/middleware.html 它没有提到 schema.pre('update' 事件是有效的。它只列出了初始化、保存、删除和验证。
    猜你喜欢
    • 1970-01-01
    • 2015-03-27
    • 2018-11-29
    • 2012-12-07
    • 2019-09-10
    • 2020-06-30
    • 2012-12-01
    • 2013-02-19
    • 1970-01-01
    相关资源
    最近更新 更多