在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原来在文件中。 它将具有文档中的任何内容。
所以,你这样做:
- 抓取文件(67)
- 修改文件<(你在这里+1)(现在68)
- 调用Document.Save()
- 预先保存打印当前文档(68)
- 保存后打印当前文档(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 });