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