簡體   English   中英

在保存前和保存后的 Mongoose 中間件方法之間共享數據

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

在底部查看更新的示例代碼

我在我當前的 NodeJS 項目中使用 Mongoose(順便說一句,這太棒了!),我有一個 MDB 集合,它將在不同的集合中存儲文檔的更改(基本上是一個存儲修改內容的更改日志

我試圖實現的方法是創建一個函數來存儲文檔的 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)鈎子中,檢索了testVar ,以及它的值修改在 post save hook 中看到了變量。

所以從那里開始,我只是將this.toJSON()的值存儲在一個變量中,然后在 post(save) 掛鈎中,我試圖檢索該文檔的緩存版本,並將其與this.toJSON()進行比較。 但是,看起來pre(save)的文檔並不包含預修改的數據,它在更新以某種方式具有文檔的值。

那么為什么我可以從pre(save)鈎子中更新testVar的值,而這種變化反映在post(save)鈎子函數中,但我不能對文檔本身做同樣的事情?

我在這里嘗試做的事情甚至可能嗎? 如果是這樣,我做錯了什么? 如果沒有 - 我怎樣才能做到這一點?

謝謝

更新

根據@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 )
    } )
}

這是更新貓鼬文檔的腳本:

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]原始/更新值仍然相同......所以我仍然堅持為什么

更新

我想也許我可以提供一個更簡單但更詳細的例子。

這是應該比較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模型並更新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 presave post僅查看this ,您無法看到所做的更改。

現在,如果您想查看數據庫中存在的真實值,您需要在更改和保存之前從數據庫中獲取它,即在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) } )

希望這可以幫助。

origDocument引用了this.toJSON()並且當您調用console.log ,引用點已經更改的實際對象的值。 使用類似JSON.stringify東西來比較這些值。

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

我認為你誤解了 pre/post 鈎子在貓鼬中是如何工作的。 當您抓取文檔時(正如您所做的那樣)並重新保存它。 它不會有文檔中最初的任何變量。 它將包含文檔中當前的任何內容。

所以,你正在這樣做:

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

然后在 post save hook 中進行比較:

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

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

這些是代碼的相關部分。 還有很多錯誤檢查和邊緣情況檢測,但從根本上說,我們每次檢索時都會存儲原始文檔,因此可以將這些值與保存的數據進行比較。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM