setDefaultsOnInsert.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118
  1. 'use strict';
  2. var modifiedPaths = require('./common').modifiedPaths;
  3. /**
  4. * Applies defaults to update and findOneAndUpdate operations.
  5. *
  6. * @param {Object} filter
  7. * @param {Schema} schema
  8. * @param {Object} castedDoc
  9. * @param {Object} options
  10. * @method setDefaultsOnInsert
  11. * @api private
  12. */
  13. module.exports = function(filter, schema, castedDoc, options) {
  14. var keys = Object.keys(castedDoc || {});
  15. var updatedKeys = {};
  16. var updatedValues = {};
  17. var numKeys = keys.length;
  18. var hasDollarUpdate = false;
  19. var modified = {};
  20. if (options && options.upsert) {
  21. for (var i = 0; i < numKeys; ++i) {
  22. if (keys[i].charAt(0) === '$') {
  23. modifiedPaths(castedDoc[keys[i]], '', modified);
  24. hasDollarUpdate = true;
  25. }
  26. }
  27. if (!hasDollarUpdate) {
  28. modifiedPaths(castedDoc, '', modified);
  29. }
  30. var paths = Object.keys(filter);
  31. var numPaths = paths.length;
  32. for (i = 0; i < numPaths; ++i) {
  33. var path = paths[i];
  34. var condition = filter[path];
  35. if (condition && typeof condition === 'object') {
  36. var conditionKeys = Object.keys(condition);
  37. var numConditionKeys = conditionKeys.length;
  38. var hasDollarKey = false;
  39. for (var j = 0; j < numConditionKeys; ++j) {
  40. if (conditionKeys[j].charAt(0) === '$') {
  41. hasDollarKey = true;
  42. break;
  43. }
  44. }
  45. if (hasDollarKey) {
  46. continue;
  47. }
  48. }
  49. updatedKeys[path] = true;
  50. modified[path] = true;
  51. }
  52. if (options && options.overwrite && !hasDollarUpdate) {
  53. // Defaults will be set later, since we're overwriting we'll cast
  54. // the whole update to a document
  55. return castedDoc;
  56. }
  57. if (options.setDefaultsOnInsert) {
  58. schema.eachPath(function(path, schemaType) {
  59. if (path === '_id') {
  60. // Ignore _id for now because it causes bugs in 2.4
  61. return;
  62. }
  63. if (schemaType.$isSingleNested) {
  64. // Only handle nested schemas 1-level deep to avoid infinite
  65. // recursion re: https://github.com/mongodb-js/mongoose-autopopulate/issues/11
  66. schemaType.schema.eachPath(function(_path, _schemaType) {
  67. if (path === '_id') {
  68. // Ignore _id for now because it causes bugs in 2.4
  69. return;
  70. }
  71. var def = _schemaType.getDefault(null, true);
  72. if (!isModified(modified, path + '.' + _path) &&
  73. typeof def !== 'undefined') {
  74. castedDoc = castedDoc || {};
  75. castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
  76. castedDoc.$setOnInsert[path + '.' + _path] = def;
  77. updatedValues[path + '.' + _path] = def;
  78. }
  79. });
  80. } else {
  81. var def = schemaType.getDefault(null, true);
  82. if (!isModified(modified, path) && typeof def !== 'undefined') {
  83. castedDoc = castedDoc || {};
  84. castedDoc.$setOnInsert = castedDoc.$setOnInsert || {};
  85. castedDoc.$setOnInsert[path] = def;
  86. updatedValues[path] = def;
  87. }
  88. }
  89. });
  90. }
  91. }
  92. return castedDoc;
  93. };
  94. function isModified(modified, path) {
  95. if (modified[path]) {
  96. return true;
  97. }
  98. var sp = path.split('.');
  99. var cur = sp[0];
  100. for (var i = 0; i < sp.length; ++i) {
  101. if (modified[cur]) {
  102. return true;
  103. }
  104. cur += '.' + sp[i];
  105. }
  106. return false;
  107. }