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)
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. 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.
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.
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()
. 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.
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?
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
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:
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. But the [MIDDLEWARE] original/updated values are still the same... So im still stuck as to why
I figured maybe I could provide a more simplified but detailed example.
Heres the middleware module thats supposed to compare the pre(save)
and
post(save)
:
'use strict'
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...
'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
You can define your custom middleware functions on the schema level itself using these hooks. Here the classical example would be the use of pre-save hook to encrypt the password while the document is being saved or if the password has changed during an update operation.
save() is considered to be an instance method of the model, while the . create() is called straight from the Model as a method call, being static in nature, and takes the object as a first parameter.
Mongoose | save() Function The save() function is used to save the document to the database. Using this function, new documents can be added to the database.
It might be obvious, but a pre-save hook is middleware that is executed when a document is saved.
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.
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, i.e. the updated document.
post.save
holds the real document that is stored in the database, hence still the updated version. So you can not see the change that is done just looking at this
in both pre
and post
save
.
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, i.e. in 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)
} )
}
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With