19

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.

Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • Duplicated of: https://stackoverflow.com/questions/38621790/skip-timestamps-middleware-for-certain-updates-in-mongoose/45707811#45707811 – danii Dec 27 '19 at 12:19

3 Answers3

19

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.

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
seankwalker
  • 326
  • 2
  • 4
5

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!

ajbieber
  • 729
  • 4
  • 10
  • Thanks. I was hoping for cleaner solution. A proper implementation of timestamps won't be that simple. I'll likely fork third-party timestamp plugin. – Estus Flask Feb 26 '19 at 20:04
  • I maintained the default timestamps: { createdAt: Date.now, updatedAt: 'last_written' } to know when I do admin duties and I want to know when my records are updated, then I create a additional updatedAt timestamp which I maintain manually. auto last updated timestamps can be very useful. I don't recommend getting rid of it. – Someone Special Dec 08 '20 at 03:27
1

I've set the createdAt to be immutable. So that it can't update even we edit the concerned document in a collection and set updatedAt field to take the current timestamp using new Date(). It worked well for me. Check the code snippet below.

createdAt: {
  type: Date,
  immutable: true,
  default: Date.now
},
updatedAt: {
  type: Date,
  default: new Date()
}

I've set timestamps to be true.

Please check the total Schema related to the above code snippet.

const projectSchema = mongoose.Schema({
  name: {
    type: String,
    requied: true
  },
  description: {
    type: String,
    required: true
  },
  owner: {
    type: mongoose.Schema.Types.ObjectId,
    ref: "User"
  },
  createdAt: {
    type: Date,
    immutable: true,
    default: Date.now
  },
  updatedAt: {
    type: Date,
    default: new Date()
  }
}, {
  timestamps:true
});
Tyler2P
  • 2,324
  • 26
  • 22
  • 31