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};