【问题标题】:Sharing data between Mongoose middleware methods pre save and post save在 Mongoose 中间件方法 pre save 和 post save 之间共享数据
【发布时间】:2017-01-31 23:02:51
【问题描述】:

在底部查看更新的示例代码

我在当前的 NodeJS 项目中使用 Mongoose(顺便说一句,这很棒!),并且我有一个 MDB 集合,它将将文档的更改存储在不同的集合中(基本上是一个 changelog存储修改过的内容)

我试图实现的方法是创建一个函数来存储文档的 JSON 版本,这是通过 pre('save') 钩子完成的。然后创建另一个钩子,通过post('save')执行,比较pre('save')中存储的数据,并将其与文档新数据进行比较。

这是我目前所拥有的:

var origDocument 
var testVar = 'Goodbye World'

module.exports = ( schema, options ) => {
    schema.pre( 'save', function( next ) {
        // Store the original value of the documents attrCache.Description value
        origDocument = this.toJSON().attrCache.Description

        // Change the testVar value to see if the change is reflected in post(save)
        testVar = 'Hello World'
        next()
    } )

    schema.post( 'save', function(  ) {
        // Attempt to compare the documents previous value of attrCache.Description, with the new value
        console.log("BEFORE:", origDocument)
        console.log("AFTER:", this.toJSON().attrCache.Description)

        // Both of the above values are the same! >.<

        console.log('post(save):',testVar) // result: post(save):Hello World
        // But the above works just fine..
    } )
}

我原本认为这行不通。为了测试这两个钩子是否在同一范围内执行,我在页面顶部创建了一个名为 testVar 的测试变量,带有一些任意值,然后在 post(save) 钩子中检索了 testVar 和值该变量的修改在保存后挂钩中可见

所以从那里,我只是将this.toJSON() 的值存储在一个变量中,然后在 post(save) 挂钩中,我试图检索此文档的缓存版本,并将其与this.toJSON() 进行比较。但是,pre(save) 中的文档看起来并不包含修改前的数据,它在某种程度上具有文档的价值它被更新之后。

那么,为什么我可以从 pre(save) 挂钩中更新 testVar 的值,而这种更改反映在 post(save) 挂钩函数中,但我不能对文档本身做同样的事情?

我在这里尝试做的事情是否可能?如果是这样,我做错了什么?如果没有 - 我该如何做到这一点?

谢谢

更新

根据@Avraam 的建议,我尝试通过JSON.stringify() 运行数据,然后通过pre(save) 挂钩将其保存在内存中,然后在post(save) 中执行相同操作,如下所示:

var origDocument 

module.exports = ( schema, options ) => {
    schema.pre( 'save', function( next ) {

        origDocument = JSON.stringify( this.toJSON().attributes[1].value )

        // Should store and output the CURRENT value as it was before the 
        // document update... but it displays the NEW value somehow
        console.log( '[MIDDLEWARE] ORIGINAL value:', origDocument )

        next()
    } )

    schema.post( 'save', function(  ) {
        var newDocument = JSON.stringify(this.toJSON().attributes[1].value)

        console.log( '[MIDDLEWARE] UPDATED value:', newDocument )
    } )
}

下面是更新 mongoose 文档的脚本:

Asset.getAsset( '56d0819b655baf4a4a7f9cad' )
    .then( assetDoc => {
        // Display original value of attribute
        console.log('[QUERY] ORIGINAL value:', assetDoc.attributes[1].value)

        var updateNum = parseInt( assetDoc.__v )+1
        assetDoc.attr('Description').set('Revision: ' + updateNum )

        return assetDoc.save()
    } )
    .then(data => {
        // Display the new value of the attribute
        console.log('[QUERY] UPDATED value:', data.attributes[1].value)
        //console.log('DONE')
    })
    .catch( err => console.error( 'ERROR:',err ) )

这是我运行新脚本时的控制台输出:

[QUERY] ORIGINAL value: Revision: 67
[MIDDLEWARE] ORIGINAL value: "Revision: 68"
[MIDDLEWARE] UPDATED value: "Revision: 68"
[QUERY] UPDATED value: Revision: 68

如您所见,[QUERY] ORIGINAL 值和 [QUERY] UPDATED 值表明有更新。但是 [MIDDLEWARE] 原始/更新的值仍然相同......所以我仍然坚持为什么

更新

我想也许我可以提供一个更简化但更详细的示例。

这是应该比较 pre(save)

的中间件模块

post(save): '使用严格'

import _ from 'moar-lodash'
import * as appRoot from 'app-root-path'
import Mongoose from 'mongoose'
import diff from 'deep-diff'

var originalDesc 


module.exports = ( schema, options ) => {
    schema.pre( 'save', function( next ) {
        originalDesc =  JSON.parse( JSON.stringify( this.toJSON() ) ).attributes[1].value

        console.log( '[MIDDLEWARE ORIGINAL Desc]\n\t', originalDesc )
        next()
    } )

    schema.post( 'save', function(  ) {
        var newDesc =  JSON.parse( JSON.stringify( this.toJSON() ) ).attributes[1].value

        console.log( '[MIDDLEWARE NEW Desc]\n\t', newDesc)
    } )
}

然后是使用Asset 模型并更新Description 属性的代码...

'use strict'

import _ from 'moar-lodash'
import Promise from 'bluebird'
import Mongoose from 'mongoose'
import Async from 'async'
import Util from 'util'
import * as appRoot from 'app-root-path'

Mongoose.Promise = Promise

Mongoose.connect( appRoot.require('./dist/lib/config').database.connection )

const accountLib = appRoot.require('./dist/lib/account')

const models = require( '../models' )( Mongoose )

models.Asset.getAsset( '56d0819b655baf4a4a7f9cad' )
    .then( assetDoc => {
        var jqDoc = JSON.parse(JSON.stringify(assetDoc.toJSON()))

        // Show the CURRENT description
        console.log('[IN QUERY - Before Modify]\n\t', jqDoc.attributes[1].value)

        assetDoc.attr('Description').set( 'Date-'+Date.now() )

        return assetDoc.save()

    } )
    .then(data => {
        // Just show the Description AFTER it was saved
        console.log('[AFTER QUERY - AFTER Modify]\n\t', data.attributes[1].value)
    })
    .catch( err => console.error( 'ERROR:',err ) )
    .finally( () => {
        Mongoose.connection.close()
        console.log('# Connection Closed')
    })


[IN QUERY - Before Modify]
     Date-1474915946697
[MIDDLEWARE ORIGINAL Desc]
     Date-1474916372134
[MIDDLEWARE NEW Desc]
     Date-1474916372134
[AFTER QUERY - AFTER Modify]
     Date-1474916372134
# Connection Closed

【问题讨论】:

  • 我真的无法在第一个控制台输出(最后一个 sn-p 的第一行)中得到它的原因,但是在 post 中间件挂钩的功能下,它没有
  • console.log( '[MIDDLEWARE ORIGINAL Desc]\n\t', this. attributes )console.log( '[MIDDLEWARE NEW Desc]\n\t', this.attributes) 显示什么?
  • @Justin 您是否真的想要两个版本的数据,或者您想知道某个属性是否已更改?

标签: javascript node.js mongodb mongoose mongoose-schema


【解决方案1】:

origDocument 引用了this.toJSON(),并且在您调用console.log 的那一刻,参考点已经改变的实际对象的值。使用 JSON.stringify 之类的东西来比较这些值。

origDocument = JSON.stringify( this.toJSON() )

【讨论】:

  • 我试过了,它似乎没有用。我会用详细信息更新主帖子
  • 如果你想发布一个完整的例子,那么我很乐意接受它并给你赏金。您确实在pre(save) 中看到了,我将其保存为origDocument = JSON.stringify( this.toJSON().attributes[1].value ),对吗?这正是你在说的
【解决方案2】:

我认为您误解了前/后挂钩在猫鼬中的工作方式。当您抓取文档(正如您所做的那样)并重新保存它时。它不会包含文档中最初存在的任何变量。它将包含文档中当前的任何内容。

所以,你正在这样做:

  1. 抓取文件 (67)
  2. 修改文档
  3. Document.Save() 已调用
  4. 预保存打印出当前文档 (68)
  5. 保存后打印当前文档 (68)

我认为您想要做的是在您的架构上实现一个实例方法,您可以使用它来定义您想要的逻辑。您可以在调用 .save() 之前调用它(或者在执行了自己的逻辑之后使用它来调用 .save())

示例:

schema.methods.createRevisionHistory= function(object, callback) {
    // Do comparison logic between this. and object.
    // modify document (this) accordingly
    // this.save(function(err, doc) {
    //    if(err)
    //       return callback(err)
    //    callback(doc);
    // })
};

希望对你有帮助

阅读更多:http://mongoosejs.com/docs/guide.html#methods

【讨论】:

  • 所以你说我应该在模式中创建一个 instance 方法,它只输出文档数据,然后在 pre 中获取它,然后更新实际的文档,然后在post中使用相同的自定义方法,它应该不同吗?
  • 我为此创建了一个赏金。如果您可以发布它的工作示例,那么您就明白了:) 我只想能够查看文档的内容(在 pre(save) 钩子中,并将其与它的内容进行比较(在 post(save) 钩子中) .
  • 嘿,我无法在接下来的 3 周内离开我的工作计算机:P。完成您所要求的唯一方法是,如果您将文档保存在您的 pre 中,将其保存到另一个变量,然后修改数据,然后在您的 post 保存比较中。
【解决方案3】:

origDocument 具有 this.toJSON 的引用,因此当 this.toJSON 在 post('save') 中更改时,origDocument 也会更改。试试下面的代码:

var origDocument 
var testVar = 'Goodbye World'

module.exports = ( schema, options ) => {
    schema.pre( 'save', function( next ) {
        // Store the original value of the documents attrCache.Description value
        origDocument = JSON.parse(JSON.strinigify(this.toJSON().attrCache.Description))

        // Change the testVar value to see if the change is reflected in post(save)
        testVar = 'Hello World'
        next()
    } )

    schema.post( 'save', function(  ) {
        // Attempt to compare the documents previous value of attrCache.Description, with the new value
        console.log("BEFORE:", origDocument)
        console.log("AFTER:", this.toJSON().attrCache.Description)

        // Both of the above values are the same! >.<

        console.log('post(save):',testVar) // result: post(save):Hello World
        // But the above works just fine..
    } )
}

使用 JSON.parse(JSON.stringify()) 我已经清除了引用。

希望对你有帮助!!!

【讨论】:

    【解决方案4】:

    好的,Avraam Mavridis 正确回答了您问题的第一部分 所以我只会关注你在问题中的最后更新。

    pre.save 实际上保存当前存在于数据库中的实际文档,而是将要保存的文档,并包含对文档所做的更改,即更新的文档。

    post.save 保存存储在数据库中的真实文档,因此仍然是更新版本。所以你看不到thisprepost save 中所做的更改。

    现在,如果您想查看数据库中存在的真实值,您需要在更改和保存之前从数据库中获取它,即在 pre.save 中。


    您可以做到这一点的一种方法是简单地从数据库中查询文档
    var originalDesc 
    
    
    module.exports = ( schema, options ) => {
        schema.pre( 'save', function( next ) {
            Asset.getAsset( '56d0819b655baf4a4a7f9cad' )
            .then( assetDoc => {
                 originalDesc = assetDoc.attributes[1].value;
                 console.log( '[MIDDLEWARE ORIGINAL Desc]\n\t', originalDesc )
                 next()
             } );
        } );
    
        schema.post( 'save', function(  ) {
            var newDesc = this.toJSON().attributes[1].value
            console.log( '[MIDDLEWARE NEW Desc]\n\t', newDesc)
        } )
    }
    


    使用自定义设置器还有一种替代方法,并且已经有一个很好的answer here,但这需要为每个属性设置一个自定义设置器
    schema.path('name').set(function (newVal) {
       this.originalDesc = this.Description;
    });
    schema.pre('save', function (next) {
      console.log( '[MIDDLEWARE ORIGINAL Desc]\n\t', this.originalDesc )
      next();
    })
    schema.post( 'save', function(  ) {
      var newDesc = this.toJSON().attributes[1].value
      console.log( '[MIDDLEWARE NEW Desc]\n\t', newDesc)
    } )
    

    希望这会有所帮助。

    【讨论】:

      【解决方案5】:

      您可以使用另一个中间件,并将当前值临时设置为未定义的属性(这样它就不会在save 调用时保存到数据库中)。

      例如

      schema.post('init', function(doc) {
        // when document is loaded we store copy of it to separate variable
        // which will be later used for tracking changes
        this._original = doc.toJSON({depopulate: true});
      });
      

      然后在 post save hook 中进行比较:

      schema.post('save', function(doc) {
        // do the diffing of revisions here
      });
      

      【讨论】:

        【解决方案6】:

        在我们的 API 中,我通过使用 document.$locals 作为原始值的存储位置解决了这个问题。 document.$locals 不会传递到数据库,而是在中间件调用之间保持不变。

        post('find')post('findOne') 挂钩中:

        doc.$locals.originalValues = doc.toObject();
        

        pre('save')post('save') 挂钩中:

        let changes = doc.getChanges()
        ,   originalValues = doc.$locals.originalValues;
        
        if (changes.$set) {
            for (let key in changes.$set) {
                _.set(result, key, originalValues[key]);
                result[key] = originalValues[key]; // May be undefined
            }
        }
        if (changes.$unset) {
            for (let key in changes.$unset) {
                _.set(result, key, originalValues[key]);
                result[key] = originalValues[key]; // Should be defined
            }
        }
        

        这些是代码的相关部分。还有很多错误检查和边缘情况检测,但基本上我们在检索原始文档时存储它,因此可以将这些值与保存的数据进行比较。

        【讨论】:

        • 有发布 API/库/插件的计划吗?
        猜你喜欢
        • 1970-01-01
        • 2021-01-08
        • 1970-01-01
        • 2015-07-20
        • 2018-04-22
        • 1970-01-01
        • 2018-11-17
        • 1970-01-01
        • 2019-01-31
        相关资源
        最近更新 更多