在Mongoose中间件方法之间共享数据前保存和后保存

查看更新示例代码@底部

我在当前的NodeJS项目中使用了Mongoose(这很棒btw!),我有一个MDB集合,它将文档的更改存储在不同的集合中(基本上是一个更改日志,用于存储已修改的内容)

我试图做到这一点是创build一个函数,存储一个JSON版本的文件,这是通过pre('save')钩完成。 然后创build另一个钩子,通过post('save') ,比较存储在pre('save') ,并将其与文档新数据进行比较。

inheritance人到目前为止:

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

我原本不认为这会奏效。 为了testing这两个钩子是否在相同的作用域中执行,我在页面顶部创build了一个名为testVar的testingvariables,其中包含一些任意的值,然后在post(save)钩子中取回testVar ,并修改variables被看到在后保存钩子。

所以从那里,我只是将this.toJSON()的值存储在一个variables中,然后在post(save)钩子中,我试图检索这个文档的caching版本,并将其与this.toJSON()进行比较。 但是,它看起来不像pre(save)的文档不包含预先修改的数据,它在更新之后以某种方式具有文档的值。

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

我即使在这里做甚么可能? 如果是这样,我做错了什么? 如果不是 – 我该如何做到这一点?

谢谢

更新

根据@Avraam的build议,我尝试通过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]原始/更新的值仍然是相同的…所以我仍然坚持为什么

UPDATE

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

下面是应该比较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) } ) } 

然后inheritance使用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 

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

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

post.save保存存储在数据库中的真实文档,因此仍然是更新的版本。 所以你不能在save pre看到这个变化。

现在,如果要查看数据库中存在的真实值,则需要在数据库更改并保存之前从数据库中获取它,即在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的替代方法,这里已经有一个很好的答案 ,但这需要为每个属性设置一个自定义设置器

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

希望这可以帮助。

origDocument引用了this.toJSON()和您调用console.log的那一刻参考点已经改变的实际对象的值。 使用类似JSON.stringify东西比较值。

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

我想你是误解了猫头鹰的前/后钩子是如何工作的。 当你抓住文件(如你所做的那样)并保存它。 它不会有任何variables原来在文件中。 它将具有文档中的任何内容。

所以,你这样做:

  1. 抓取文件(67)
  2. 修改文件<(你在这里+1)(现在68)
  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

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())我已经清除了引用。

希望这可以帮助!!!

您可以使用另一个中间件,并暂时将当前值设置为一个未定义的属性 (因此在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}); }); 

然后在后面的save hook做比较:

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