Refactoring day1

This commit is contained in:
2020-08-20 20:27:14 +02:00
parent 6aceefeb2f
commit b907489a75
481 changed files with 5321 additions and 5616 deletions

View File

@@ -4,14 +4,13 @@ module.exports = arrayDepth;
function arrayDepth(arr) {
if (!Array.isArray(arr)) {
return { min: 0, max: 0, containsNonArrayItem: true };
return { min: 0, max: 0 };
}
if (arr.length === 0) {
return { min: 1, max: 1, containsNonArrayItem: false };
return { min: 1, max: 1 };
}
const res = arrayDepth(arr[0]);
for (let i = 1; i < arr.length; ++i) {
const _res = arrayDepth(arr[i]);
if (_res.min < res.min) {
@@ -20,7 +19,6 @@ function arrayDepth(arr) {
if (_res.max > res.max) {
res.max = _res.max;
}
res.containsNonArrayItem = res.containsNonArrayItem || _res.containsNonArrayItem;
}
res.min = res.min + 1;

View File

@@ -124,13 +124,6 @@ function defineKey(prop, subprops, prototype, prefix, keys, options) {
}
});
Object.defineProperty(nested, '$__parent', {
enumerable: false,
configurable: true,
writable: false,
value: this
});
compile(subprops, nested, path, options);
this.$__.getters[path] = nested;
}
@@ -138,10 +131,8 @@ function defineKey(prop, subprops, prototype, prefix, keys, options) {
return this.$__.getters[path];
},
set: function(v) {
if (v != null && v.$__isNested) {
// Convert top-level to POJO, but leave subdocs hydrated so `$set`
// can handle them. See gh-9293.
v = v.$__parent.get(path);
if (v instanceof Document) {
v = v.toObject({ transform: false });
}
const doc = this.$__[scopeSymbol] || this;
doc.$set(path, v);

View File

@@ -3,52 +3,8 @@
const get = require('../get');
const utils = require('../../utils');
/**
* Given a Mongoose index definition (key + options objects) and a MongoDB server
* index definition, determine if the two indexes are equal.
*
* @param {Object} key the Mongoose index spec
* @param {Object} options the Mongoose index definition's options
* @param {Object} dbIndex the index in MongoDB as returned by `listIndexes()`
* @api private
*/
module.exports = function isIndexEqual(key, options, dbIndex) {
// Special case: text indexes have a special format in the db. For example,
// `{ name: 'text' }` becomes:
// {
// v: 2,
// key: { _fts: 'text', _ftsx: 1 },
// name: 'name_text',
// ns: 'test.tests',
// background: true,
// weights: { name: 1 },
// default_language: 'english',
// language_override: 'language',
// textIndexVersion: 3
// }
if (dbIndex.textIndexVersion != null) {
const weights = dbIndex.weights;
if (Object.keys(weights).length !== Object.keys(key).length) {
return false;
}
for (const prop of Object.keys(weights)) {
if (!(prop in key)) {
return false;
}
const weight = weights[prop];
if (weight !== get(options, 'weights.' + prop) && !(weight === 1 && get(options, 'weights.' + prop) == null)) {
return false;
}
}
if (options['default_language'] !== dbIndex['default_language']) {
return dbIndex['default_language'] === 'english' && options['default_language'] == null;
}
return true;
}
// If these options are different, need to rebuild the index
const optionKeys = [
'unique',
'partialFilterExpression',
@@ -61,9 +17,6 @@ module.exports = function isIndexEqual(key, options, dbIndex) {
continue;
}
if (key === 'collation') {
if (options[key] == null || dbIndex[key] == null) {
return options[key] == null && dbIndex[key] == null;
}
const definedKeys = Object.keys(options.collation);
const schemaCollation = options.collation;
const dbCollation = dbIndex.collation;
@@ -92,4 +45,4 @@ module.exports = function isIndexEqual(key, options, dbIndex) {
}
return true;
};
};

View File

@@ -37,12 +37,8 @@ module.exports = function castBulkWrite(originalModel, op, options) {
} else if (op['updateOne']) {
return (callback) => {
try {
if (!op['updateOne']['filter']) {
throw new Error('Must provide a filter object.');
}
if (!op['updateOne']['update']) {
throw new Error('Must provide an update object.');
}
if (!op['updateOne']['filter']) throw new Error('Must provide a filter object.');
if (!op['updateOne']['update']) throw new Error('Must provide an update object.');
const model = decideModelByObject(originalModel, op['updateOne']['filter']);
const schema = model.schema;
@@ -58,6 +54,7 @@ module.exports = function castBulkWrite(originalModel, op, options) {
applyTimestampsToChildren(now, op['updateOne']['update'], model.schema);
if (op['updateOne'].setDefaultsOnInsert) {
setDefaultsOnInsert(op['updateOne']['filter'], model.schema, op['updateOne']['update'], {
setDefaultsOnInsert: true,
@@ -74,7 +71,8 @@ module.exports = function castBulkWrite(originalModel, op, options) {
strict: strict,
overwrite: false,
upsert: op['updateOne'].upsert
}, model, op['updateOne']['filter']);
});
} catch (error) {
return callback(error, null);
}
@@ -84,12 +82,8 @@ module.exports = function castBulkWrite(originalModel, op, options) {
} else if (op['updateMany']) {
return (callback) => {
try {
if (!op['updateMany']['filter']) {
throw new Error('Must provide a filter object.');
}
if (!op['updateMany']['update']) {
throw new Error('Must provide an update object.');
}
if (!op['updateMany']['filter']) throw new Error('Must provide a filter object.');
if (!op['updateMany']['update']) throw new Error('Must provide an update object.');
const model = decideModelByObject(originalModel, op['updateMany']['filter']);
const schema = model.schema;
@@ -121,7 +115,7 @@ module.exports = function castBulkWrite(originalModel, op, options) {
strict: strict,
overwrite: false,
upsert: op['updateMany'].upsert
}, model, op['updateMany']['filter']);
});
} catch (error) {
return callback(error, null);
@@ -159,7 +153,6 @@ module.exports = function castBulkWrite(originalModel, op, options) {
if (error) {
return callback(error, null);
}
op['replaceOne']['replacement'] = op['replaceOne']['replacement'].toBSON();
callback(null);
});
};

View File

@@ -149,17 +149,11 @@ module.exports = function discriminator(model, name, schema, tiedValue, applyPlu
for (const _key of keys) {
if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
// Special case: compiling a model sets `pluralization = true` by default. Avoid throwing an error
// for that case. See gh-9238
if (_key === 'pluralization' && schema.options[_key] == true && baseSchema.options[_key] == null) {
continue;
}
if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
throw new Error('Can\'t customize discriminator option ' + _key +
' (can only modify ' +
Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
')');
' (can only modify ' +
Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
')');
}
}
}

View File

@@ -18,7 +18,6 @@ module.exports = function assignVals(o) {
justOne: o.justOne
});
populateOptions.$nullIfNotFound = o.isVirtual;
const populatedModel = o.populatedModel;
const originalIds = [].concat(o.rawIds);
@@ -40,31 +39,12 @@ module.exports = function assignVals(o) {
if (val instanceof SkipPopulateValue) {
return val.val;
}
if (o.justOne === true && Array.isArray(val)) {
// Might be an embedded discriminator (re: gh-9244) with multiple models, so make sure to pick the right
// model before assigning.
const ret = [];
for (const doc of val) {
const _docPopulatedModel = leanPopulateMap.get(doc);
if (_docPopulatedModel == null || _docPopulatedModel === populatedModel) {
ret.push(doc);
}
}
// Since we don't want to have to create a new mongoosearray, make sure to
// modify the array in place
while (val.length > ret.length) {
Array.prototype.pop.apply(val, []);
}
for (let i = 0; i < ret.length; ++i) {
val[i] = ret[i];
}
return valueFilter(val[0], options, populateOptions, populatedModel);
return valueFilter(val[0], options, populateOptions);
} else if (o.justOne === false && !Array.isArray(val)) {
return valueFilter([val], options, populateOptions, populatedModel);
return valueFilter([val], options, populateOptions);
}
return valueFilter(val, options, populateOptions, populatedModel);
return valueFilter(val, options, populateOptions);
}
for (let i = 0; i < docs.length; ++i) {
@@ -104,16 +84,6 @@ module.exports = function assignVals(o) {
}, new Map());
}
if (isDoc && Array.isArray(valueToSet)) {
for (const val of valueToSet) {
if (val != null && val.$__ != null) {
val.$__.parent = docs[i];
}
}
} else if (isDoc && valueToSet != null && valueToSet.$__ != null) {
valueToSet.$__.parent = docs[i];
}
if (o.isVirtual && isDoc) {
docs[i].populated(o.path, o.justOne ? originalIds[0] : originalIds, o.allOptions);
// If virtual populate and doc is already init-ed, need to walk through

View File

@@ -40,7 +40,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
for (i = 0; i < len; i++) {
doc = docs[i];
let justOne = null;
schema = getSchemaTypes(modelSchema, doc, options.path);
// Special case: populating a path that's a DocumentArray unless
@@ -77,7 +76,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
isRefPath = isRefPath || res.isRefPath;
normalizedRefPath = normalizeRefPath(normalizedRefPath, doc, options.path) ||
res.refPath;
justOne = res.justOne;
} catch (error) {
return error;
}
@@ -101,7 +99,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
modelNames = res.modelNames;
isRefPath = res.isRefPath;
normalizedRefPath = res.refPath;
justOne = res.justOne;
} catch (error) {
return error;
}
@@ -145,6 +142,7 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
// `justOne = null` means we don't know from the schema whether the end
// result should be an array or a single doc. This can result from
// populating a POJO using `Model.populate()`
let justOne = null;
if ('justOne' in options && options.justOne !== void 0) {
justOne = options.justOne;
} else if (virtual && virtual.options && virtual.options.refPath) {
@@ -341,7 +339,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
let modelNames;
let discriminatorKey;
let isRefPath = false;
let justOne = null;
if (schema && schema.caster) {
schema = schema.caster;
@@ -410,10 +407,6 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
const _virtualRes = getVirtual(modelForCurrentDoc.schema, options.path);
const virtual = _virtualRes == null ? null : _virtualRes.virtual;
if (schemaForCurrentDoc != null) {
justOne = !schemaForCurrentDoc.$isMongooseArray && !schemaForCurrentDoc._arrayPath;
}
let ref;
let refPath;
@@ -447,14 +440,14 @@ module.exports = function getModelsMapForPopulate(model, docs, options) {
}
if (!modelNames) {
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath, justOne: justOne };
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath };
}
if (!Array.isArray(modelNames)) {
modelNames = [modelNames];
}
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath, justOne: justOne };
return { modelNames: modelNames, isRefPath: isRefPath, refPath: normalizedRefPath };
}
};

View File

@@ -105,14 +105,6 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
}
}
if (Object.keys(ret).length === 0 &&
options.upsert &&
Object.keys(filter).length > 0) {
// Trick the driver into allowing empty upserts to work around
// https://github.com/mongodb/node-mongodb-native/pull/2490
return { $setOnInsert: filter };
}
return ret;
};

View File

@@ -33,13 +33,11 @@ function createImmutableSetter(path, immutable) {
if (!_immutable) {
return v;
}
const _value = this.$__getValue(path);
if (this.$__.strictMode === 'throw' && v !== _value) {
if (this.$__.strictMode === 'throw' && v !== this[path]) {
throw new StrictModeError(path, 'Path `' + path + '` is immutable ' +
'and strict mode is set to throw.', true);
}
return _value;
return this[path];
};
}

View File

@@ -14,17 +14,6 @@ const get = require('./get');
*/
module.exports = function(filter, schema, castedDoc, options) {
options = options || {};
const shouldSetDefaultsOnInsert =
options.setDefaultsOnInsert != null ?
options.setDefaultsOnInsert :
schema.base.options.setDefaultsOnInsert;
if (!options.upsert || !shouldSetDefaultsOnInsert) {
return castedDoc;
}
const keys = Object.keys(castedDoc || {});
const updatedKeys = {};
const updatedValues = {};
@@ -33,6 +22,12 @@ module.exports = function(filter, schema, castedDoc, options) {
let hasDollarUpdate = false;
options = options || {};
if (!options.upsert || !options.setDefaultsOnInsert) {
return castedDoc;
}
for (let i = 0; i < numKeys; ++i) {
if (keys[i].startsWith('$')) {
modifiedPaths(castedDoc[keys[i]], '', modified);

View File

@@ -11,6 +11,5 @@ exports.modelSymbol = Symbol('mongoose#Model');
exports.objectIdSymbol = Symbol('mongoose#ObjectId');
exports.populateModelSymbol = Symbol('mongoose.PopulateOptions#Model');
exports.schemaTypeSymbol = Symbol('mongoose#schemaType');
exports.sessionNewDocuments = Symbol('mongoose:ClientSession#newDocuments');
exports.scopeSymbol = Symbol('mongoose#Document#scope');
exports.validatorErrorSymbol = Symbol('mongoose:validatorError');

View File

@@ -15,19 +15,25 @@ function applyTimestampsToChildren(now, update, schema) {
}
const keys = Object.keys(update);
let key;
let createdAt;
let updatedAt;
let timestamps;
let path;
const hasDollarKey = keys.length && keys[0].startsWith('$');
if (hasDollarKey) {
if (update.$push) {
for (const key of Object.keys(update.$push)) {
for (key in 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');
timestamps = $path.schema.options.timestamps;
createdAt = handleTimestampOption(timestamps, 'createdAt');
updatedAt = handleTimestampOption(timestamps, 'updatedAt');
if (update.$push[key].$each) {
update.$push[key].$each.forEach(function(subdoc) {
if (updatedAt != null) {
@@ -50,14 +56,93 @@ function applyTimestampsToChildren(now, update, schema) {
}
if (update.$set != null) {
const keys = Object.keys(update.$set);
for (const key of keys) {
applyTimestampsToUpdateKey(schema, key, update.$set, now);
for (key of keys) {
// Replace positional operator `$` and array filters `$[]` and `$[.*]`
const keyToSearch = cleanPositionalOperators(key);
path = schema.path(keyToSearch);
if (!path) {
continue;
}
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.$set[key]) && path.$isMongooseDocumentArray) {
applyTimestampsToDocumentArray(update.$set[key], path, now);
} else if (update.$set[key] && path.$isSingleNested) {
applyTimestampsToSingleNested(update.$set[key], path, now);
} else if (parentSchemaTypes.length > 0) {
for (const item of parentSchemaTypes) {
const parentPath = item.parentPath;
const parentSchemaType = item.parentSchemaType;
timestamps = parentSchemaType.schema.options.timestamps;
createdAt = handleTimestampOption(timestamps, 'createdAt');
updatedAt = handleTimestampOption(timestamps, 'updatedAt');
if (!timestamps || updatedAt == null) {
continue;
}
if (parentSchemaType.$isSingleNested) {
// Single nested is easy
update.$set[parentPath + '.' + updatedAt] = now;
continue;
}
if (parentSchemaType.$isMongooseDocumentArray) {
let childPath = key.substr(parentPath.length + 1);
if (/^\d+$/.test(childPath)) {
update.$set[parentPath + '.' + childPath][updatedAt] = now;
continue;
}
const firstDot = childPath.indexOf('.');
childPath = firstDot !== -1 ? childPath.substr(0, firstDot) : childPath;
update.$set[parentPath + '.' + childPath + '.' + updatedAt] = now;
}
}
} else if (path.schema != null && path.schema != schema && update.$set[key]) {
timestamps = path.schema.options.timestamps;
createdAt = handleTimestampOption(timestamps, 'createdAt');
updatedAt = handleTimestampOption(timestamps, 'updatedAt');
if (!timestamps) {
continue;
}
if (updatedAt != null) {
update.$set[key][updatedAt] = now;
}
if (createdAt != null) {
update.$set[key][createdAt] = now;
}
}
}
}
} else {
const keys = Object.keys(update).filter(key => !key.startsWith('$'));
for (const key of keys) {
applyTimestampsToUpdateKey(schema, key, update, now);
for (key of keys) {
// Replace positional operator `$` and array filters `$[]` and `$[.*]`
const keyToSearch = cleanPositionalOperators(key);
path = schema.path(keyToSearch);
if (!path) {
continue;
}
if (Array.isArray(update[key]) && path.$isMongooseDocumentArray) {
applyTimestampsToDocumentArray(update[key], path, now);
} else if (update[key] != null && path.$isSingleNested) {
applyTimestampsToSingleNested(update[key], path, now);
}
}
}
}
@@ -102,71 +187,3 @@ function applyTimestampsToSingleNested(subdoc, schematype, 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;
}
}
}