Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Prevent updatedAt change in Mongoose findOneAndUpdate

I have Mongoose model updated and then retrieved with $inc operator that implements simple view counter:

const profile = await Profile.findOneAndUpdate({ userName }, { $inc: { viewsCount: 1 } });

Profile schema has timestamps option enabled. The problem is that updatedAt is updated during viewsCount update, this is not a desirable behaviour.

I would like to to disable updatedAt updates when viewsCount is updated, preferably by doing as few queries as possible.

I assume that Mongoose timestamps are implemented with pre-hooks.

How can findOneAndUpdate increment viewsCount without updating updatedAt?

Can this be done by negating the effect of timestamp hook?

There are similar questions that address the problem with updatedAt updates but solutions don't suit the case.

like image 374
Estus Flask Avatar asked Feb 15 '19 21:02

Estus Flask


People also ask

What does findOneAndUpdate return mongoose?

As the name implies, findOneAndUpdate() finds the first document that matches a given filter , applies an update , and returns the document. By default, findOneAndUpdate() returns the document as it was before update was applied. You should set the new option to true to return the document after update was applied.

Why we use $set in mongoose?

By default, if you don't include any update operators in doc, Mongoose will wrap doc in $set for you. This prevents you from accidentally overwriting the document.

What is update query in mongoose?

Mongoose | update() Function The update() function is used to update one document in the database without returning it.

What is model in mongoose?

A Mongoose model is a wrapper on the Mongoose schema. A Mongoose schema defines the structure of the document, default values, validators, etc., whereas a Mongoose model provides an interface to the database for creating, querying, updating, deleting records, etc.


2 Answers

Starting with mongoose 5.9.3, you can set the third parameter to { timestamps: false }.

From the docs:

[options.timestamps=null] «Boolean» If set to false and schema-level timestamps are enabled, skip timestamps for this update. Note that this allows you to overwrite timestamps. Does nothing if schema-level timestamps are not set.

like image 122
seankwalker Avatar answered Sep 19 '22 03:09

seankwalker


After scouring the Mongoose documentation, it appears that the short answer is no, there is no way to call findOneAndUpdate() without the updatedAt field being set. If you view the docs here, it clearly states that all of the functions for updating (findOneAndUpdate(), update() and bulkWrite()) add the $set operator to the query to set updatedAt. That being said, you do have another option which I would highly recommend.

Work Around

Add the createdAt and updatedAt fields directly to your models, and get rid of the timestamps. Sure, it's really nice that Mongoose handles it for you, but in the end, if you have to try and change the set functionality of the timestamps for a specific use case, it would be better to do it on your own.

If you enjoy having the updatedAt and createdAt set when you call .save() or .update(), you can always write your own middleware to add in the operations.

ProfileSchema.pre('save', function(next) {
  this.createdAt = Date.now();
  this.updatedAt = Date.now();
  next();
});

I understand this isn't the desired solution, but quite frankly I find it more useful since have greater control, and less unexpected behavior from Mongoose. The more control you have over your own code, the better. Hope this helps!

like image 20
ajbieber Avatar answered Sep 19 '22 03:09

ajbieber