简体   繁体   English

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

[英]Sharing data between Mongoose middleware methods pre save and post save

SEE UPDATED EXAMPLE CODE @ BOTTOM在底部查看更新的示例代码

I'm using Mongoose (which is awesome btw!) in my current NodeJS projects, and I have a MDB collection thats going to store the changes of documents in a different collection (Basically a changelog storing what was modified)我在我当前的 NodeJS 项目中使用 Mongoose(顺便说一句,这太棒了!),我有一个 MDB 集合,它将在不同的集合中存储文档的更改(基本上是一个存储修改内容的更改日志

How I'm trying to accomplish that is create a function that stores a JSON version of the document, which is done via the pre('save') hook.我试图实现的方法是创建一个函数来存储文档的 JSON 版本,这是通过pre('save')钩子完成的。 Then create another hook, which gets executed via post('save') , to compare the data stored in pre('save') , and compare it with the documents new data.然后创建另一个钩子,它通过post('save') ,以比较存储在pre('save') ,并将其与文档新数据进行比较。

Heres what I have thus far:这是我迄今为止所拥有的:

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

I originally didn't think this would work.我原本不认为这会奏效。 To test that the two hooks get executed in the same scope, I created a test variable at the top of the page called testVar with some arbitrary value, then in the post(save) hook, retrieved the testVar , and the value modification of that variable was seen in the post save hook.为了测试这两个钩子是否在同一范围内执行,我在页面顶部创建了一个名为testVar的测试变量,其中包含一些任意值,然后在post(save)钩子中,检索了testVar ,以及它的值修改在 post save hook 中看到了变量。

So from there, I just stored the value of this.toJSON() in a variable, then in the post(save) hook, I am trying to retrieve the cached version of this document, and compare it to this.toJSON() .所以从那里开始,我只是将this.toJSON()的值存储在一个变量中,然后在 post(save) 挂钩中,我试图检索该文档的缓存版本,并将其与this.toJSON()进行比较。 However, it doesn't look like the document from the pre(save) doesnt hold the pre-modified data, it somehow has the value of the document after it was updated.但是,看起来pre(save)的文档并不包含预修改的数据,它在更新以某种方式具有文档的值。

So why can I update the value of testVar from within a pre(save) hook, and that change is reflected from a post(save) hook function, but I cant do the same thing with the document itself?那么为什么我可以从pre(save)钩子中更新testVar的值,而这种变化反映在post(save)钩子函数中,但我不能对文档本身做同样的事情?

Is what im trying to do here even possible?我在这里尝试做的事情甚至可能吗? If so, what am I doing wrong?如果是这样,我做错了什么? If not - How can I accomplish this?如果没有 - 我怎样才能做到这一点?

Thank you谢谢

Update更新

Per the advice from @Avraam, I tried to run the data through JSON.stringify() before saving it in memory via the pre(save) hook, then do the same in the post(save) , like so:根据@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 )
    } )
}

And here's the script that updates the mongoose document:这是更新猫鼬文档的脚本:

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

Heres the console output when I run the New script:这是我运行新脚本时的控制台输出:

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

As you can see, the [QUERY] ORIGINAL value and the [QUERY] UPDATED values show that there was an update.如您所见, [QUERY] ORIGINAL值和[QUERY] UPDATED值表明存在更新。 But the [MIDDLEWARE] original/updated values are still the same... So im still stuck as to why但是[MIDDLEWARE]原始/更新值仍然相同......所以我仍然坚持为什么

UPDATE更新

I figured maybe I could provide a more simplified but detailed example.我想也许我可以提供一个更简单但更详细的例子。

Heres the middleware module thats supposed to compare the pre(save) and这是应该比较pre(save)

post(save) : 'use strict' 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)
    } )
}

Then heres the the code that uses the Asset model and updates the Description attribute...然后是使用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

Ok, The first part of your question is correctly answered by Avraam Mavridis so I will only focus on your last update in the question.好的,您问题的第一部分由 Avraam Mavridis 正确回答,因此我将只关注您在问题中的最后更新。

pre.save actually doesn't hold the actual document that currently exixts in the database, instead it is the document which is going to be saved, and contains the changes done to the document, ie the updated document. pre.save实际上并不保存当前存在于数据库中的实际文档,而是将要保存的文档,并包含对文档所做的更改,即更新的文档。

post.save holds the real document that is stored in the database, hence still the updated version. post.save保存存储在数据库中的真实文档,因此仍然是更新版本。 So you can not see the change that is done just looking at this in both pre and post save .因此,在save presave post仅查看this ,您无法看到所做的更改。

Now if you want to see the real values that existed in the database you need to fetch it from database before it is changed and saved, ie in pre.save .现在,如果您想查看数据库中存在的真实值,您需要在更改和保存之前从数据库中获取它,即在pre.save


One way you could do this is simply query the document from database 您可以这样做的一种方法是简单地从数据库中查询文档

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


There is an alternative way than this using custom setter, and there is already a good answer here , but this would require to set a custom setter for each property 还有一种使用自定义设置器的替代方法,这里已经有一个很好的答案,但这需要为每个属性设置一个自定义设置器

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

Hope this helps.希望这可以帮助。

The origDocument has reference to the this.toJSON() and the moment you call the console.log the value of the actual object where the reference points has already change. origDocument引用了this.toJSON()并且当您调用console.log ,引用点已经更改的实际对象的值。 Use something like JSON.stringify to compare the values.使用类似JSON.stringify东西来比较这些值。

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

I think you are misunderstanding how the pre/post hooks work in mongoose.我认为你误解了 pre/post 钩子在猫鼬中是如何工作的。 When you grab the document (As you are doing) and resaving it.当您抓取文档时(正如您所做的那样)并重新保存它。 It will not have whatever variable was originally in the document.它不会有文档中最初的任何变量。 It will have whatever is currently in the document.它将包含文档中当前的任何内容。

So, you are doing this:所以,你正在这样做:

  1. Grab Document (67)抓取文件 (67)
  2. Modify Document < (You did the +1 here) (68 now)修改文档 <(你在这里做了 +1)(现在 68)
  3. Document.Save() called Document.Save() 调用
  4. Pre-save printing out the current document (68)预先保存打印出当前文档 (68)
  5. Post-save printing out the current document (68)保存后打印出当前文档 (68)

I think what you want to do is implement an instance method on your schema which you can use to define the logic you want.我认为您想要做的是在您的架构上实现一个实例方法,您可以使用它来定义您想要的逻辑。 You would call this before you call .save() (Or use it to just call .save() after you have performed your own logic)你会在调用 .save() 之前调用它(或者在你执行自己的逻辑之后使用它来调用 .save() )

example:例子:

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

Hope this helps希望这可以帮助

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

origDocument have the reference of this.toJSON so when this.toJSON changed in post('save') origDocument also get changed. origDocument 有 this.toJSON 的引用,所以当 this.toJSON 在 post('save') origDocument 中改变时也会改变。 Try below code:试试下面的代码:

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

Using JSON.parse(JSON.stringify()) I have cleared the reference.使用 JSON.parse(JSON.stringify()) 我清除了参考。

Hope this helps!!!希望这可以帮助!!!

You can use yet another middleware, and temporarily set the current value to a non-defined attribute (so it won't be saved to DB upon save call).您可以使用另一个中间件,并将当前值临时设置为未定义的属性(因此它不会在save调用时保存到数据库中)。

Eg例如

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

And then in post save hook do the comparison:然后在 post save hook 中进行比较:

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

In our API, I solved this by using document.$locals as the storage location for the original values.在我们的 API 中,我通过使用document.$locals作为原始值的存储位置解决了这个问题。 document.$locals does not get passed to the database and it gets persisted between middleware calls. document.$locals不会传递到数据库,它会在中间件调用之间持久化。

In post('find') and post('findOne') hooks:post('find')post('findOne')钩子中:

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

In pre('save') and post('save') hooks: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
    }
}

Those are the relevant parts of the code.这些是代码的相关部分。 There's plenty of error checking and edge case detection as well, but fundamentally we're storing the original document whenever we retrieve it, so those values can be compared to the data being saved.还有很多错误检查和边缘情况检测,但从根本上说,我们每次检索时都会存储原始文档,因此可以将这些值与保存的数据进行比较。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM