在保存和保存后保存Mongoose中间件方法之间的数据

时间:2016-09-23 07:12:12

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

查看更新示例代码@BOTTOM

我在当前的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)钩子中检索{{1在保存后的挂钩中看到变量的值修改。

所以,从那里,我只是将testVar的值存储在变量中,然后在post(save)钩子中,我试图检索此文档的缓存版本,并将其与{{1 }}。但是,看起来this.toJSON()中的文档并不包含预先修改过的数据,它在某种程度上具有文档之后更新后的值。

那么为什么我可以在this.toJSON()钩子中更新pre(save)的值,并且该更改反映在testVar钩子函数中,但是我不能对它做同样的事情。记录本身?

我试图在这里做什么甚至可能?如果是这样,我做错了什么?如果不是 - 我怎么能做到这一点?

谢谢

更新

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

pre(save)

这是更新mongoose文档的脚本:

post(save)

当我运行New脚本时,继承控制台输出:

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

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

更新

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

继承了应该比较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 :     &#39;使用严格的&#39;

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

6 个答案:

答案 0 :(得分:3)

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

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

post.save包含存储在数据库中的真实文档,因此仍然是更新版本。因此,您只能在thispre post中查看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)
    } )
}

<小时/> 除了使用自定义setter之外,还有一种替代方法,并且已经有了很好的answer here,但这需要为每个属性设置自定义setter

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

希望这有帮助。

答案 1 :(得分:0)

origDocument引用了this.toJSON()以及您调用console.log参考点已经更改的实际对象的值的那一刻。使用类似JSON.stringify的内容来比较值。

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

答案 2 :(得分:0)

我认为你误解了前/后钩子在mongoose中是如何工作的。当您抓取文档(正如您所做)并重新保存它时。它不会具有文档中最初的任何变量。它将包含文档中当前的任何内容。

所以,你这样做:

  1. 抓文件(67)
  2. 修改文件&lt; (你在这里做了+1)(现在68)
  3. Document.Save()调用
  4. 预先保存打印出当前文档(68)
  5. 保存后打印出当前文档(68)
  6. 我认为你想要做的是在你的架构上实现一个实例方法,你可以用它来定义你想要的逻辑。你可以在调用.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

答案 3 :(得分:0)

origDocument有this.toJSON的引用,所以当post.toJSON在post(&#39; save&#39;)中更改时,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 :(得分:0)

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

E.g。

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

答案 5 :(得分:0)

在我们的 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
    }
}

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