applyTimestampsToUpdate.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131
  1. 'use strict';
  2. /*!
  3. * ignore
  4. */
  5. const get = require('../get');
  6. const utils = require('../../utils');
  7. module.exports = applyTimestampsToUpdate;
  8. /*!
  9. * ignore
  10. */
  11. function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options, isReplace) {
  12. const updates = currentUpdate;
  13. let _updates = updates;
  14. const timestamps = get(options, 'timestamps', true);
  15. // Support skipping timestamps at the query level, see gh-6980
  16. if (!timestamps || updates == null) {
  17. return currentUpdate;
  18. }
  19. const skipCreatedAt = timestamps?.createdAt === false;
  20. const skipUpdatedAt = timestamps?.updatedAt === false;
  21. if (isReplace) {
  22. if (currentUpdate?.$set) {
  23. currentUpdate = currentUpdate.$set;
  24. updates.$set = {};
  25. _updates = updates.$set;
  26. }
  27. if (!skipUpdatedAt && updatedAt && !currentUpdate[updatedAt]) {
  28. _updates[updatedAt] = now;
  29. }
  30. if (!skipCreatedAt && createdAt && !currentUpdate[createdAt]) {
  31. _updates[createdAt] = now;
  32. }
  33. return updates;
  34. }
  35. currentUpdate = currentUpdate || {};
  36. if (Array.isArray(updates)) {
  37. // Update with aggregation pipeline
  38. if (updatedAt == null) {
  39. return updates;
  40. }
  41. updates.push({ $set: { [updatedAt]: now } });
  42. return updates;
  43. }
  44. updates.$set = updates.$set || {};
  45. if (!skipUpdatedAt && updatedAt &&
  46. !currentUpdate.$currentDate?.[updatedAt]) {
  47. let timestampSet = false;
  48. if (updatedAt.indexOf('.') !== -1) {
  49. const pieces = updatedAt.split('.');
  50. for (let i = 1; i < pieces.length; ++i) {
  51. const remnant = pieces.slice(-i).join('.');
  52. const start = pieces.slice(0, -i).join('.');
  53. if (currentUpdate[start] != null) {
  54. currentUpdate[start][remnant] = now;
  55. timestampSet = true;
  56. break;
  57. } else if (currentUpdate.$set?.[start]) {
  58. currentUpdate.$set[start][remnant] = now;
  59. timestampSet = true;
  60. break;
  61. }
  62. }
  63. }
  64. if (!timestampSet) {
  65. updates.$set[updatedAt] = now;
  66. }
  67. if (Object.hasOwn(updates, updatedAt)) {
  68. delete updates[updatedAt];
  69. }
  70. }
  71. if (!skipCreatedAt && createdAt) {
  72. const overwriteImmutable = get(options, 'overwriteImmutable', false);
  73. const hasUserCreatedAt = currentUpdate[createdAt] != null || currentUpdate.$set?.[createdAt] != null;
  74. // If overwriteImmutable is true and user provided createdAt, keep their value
  75. if (overwriteImmutable && hasUserCreatedAt) {
  76. // Move createdAt from top-level to $set if needed
  77. if (currentUpdate[createdAt] != null) {
  78. updates.$set[createdAt] = currentUpdate[createdAt];
  79. delete currentUpdate[createdAt];
  80. }
  81. // User's value is already in $set, nothing more to do
  82. } else {
  83. if (currentUpdate[createdAt]) {
  84. delete currentUpdate[createdAt];
  85. }
  86. if (currentUpdate.$set?.[createdAt]) {
  87. delete currentUpdate.$set[createdAt];
  88. }
  89. let timestampSet = false;
  90. if (createdAt.indexOf('.') !== -1) {
  91. const pieces = createdAt.split('.');
  92. for (let i = 1; i < pieces.length; ++i) {
  93. const remnant = pieces.slice(-i).join('.');
  94. const start = pieces.slice(0, -i).join('.');
  95. if (currentUpdate[start] != null) {
  96. currentUpdate[start][remnant] = now;
  97. timestampSet = true;
  98. break;
  99. } else if (currentUpdate.$set?.[start]) {
  100. currentUpdate.$set[start][remnant] = now;
  101. timestampSet = true;
  102. break;
  103. }
  104. }
  105. }
  106. if (!timestampSet) {
  107. updates.$setOnInsert = updates.$setOnInsert || {};
  108. updates.$setOnInsert[createdAt] = now;
  109. }
  110. }
  111. }
  112. if (utils.hasOwnKeys(updates.$set) === false) {
  113. delete updates.$set;
  114. }
  115. return updates;
  116. }