Mongoose Schema:
new Schema({
...
createDate: { type: Date, default: Date.now },
updateDate: { type: Date, default: Date.now }
});
Upsert operation:
const upsertDoc = {
...
}
Model.update({ key: 123 }, upsertDoc, { upsert: true })
when I upsert with update
or findOneAndUpdate
the default schema values createDate
and updateDate
are always renewed no matter document is inserted or updated. It's same when I use $set
(in which of course I don't pass dates).
I don't seem to find anything to tell if it's an expected behavior. I expect dates to be added only on insert and not update, unless explicitly set.
If you are looking for "proof" of the expected behavior, then look no further than the source code itself. Particularly within the schema.js
main definition:
updates.$setOnInsert = {};
updates.$setOnInsert[createdAt] = now;
}
return updates;
};
this.methods.initializeTimestamps = function() {
if (createdAt && !this.get(createdAt)) {
this.set(createdAt, new Date());
}
if (updatedAt && !this.get(updatedAt)) {
this.set(updatedAt, new Date());
}
return this;
};
this.pre('findOneAndUpdate', _setTimestampsOnUpdate);
this.pre('update', _setTimestampsOnUpdate);
this.pre('updateOne', _setTimestampsOnUpdate);
this.pre('updateMany', _setTimestampsOnUpdate);
}
function _setTimestampsOnUpdate(next) {
var overwrite = this.options.overwrite;
this.update({}, genUpdates(this.getUpdate(), overwrite), {
overwrite: overwrite
});
applyTimestampsToChildren(this);
next();
}
So there you can see all the 'pre'
middleware handlers being registered for each of the "update" method variants and to the same functional code. These all essentially modify the $set
operator in any "update" you issue to include the updatedAt
field, or whatever name you mapped to that key in the schema options.
The actual statement sent with "upsert" actions uses $setOnInsert
for the createdAt
field or mapped option name ( see the top of the listing ). This action only applies when an "upsert" actually occurs, so documents that exist and are merely matches for any of the "update" methods are never actually touched by this value.
Those operators are part of how MongoDB works and not really to do with mongoose, but the code shown here shows how mongoose "adjusts" your "update" actions in order to include these additional operations.
For reference the whole main function in schema.js
which works out what to apply currently begins at Line #798 for the genUpdates()
function as called in the bottom part of the listing shown here yet the top part is the last few lines of that function where the keys of $setOnInsert
get defined.
So in summary, YES every "update" action is intentional that the updatedAt
mapped field has the current Date
value assigned, and also that the "updates" are modified to include the $setOnInsert
action which only applies when a new document is created as the result of an "upsert" action for the createdAt
mapped field.
Well, I'd always recommend to use the provided and recommended way to manage createdAt
and updatedAt
by mongoose. Simply by passing timeStamp: true
as schema options.
This is always a best practice and lets you not to be worried about such behaviors.
I use it and I never see a problem with timestamps using update
or findOneAndUpdate
.
Here is how you use it
new Schema({
... //Your schema
},{ timestamps: true})