Keeping MongoDB attributes in sync can be tricky. But there’s more than one way to skin a cat!

Micah Bales
Aug 26, 2017 · 2 min read

Today I was attempting to use Mongoose middlewarepre('findOneAndUpdate') — to update the icon attribute of a document calledMeeting. My updated icon value depended on the pre-existing value of the yearlymeeting attribute in the Meeting record (see below).

In Mongoose, pre and post save() hooks are executed on save(), but not on update(), so I was unable to access the original document at all. Yet it was essential for this operation that I have access to the stored document.

For example, I am able to accomplish my purpose on pre('save'), like so:

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

What I would like to be able to do is something like this:

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

Unfortunately for me,this in pre('findOneAndUpdate') refers to the query, rather than the stored document itself. This means that the information I needed is located only in the database, where I didn’t have access to it.

After quite a bit of brainstorming, research, and even posting the problem on StackOverflow, I eventually concluded that the best way to solve this problem was to give up trying to update the icon attribute from Mongoose middleware in the schema. Instead, I would perform the operation from a place where I knew I would have access to the existing document — in the controller!

Here’s the code I ended up using to solve my problem:

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

What do you think about this solution? Was moving the operation over into the controller, rather than the schema, the right way to go about it? How would you have solved this problem?

)

Micah Bales

Written by

Full-stack software developer in Washington, DC. Running wild in the Javascript/Node.js ecosystem. Crunching IoT data and making it beautiful.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade