You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
3.0 KiB

  1. /*!
  2. * Module dependencies.
  3. */
  4. var Mixed = require('../schema/mixed');
  5. var ValidationError = require('../error/validation');
  6. var async = require('async');
  7. var flatten = require('./common').flatten;
  8. var modifiedPaths = require('./common').modifiedPaths;
  9. /**
  10. * Applies validators and defaults to update and findOneAndUpdate operations,
  11. * specifically passing a null doc as `this` to validators and defaults
  12. *
  13. * @param {Query} query
  14. * @param {Schema} schema
  15. * @param {Object} castedDoc
  16. * @param {Object} options
  17. * @method runValidatorsOnUpdate
  18. * @api private
  19. */
  20. module.exports = function(query, schema, castedDoc, options) {
  21. var keys = Object.keys(castedDoc || {});
  22. var updatedKeys = {};
  23. var updatedValues = {};
  24. var numKeys = keys.length;
  25. var hasDollarUpdate = false;
  26. var modified = {};
  27. for (var i = 0; i < numKeys; ++i) {
  28. if (keys[i].charAt(0) === '$') {
  29. modifiedPaths(castedDoc[keys[i]], '', modified);
  30. var flat = flatten(castedDoc[keys[i]]);
  31. var paths = Object.keys(flat);
  32. var numPaths = paths.length;
  33. for (var j = 0; j < numPaths; ++j) {
  34. var updatedPath = paths[j].replace('.$.', '.0.');
  35. updatedPath = updatedPath.replace(/\.\$$/, '.0');
  36. if (keys[i] === '$set' || keys[i] === '$setOnInsert') {
  37. updatedValues[updatedPath] = flat[paths[j]];
  38. } else if (keys[i] === '$unset') {
  39. updatedValues[updatedPath] = undefined;
  40. }
  41. updatedKeys[updatedPath] = true;
  42. }
  43. hasDollarUpdate = true;
  44. }
  45. }
  46. if (!hasDollarUpdate) {
  47. modifiedPaths(castedDoc, '', modified);
  48. updatedValues = flatten(castedDoc);
  49. updatedKeys = Object.keys(updatedValues);
  50. }
  51. var updates = Object.keys(updatedValues);
  52. var numUpdates = updates.length;
  53. var validatorsToExecute = [];
  54. var validationErrors = [];
  55. function iter(i) {
  56. var schemaPath = schema._getSchema(updates[i]);
  57. if (schemaPath) {
  58. // gh-4305: `_getSchema()` will report all sub-fields of a 'Mixed' path
  59. // as 'Mixed', so avoid double validating them.
  60. if (schemaPath instanceof Mixed && schemaPath.path !== updates[i]) {
  61. return;
  62. }
  63. validatorsToExecute.push(function(callback) {
  64. schemaPath.doValidate(
  65. updatedValues[updates[i]],
  66. function(err) {
  67. if (err) {
  68. err.path = updates[i];
  69. validationErrors.push(err);
  70. }
  71. callback(null);
  72. },
  73. options && options.context === 'query' ? query : null,
  74. {updateValidator: true});
  75. });
  76. }
  77. }
  78. for (i = 0; i < numUpdates; ++i) {
  79. iter(i);
  80. }
  81. return function(callback) {
  82. async.parallel(validatorsToExecute, function() {
  83. if (validationErrors.length) {
  84. var err = new ValidationError(null);
  85. for (var i = 0; i < validationErrors.length; ++i) {
  86. err.errors[validationErrors[i].path] = validationErrors[i];
  87. }
  88. return callback(err);
  89. }
  90. callback(null);
  91. });
  92. };
  93. };