summaryrefslogtreecommitdiffstats
path: root/node_modules/mongoose/lib/helpers/update
diff options
context:
space:
mode:
authorGravatar Piotr Russ <mail@pruss.it> 2020-11-16 00:10:28 +0100
committerGravatar Piotr Russ <mail@pruss.it> 2020-11-16 00:10:28 +0100
commite06ec920f7a5d784e674c4c4b4e6d1da3dc7391d (patch)
tree55713f725f77b44ebfec86e4eec3ce33e71458ca /node_modules/mongoose/lib/helpers/update
downloadwebsite_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.gz
website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.tar.bz2
website_creator-e06ec920f7a5d784e674c4c4b4e6d1da3dc7391d.zip
api, login, auth
Diffstat (limited to 'node_modules/mongoose/lib/helpers/update')
-rw-r--r--node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js172
-rw-r--r--node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js119
-rw-r--r--node_modules/mongoose/lib/helpers/update/castArrayFilters.js78
-rw-r--r--node_modules/mongoose/lib/helpers/update/modifiedPaths.js33
-rw-r--r--node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js53
-rw-r--r--node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js18
6 files changed, 473 insertions, 0 deletions
diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
new file mode 100644
index 0000000..680d692
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToChildren.js
@@ -0,0 +1,172 @@
+'use strict';
+
+const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
+const handleTimestampOption = require('../schema/handleTimestampOption');
+
+module.exports = applyTimestampsToChildren;
+
+/*!
+ * ignore
+ */
+
+function applyTimestampsToChildren(now, update, schema) {
+ if (update == null) {
+ return;
+ }
+
+ const keys = Object.keys(update);
+ const hasDollarKey = keys.some(key => key.startsWith('$'));
+
+ if (hasDollarKey) {
+ if (update.$push) {
+ for (const key of Object.keys(update.$push)) {
+ const $path = schema.path(key);
+ if (update.$push[key] &&
+ $path &&
+ $path.$isMongooseDocumentArray &&
+ $path.schema.options.timestamps) {
+ const timestamps = $path.schema.options.timestamps;
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ if (update.$push[key].$each) {
+ update.$push[key].$each.forEach(function(subdoc) {
+ if (updatedAt != null) {
+ subdoc[updatedAt] = now;
+ }
+ if (createdAt != null) {
+ subdoc[createdAt] = now;
+ }
+ });
+ } else {
+ if (updatedAt != null) {
+ update.$push[key][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ update.$push[key][createdAt] = now;
+ }
+ }
+ }
+ }
+ }
+ if (update.$set != null) {
+ const keys = Object.keys(update.$set);
+ for (const key of keys) {
+ applyTimestampsToUpdateKey(schema, key, update.$set, now);
+ }
+ }
+ }
+
+ const updateKeys = Object.keys(update).filter(key => !key.startsWith('$'));
+ for (const key of updateKeys) {
+ applyTimestampsToUpdateKey(schema, key, update, now);
+ }
+}
+
+function applyTimestampsToDocumentArray(arr, schematype, now) {
+ const timestamps = schematype.schema.options.timestamps;
+
+ if (!timestamps) {
+ return;
+ }
+
+ const len = arr.length;
+
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ for (let i = 0; i < len; ++i) {
+ if (updatedAt != null) {
+ arr[i][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ arr[i][createdAt] = now;
+ }
+
+ applyTimestampsToChildren(now, arr[i], schematype.schema);
+ }
+}
+
+function applyTimestampsToSingleNested(subdoc, schematype, now) {
+ const timestamps = schematype.schema.options.timestamps;
+ if (!timestamps) {
+ return;
+ }
+
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+ if (updatedAt != null) {
+ subdoc[updatedAt] = now;
+ }
+ if (createdAt != null) {
+ subdoc[createdAt] = now;
+ }
+
+ applyTimestampsToChildren(now, subdoc, schematype.schema);
+}
+
+function applyTimestampsToUpdateKey(schema, key, update, now) {
+ // Replace positional operator `$` and array filters `$[]` and `$[.*]`
+ const keyToSearch = cleanPositionalOperators(key);
+ const path = schema.path(keyToSearch);
+ if (!path) {
+ return;
+ }
+
+ const parentSchemaTypes = [];
+ const pieces = keyToSearch.split('.');
+ for (let i = pieces.length - 1; i > 0; --i) {
+ const s = schema.path(pieces.slice(0, i).join('.'));
+ if (s != null &&
+ (s.$isMongooseDocumentArray || s.$isSingleNested)) {
+ parentSchemaTypes.push({ parentPath: key.split('.').slice(0, i).join('.'), parentSchemaType: s });
+ }
+ }
+
+ if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) {
+ applyTimestampsToDocumentArray(update[key], path, now);
+ } else if (update[key] && path.$isSingleNested) {
+ applyTimestampsToSingleNested(update[key], path, now);
+ } else if (parentSchemaTypes.length > 0) {
+ for (const item of parentSchemaTypes) {
+ const parentPath = item.parentPath;
+ const parentSchemaType = item.parentSchemaType;
+ const timestamps = parentSchemaType.schema.options.timestamps;
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+
+ if (!timestamps || updatedAt == null) {
+ continue;
+ }
+
+ if (parentSchemaType.$isSingleNested) {
+ // Single nested is easy
+ update[parentPath + '.' + updatedAt] = now;
+ } else if (parentSchemaType.$isMongooseDocumentArray) {
+ let childPath = key.substr(parentPath.length + 1);
+
+ if (/^\d+$/.test(childPath)) {
+ update[parentPath + '.' + childPath][updatedAt] = now;
+ continue;
+ }
+
+ const firstDot = childPath.indexOf('.');
+ childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath;
+
+ update[parentPath + '.' + childPath + '.' + updatedAt] = now;
+ }
+ }
+ } else if (path.schema != null && path.schema != schema && update[key]) {
+ const timestamps = path.schema.options.timestamps;
+ const createdAt = handleTimestampOption(timestamps, 'createdAt');
+ const updatedAt = handleTimestampOption(timestamps, 'updatedAt');
+
+ if (!timestamps) {
+ return;
+ }
+
+ if (updatedAt != null) {
+ update[key][updatedAt] = now;
+ }
+ if (createdAt != null) {
+ update[key][createdAt] = now;
+ }
+ }
+} \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
new file mode 100644
index 0000000..27d6e32
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/applyTimestampsToUpdate.js
@@ -0,0 +1,119 @@
+'use strict';
+
+/*!
+ * ignore
+ */
+
+const get = require('../get');
+
+module.exports = applyTimestampsToUpdate;
+
+/*!
+ * ignore
+ */
+
+function applyTimestampsToUpdate(now, createdAt, updatedAt, currentUpdate, options) {
+ const updates = currentUpdate;
+ let _updates = updates;
+ const overwrite = get(options, 'overwrite', false);
+ const timestamps = get(options, 'timestamps', true);
+
+ // Support skipping timestamps at the query level, see gh-6980
+ if (!timestamps || updates == null) {
+ return currentUpdate;
+ }
+
+ const skipCreatedAt = timestamps != null && timestamps.createdAt === false;
+ const skipUpdatedAt = timestamps != null && timestamps.updatedAt === false;
+
+ if (overwrite) {
+ if (currentUpdate && currentUpdate.$set) {
+ currentUpdate = currentUpdate.$set;
+ updates.$set = {};
+ _updates = updates.$set;
+ }
+ if (!skipUpdatedAt && updatedAt && !currentUpdate[updatedAt]) {
+ _updates[updatedAt] = now;
+ }
+ if (!skipCreatedAt && createdAt && !currentUpdate[createdAt]) {
+ _updates[createdAt] = now;
+ }
+ return updates;
+ }
+ currentUpdate = currentUpdate || {};
+
+ if (Array.isArray(updates)) {
+ // Update with aggregation pipeline
+ updates.push({ $set: { updatedAt: now } });
+
+ return updates;
+ }
+
+ updates.$set = updates.$set || {};
+ if (!skipUpdatedAt && updatedAt &&
+ (!currentUpdate.$currentDate || !currentUpdate.$currentDate[updatedAt])) {
+ let timestampSet = false;
+ if (updatedAt.indexOf('.') !== -1) {
+ const pieces = updatedAt.split('.');
+ for (let i = 1; i < pieces.length; ++i) {
+ const remnant = pieces.slice(-i).join('.');
+ const start = pieces.slice(0, -i).join('.');
+ if (currentUpdate[start] != null) {
+ currentUpdate[start][remnant] = now;
+ timestampSet = true;
+ break;
+ } else if (currentUpdate.$set && currentUpdate.$set[start]) {
+ currentUpdate.$set[start][remnant] = now;
+ timestampSet = true;
+ break;
+ }
+ }
+ }
+
+ if (!timestampSet) {
+ updates.$set[updatedAt] = now;
+ }
+
+ if (updates.hasOwnProperty(updatedAt)) {
+ delete updates[updatedAt];
+ }
+ }
+
+ if (!skipCreatedAt && createdAt) {
+ if (currentUpdate[createdAt]) {
+ delete currentUpdate[createdAt];
+ }
+ if (currentUpdate.$set && currentUpdate.$set[createdAt]) {
+ delete currentUpdate.$set[createdAt];
+ }
+
+ let timestampSet = false;
+ if (createdAt.indexOf('.') !== -1) {
+ const pieces = createdAt.split('.');
+ for (let i = 1; i < pieces.length; ++i) {
+ const remnant = pieces.slice(-i).join('.');
+ const start = pieces.slice(0, -i).join('.');
+ if (currentUpdate[start] != null) {
+ currentUpdate[start][remnant] = now;
+ timestampSet = true;
+ break;
+ } else if (currentUpdate.$set && currentUpdate.$set[start]) {
+ currentUpdate.$set[start][remnant] = now;
+ timestampSet = true;
+ break;
+ }
+ }
+ }
+
+ if (!timestampSet) {
+ updates.$setOnInsert = updates.$setOnInsert || {};
+ updates.$setOnInsert[createdAt] = now;
+ }
+ }
+
+ if (Object.keys(updates.$set).length === 0) {
+ delete updates.$set;
+ }
+
+ return updates;
+}
diff --git a/node_modules/mongoose/lib/helpers/update/castArrayFilters.js b/node_modules/mongoose/lib/helpers/update/castArrayFilters.js
new file mode 100644
index 0000000..57018d9
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/castArrayFilters.js
@@ -0,0 +1,78 @@
+'use strict';
+
+const castFilterPath = require('../query/castFilterPath');
+const cleanPositionalOperators = require('../schema/cleanPositionalOperators');
+const getPath = require('../schema/getPath');
+const modifiedPaths = require('./modifiedPaths');
+
+module.exports = function castArrayFilters(query) {
+ const arrayFilters = query.options.arrayFilters;
+ if (!Array.isArray(arrayFilters)) {
+ return;
+ }
+
+ const update = query.getUpdate();
+ const schema = query.schema;
+ const strictQuery = schema.options.strictQuery;
+
+ const updatedPaths = modifiedPaths(update);
+
+ const updatedPathsByFilter = Object.keys(updatedPaths).reduce((cur, path) => {
+ const matches = path.match(/\$\[[^\]]+\]/g);
+ if (matches == null) {
+ return cur;
+ }
+ for (const match of matches) {
+ const firstMatch = path.indexOf(match);
+ if (firstMatch !== path.lastIndexOf(match)) {
+ throw new Error(`Path '${path}' contains the same array filter multiple times`);
+ }
+ cur[match.substring(2, match.length - 1)] = path.
+ substr(0, firstMatch - 1).
+ replace(/\$\[[^\]]+\]/g, '0');
+ }
+ return cur;
+ }, {});
+
+ for (const filter of arrayFilters) {
+ if (filter == null) {
+ throw new Error(`Got null array filter in ${arrayFilters}`);
+ }
+ for (const key in filter) {
+
+ if (filter[key] == null) {
+ continue;
+ }
+
+ const dot = key.indexOf('.');
+ let filterPath = dot === -1 ?
+ updatedPathsByFilter[key] + '.0' :
+ updatedPathsByFilter[key.substr(0, dot)] + '.0' + key.substr(dot);
+
+ if (filterPath == null) {
+ throw new Error(`Filter path not found for ${key}`);
+ }
+
+ // If there are multiple array filters in the path being updated, make sure
+ // to replace them so we can get the schema path.
+ filterPath = cleanPositionalOperators(filterPath);
+
+ const schematype = getPath(schema, filterPath);
+ if (schematype == null) {
+ if (!strictQuery) {
+ return;
+ }
+ // For now, treat `strictQuery = true` and `strictQuery = 'throw'` as
+ // equivalent for casting array filters. `strictQuery = true` doesn't
+ // quite work in this context because we never want to silently strip out
+ // array filters, even if the path isn't in the schema.
+ throw new Error(`Could not find path "${filterPath}" in schema`);
+ }
+ if (typeof filter[key] === 'object') {
+ filter[key] = castFilterPath(query, schematype, filter[key]);
+ } else {
+ filter[key] = schematype.castForQuery(filter[key]);
+ }
+ }
+ }
+}; \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/update/modifiedPaths.js b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js
new file mode 100644
index 0000000..9cb567d
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/modifiedPaths.js
@@ -0,0 +1,33 @@
+'use strict';
+
+const _modifiedPaths = require('../common').modifiedPaths;
+
+/**
+ * Given an update document with potential update operators (`$set`, etc.)
+ * returns an object whose keys are the directly modified paths.
+ *
+ * If there are any top-level keys that don't start with `$`, we assume those
+ * will get wrapped in a `$set`. The Mongoose Query is responsible for wrapping
+ * top-level keys in `$set`.
+ *
+ * @param {Object} update
+ * @return {Object} modified
+ */
+
+module.exports = function modifiedPaths(update) {
+ const keys = Object.keys(update);
+ const res = {};
+
+ const withoutDollarKeys = {};
+ for (const key of keys) {
+ if (key.startsWith('$')) {
+ _modifiedPaths(update[key], '', res);
+ continue;
+ }
+ withoutDollarKeys[key] = update[key];
+ }
+
+ _modifiedPaths(withoutDollarKeys, '', res);
+
+ return res;
+};
diff --git a/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js b/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js
new file mode 100644
index 0000000..8541c5b
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/moveImmutableProperties.js
@@ -0,0 +1,53 @@
+'use strict';
+
+const get = require('../get');
+
+/**
+ * Given an update, move all $set on immutable properties to $setOnInsert.
+ * This should only be called for upserts, because $setOnInsert bypasses the
+ * strictness check for immutable properties.
+ */
+
+module.exports = function moveImmutableProperties(schema, update, ctx) {
+ if (update == null) {
+ return;
+ }
+
+ const keys = Object.keys(update);
+ for (const key of keys) {
+ const isDollarKey = key.startsWith('$');
+
+ if (key === '$set') {
+ const updatedPaths = Object.keys(update[key]);
+ for (const path of updatedPaths) {
+ _walkUpdatePath(schema, update[key], path, update, ctx);
+ }
+ } else if (!isDollarKey) {
+ _walkUpdatePath(schema, update, key, update, ctx);
+ }
+
+ }
+};
+
+function _walkUpdatePath(schema, op, path, update, ctx) {
+ const schematype = schema.path(path);
+ if (schematype == null) {
+ return;
+ }
+
+ let immutable = get(schematype, 'options.immutable', null);
+ if (immutable == null) {
+ return;
+ }
+ if (typeof immutable === 'function') {
+ immutable = immutable.call(ctx, ctx);
+ }
+
+ if (!immutable) {
+ return;
+ }
+
+ update.$setOnInsert = update.$setOnInsert || {};
+ update.$setOnInsert[path] = op[path];
+ delete op[path];
+} \ No newline at end of file
diff --git a/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js b/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js
new file mode 100644
index 0000000..d460628
--- /dev/null
+++ b/node_modules/mongoose/lib/helpers/update/removeUnusedArrayFilters.js
@@ -0,0 +1,18 @@
+'use strict';
+
+/**
+ * MongoDB throws an error if there's unused array filters. That is, if `options.arrayFilters` defines
+ * a filter, but none of the `update` keys use it. This should be enough to filter out all unused array
+ * filters.
+ */
+
+module.exports = function removeUnusedArrayFilters(update, arrayFilters) {
+ const updateKeys = Object.keys(update).map(key => Object.keys(update[key])).reduce((cur, arr) => cur.concat(arr), []);
+ return arrayFilters.filter(obj => {
+ const firstKey = Object.keys(obj)[0];
+ const firstDot = firstKey.indexOf('.');
+ const arrayFilterKey = firstDot === -1 ? firstKey : firstKey.slice(0, firstDot);
+
+ return updateKeys.find(key => key.includes('$[' + arrayFilterKey + ']')) != null;
+ });
+}; \ No newline at end of file