2017-08-25 58 views
0

我正在嘗試使用pre('findOneAndUpdate')來更新Meeting文檔的icon屬性。此更新基於yearlymeeting屬性的預先存在的值(請參閱下文)。Mongoose('findOneAndUpdate')中間件:需要訪問原始文檔

因爲prepostsave()掛鉤不update()執行,我似乎無法在所有訪問原始文檔。然而,這對我嘗試執行的操作至關重要。有沒有辦法解決?

例如,我能夠完成我的目的就pre('save'),就像這樣:

meetingSchema.pre('save', function(next) { 
    const yearlymeetingSlug = this.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, ''); 
    this.icon = `${yearlymeetingSlug}.png` 
    next(); 
}); 

我想做什麼就能做的是這樣的:

meetingSchema.pre('findOneAndUpdate', function(next) { 
    const yearlymeetingSlug = originalDocument.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, ''); 
    this.icon = `${yearlymeetingSlug}.png` 
    next(); 
}); 

我明白(findOneAndUpdate)中的this引用查詢,而不是存儲的文檔本身。有什麼方法可以訪問文檔,以便我可以根據存儲的yearlymeeting的值更新icon

+0

如果你明白了,那麼你就會明白,「文檔」在修改時位於「服務器」上,因此「客戶端」上的任何庫函數都不可能對其中的文檔進行更改。 '.findOneAndUpdate()'只知道你在指令中實際發送的「當前數據」。如果您的客戶端上已經有了字段內容,那麼您在將選項填入'$ set'或類似內容之前進行修改。如果你沒有這些數據,那麼你需要從服務器上取回它。 MongoDB無法引用現有值 –

回答

2

tl; dr

不可能通過中間件。首先查詢文檔,然後單獨更新文檔的特定版本以防止競爭條件。


不能做到這一點你根據this issue on the Mongoose Github (from the main dev)試圖方式:

通過設計 - 該文件被更新,甚至可能不是在服務器的內存中。爲了做到這一點,貓鼬必須在執行update()之前執行findOne()來加載文檔,這是不可接受的。

該設計使您能夠通過添加或刪除過濾器,更新參數,選項等操作查詢對象。例如,使用find()和findOne()自動調用.populate(),設置multi:在某些型號,訪問控制和其他可能性上默認爲true選項。

findOneAndUpdate()有點用詞不當,它使用底層的mongodb findAndModify命令,它與findOne()+ update()不一樣。作爲一個獨立的操作,它應該有它自己的中間件。

之後,在問題線程中沒有其他建議可以訪問中間件本身的原始文檔。

我見過做什麼(我不得不做很多次自己是什麼),簡直是在更新它(這當然會導致取決於誰的競爭條件之前,查詢該文檔正在更新文檔,而當,但您可以通過查詢也爲文檔的特定版本修復 - 一種「樂觀鎖定」):

let meeting = yield Meeting.findOne({}).exec() 
let update = {} 

// ... some conditional logic to figure out which icon to set 
update.icon = // whatever 

yield Meeting.update({ _id: meeting._id, version: meeting.version }, update) 

當然,這是假設你有一個「版本「字段在您的模式。這種鎖定會阻止您更新舊版本的文檔。如果你要使用這種版本的,你也可能會想添加一些中間件更新一個文檔的版本的文檔更新/保存的任何時間。

你也可以使用一個更天真的實現,在那裏你不使用鎖定,只要你意識到競爭條件的可能性和風險,在你的特定商業案例中這可能沒問題。

0

這可能不是最好的解決方案,但我確實找到了一種使其工作的方法。我使用了控制器而不是模式預鉤。以下是我的更新控制器現在的樣子:

exports.updateMeeting = async (req, res) => { 
    const _id = req.params.id 
    let meeting = await Meeting.findOneAndUpdate({ _id }, req.body, { 
    new: true, 
    runValidators: true 
    }); 

    /* New Code: */ 
    const yearlymeetingSlug = meeting.yearlymeeting[0].toLowerCase().replace(/[^A-z0-9]/g, ''); 
    meeting.icon = `${yearlymeetingSlug}.png`; 
    meeting.save(); 

    req.flash('success', 'meeting successfully updated!'); 
    res.redirect(`/meetings/${meeting.slug}`); 
}; 

對於您使用此解決方案看到的任何問題,歡迎您提出寶貴意見。

+1

'await meeting.save()'。它仍然是一個異步方法。 –