setupTimestamps.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107
  1. 'use strict';
  2. const applyTimestampsToChildren = require('../update/applyTimestampsToChildren');
  3. const applyTimestampsToUpdate = require('../update/applyTimestampsToUpdate');
  4. const get = require('../get');
  5. const handleTimestampOption = require('../schema/handleTimestampOption');
  6. const setDocumentTimestamps = require('./setDocumentTimestamps');
  7. const symbols = require('../../schema/symbols');
  8. const replaceOps = new Set([
  9. 'replaceOne',
  10. 'findOneAndReplace'
  11. ]);
  12. module.exports = function setupTimestamps(schema, timestamps) {
  13. const childHasTimestamp = schema.childSchemas.find(withTimestamp);
  14. function withTimestamp(s) {
  15. const ts = s.schema.options.timestamps;
  16. return !!ts;
  17. }
  18. if (!timestamps && !childHasTimestamp) {
  19. return;
  20. }
  21. const createdAt = handleTimestampOption(timestamps, 'createdAt');
  22. const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
  23. const currentTime = timestamps != null && Object.hasOwn(timestamps, 'currentTime') ?
  24. timestamps.currentTime :
  25. null;
  26. const schemaAdditions = {};
  27. schema.$timestamps = { createdAt: createdAt, updatedAt: updatedAt };
  28. if (createdAt && !schema.paths[createdAt]) {
  29. const baseImmutableCreatedAt = schema.base?.get('timestamps.createdAt.immutable') ?? null;
  30. const immutable = baseImmutableCreatedAt ?? true;
  31. schemaAdditions[createdAt] = { [schema.options.typeKey || 'type']: Date, immutable };
  32. }
  33. if (updatedAt && !schema.paths[updatedAt]) {
  34. schemaAdditions[updatedAt] = Date;
  35. }
  36. schema.add(schemaAdditions);
  37. schema.pre('save', function timestampsPreSave() {
  38. const timestampOption = get(this, '$__.saveOptions.timestamps');
  39. if (timestampOption === false) {
  40. return;
  41. }
  42. setDocumentTimestamps(this, timestampOption, currentTime, createdAt, updatedAt);
  43. });
  44. schema.methods.initializeTimestamps = function() {
  45. const ts = currentTime != null ?
  46. currentTime() : this.constructor.base.now();
  47. if (createdAt && !this.get(createdAt)) {
  48. this.$set(createdAt, ts);
  49. }
  50. if (updatedAt && !this.get(updatedAt)) {
  51. this.$set(updatedAt, ts);
  52. }
  53. if (this.$isSubdocument) {
  54. return this;
  55. }
  56. const subdocs = this.$getAllSubdocs();
  57. for (const subdoc of subdocs) {
  58. if (subdoc.initializeTimestamps) {
  59. subdoc.initializeTimestamps();
  60. }
  61. }
  62. return this;
  63. };
  64. _setTimestampsOnUpdate[symbols.builtInMiddleware] = true;
  65. const opts = { query: true, model: false };
  66. schema.pre('findOneAndReplace', opts, _setTimestampsOnUpdate);
  67. schema.pre('findOneAndUpdate', opts, _setTimestampsOnUpdate);
  68. schema.pre('replaceOne', opts, _setTimestampsOnUpdate);
  69. schema.pre('update', opts, _setTimestampsOnUpdate);
  70. schema.pre('updateOne', opts, _setTimestampsOnUpdate);
  71. schema.pre('updateMany', opts, _setTimestampsOnUpdate);
  72. function _setTimestampsOnUpdate() {
  73. const now = currentTime != null ?
  74. currentTime() :
  75. this.model.base.now();
  76. // Replacing with null update should still trigger timestamps
  77. if (replaceOps.has(this.op) && this.getUpdate() == null) {
  78. this.setUpdate({});
  79. }
  80. applyTimestampsToUpdate(
  81. now,
  82. createdAt,
  83. updatedAt,
  84. this.getUpdate(),
  85. this._mongooseOptions,
  86. replaceOps.has(this.op)
  87. );
  88. applyTimestampsToChildren(now, this.getUpdate(), this.model.schema);
  89. }
  90. };