Skip to content

Commit

Permalink
Merge branch 'master' into 5.6
Browse files Browse the repository at this point in the history
  • Loading branch information
vkarpov15 committed Jun 13, 2019
2 parents cc026ee + 5bbc4c4 commit 117501e
Show file tree
Hide file tree
Showing 28 changed files with 171 additions and 39 deletions.
9 changes: 9 additions & 0 deletions History.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,12 @@
5.5.15 / 2019-06-12
===================
* fix(connection): reject initial connect promise even if there is an on('error') listener #7850
* fix(map): make `of` automatically convert POJOs to schemas unless typeKey is set #7859
* fix(update): use discriminator schema to cast update if discriminator key specified in filter #7843
* fix(array): copy atomics from source array #7891 #7889 [jyrkive](https://github.com/jyrkive)
* fix(schema): return this when Schema.prototype.add is called with Schema #7887 [Mickael-van-der-Beek](https://github.com/Mickael-van-der-Beek)
* fix(document): add `numAffected` and `result` to DocumentNotFoundError for better debugging #7892 #7844

5.5.14 / 2019-06-08
===================
* fix(query): correct this scope of setters in update query #7876 [Fonger](https://github.com/Fonger)
Expand Down
6 changes: 3 additions & 3 deletions lib/aggregate.js
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ Aggregate.prototype.unwind = function() {
res.push({ $unwind: arg });
} else if (typeof arg === 'string') {
res.push({
$unwind: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
$unwind: (arg && arg.startsWith('$')) ? arg : '$' + arg
});
} else {
throw new Error('Invalid arg "' + arg + '" to unwind(), ' +
Expand Down Expand Up @@ -460,7 +460,7 @@ Aggregate.prototype.sortByCount = function(arg) {
return this.append({ $sortByCount: arg });
} else if (typeof arg === 'string') {
return this.append({
$sortByCount: (arg && arg.charAt(0) === '$') ? arg : '$' + arg
$sortByCount: (arg && arg.startsWith('$')) ? arg : '$' + arg
});
} else {
throw new TypeError('Invalid arg "' + arg + '" to sortByCount(), ' +
Expand Down Expand Up @@ -511,7 +511,7 @@ Aggregate.prototype.graphLookup = function(options) {
const startWith = cloneOptions.startWith;

if (startWith && typeof startWith === 'string') {
cloneOptions.startWith = cloneOptions.startWith.charAt(0) === '$' ?
cloneOptions.startWith = cloneOptions.startWith.startsWith('$') ?
cloneOptions.startWith :
'$' + cloneOptions.startWith;
}
Expand Down
4 changes: 2 additions & 2 deletions lib/cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ module.exports = function cast(schema, obj, options, context) {
continue;
} else if (val.constructor.name === 'Object') {
any$conditionals = Object.keys(val).some(function(k) {
return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
return k.startsWith('$') && k !== '$id' && k !== '$ref';
});

if (!any$conditionals) {
Expand All @@ -266,7 +266,7 @@ module.exports = function cast(schema, obj, options, context) {
if ($cond === '$not') {
if (nested && schematype && !schematype.caster) {
_keys = Object.keys(nested);
if (_keys.length && _keys[0].charAt(0) === '$') {
if (_keys.length && _keys[0].startsWith('$')) {
for (const key in nested) {
nested[key] = schematype.castForQueryWrapper({
$conditional: key,
Expand Down
1 change: 0 additions & 1 deletion lib/connection.js
Original file line number Diff line number Diff line change
Expand Up @@ -660,7 +660,6 @@ Connection.prototype.openUri = function(uri, options, callback) {
catch(err => {
if (this.listeners('error').length > 0) {
process.nextTick(() => this.emit('error', err));
return;
}
throw err;
});
Expand Down
8 changes: 4 additions & 4 deletions lib/document.js
Original file line number Diff line number Diff line change
Expand Up @@ -1252,7 +1252,7 @@ Document.prototype.$__set = function(pathToMark, path, constructing, parts, sche
// Small hack for gh-1638: if we're overwriting the entire array, ignore
// paths that were modified before the array overwrite
this.$__.activePaths.forEach(function(modifiedPath) {
if (modifiedPath.indexOf(path + '.') === 0) {
if (modifiedPath.startsWith(path + '.')) {
_this.$__.activePaths.ignore(modifiedPath);
}
});
Expand Down Expand Up @@ -1656,7 +1656,7 @@ Document.prototype.isModified = function(paths, modifiedPaths) {
});
return isModifiedChild || paths.some(function(path) {
return directModifiedPaths.some(function(mod) {
return mod === path || path.indexOf(mod + '.') === 0;
return mod === path || path.startsWith(mod + '.');
});
});
}
Expand Down Expand Up @@ -1804,11 +1804,11 @@ Document.prototype.isSelected = function isSelected(path) {
continue;
}

if (cur.indexOf(pathDot) === 0) {
if (cur.startsWith(pathDot)) {
return inclusive || cur !== pathDot;
}

if (pathDot.indexOf(cur + '.') === 0) {
if (pathDot.startsWith(cur + '.')) {
return inclusive;
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/error/cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ const util = require('util');
function CastError(type, value, path, reason) {
let stringValue = util.inspect(value);
stringValue = stringValue.replace(/^'/, '"').replace(/'$/, '"');
if (stringValue.charAt(0) !== '"') {
if (!stringValue.startsWith('"')) {
stringValue = '"' + stringValue + '"';
}
MongooseError.call(this, 'Cast to ' + type + ' failed for value ' +
Expand Down
4 changes: 3 additions & 1 deletion lib/error/notFound.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const util = require('util');
* @inherits MongooseError
*/

function DocumentNotFoundError(filter, model) {
function DocumentNotFoundError(filter, model, numAffected, result) {
let msg;
const messages = MongooseError.messages;
if (messages.DocumentNotFoundError != null) {
Expand All @@ -28,6 +28,8 @@ function DocumentNotFoundError(filter, model) {
MongooseError.call(this, msg);

this.name = 'DocumentNotFoundError';
this.result = result;
this.numAffected = numAffected;
if (Error.captureStackTrace) {
Error.captureStackTrace(this);
} else {
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/document/cleanModifiedSubpaths.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ module.exports = function cleanModifiedSubpaths(doc, path, options) {
continue;
}
}
if (modifiedPath.indexOf(path + '.') === 0) {
if (modifiedPath.startsWith(path + '.')) {
delete doc.$__.activePaths.states.modify[modifiedPath];
++deleted;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/projection/isInclusive.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ module.exports = function isInclusive(projection) {
for (let i = 0; i < numProps; ++i) {
const prop = props[i];
// Plus paths can't define the projection (see gh-7050)
if (prop.charAt(0) === '+') {
if (prop.startsWith('+')) {
continue;
}
// If field is truthy (1, true, etc.) and not an object, then this
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/query/castFilterPath.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
module.exports = function castFilterPath(query, schematype, val) {
const ctx = query;
const any$conditionals = Object.keys(val).some(function(k) {
return k.charAt(0) === '$' && k !== '$id' && k !== '$ref';
return k.startsWith('$') && k !== '$id' && k !== '$ref';
});

if (!any$conditionals) {
Expand All @@ -24,7 +24,7 @@ module.exports = function castFilterPath(query, schematype, val) {
if ($cond === '$not') {
if (nested && schematype && !schematype.caster) {
const _keys = Object.keys(nested);
if (_keys.length && _keys[0].charAt(0) === '$') {
if (_keys.length && _keys[0].startsWith('$')) {
for (const key in nested) {
nested[key] = schematype.castForQueryWrapper({
$conditional: key,
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/query/castUpdate.js
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ module.exports = function castUpdate(schema, obj, options, context, filter) {
while (i--) {
const op = ops[i];
val = ret[op];
hasDollarKey = hasDollarKey || op.charAt(0) === '$';
hasDollarKey = hasDollarKey || op.startsWith('$');

if (val &&
typeof val === 'object' &&
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/query/hasDollarKeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ module.exports = function(obj) {
const keys = Object.keys(obj);
const len = keys.length;
for (let i = 0; i < len; ++i) {
if (keys[i].charAt(0) === '$') {
if (keys[i].startsWith('$')) {
return true;
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/setDefaultsOnInsert.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ module.exports = function(filter, schema, castedDoc, options) {
}

for (let i = 0; i < numKeys; ++i) {
if (keys[i].charAt(0) === '$') {
if (keys[i].startsWith('$')) {
modifiedPaths(castedDoc[keys[i]], '', modified);
hasDollarUpdate = true;
}
Expand All @@ -49,7 +49,7 @@ module.exports = function(filter, schema, castedDoc, options) {
const numConditionKeys = conditionKeys.length;
let hasDollarKey = false;
for (let j = 0; j < numConditionKeys; ++j) {
if (conditionKeys[j].charAt(0) === '$') {
if (conditionKeys[j].startsWith('$')) {
hasDollarKey = true;
break;
}
Expand Down
2 changes: 1 addition & 1 deletion lib/helpers/update/applyTimestampsToChildren.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function applyTimestampsToChildren(now, update, schema) {
let timestamps;
let path;

const hasDollarKey = keys.length && keys[0].charAt(0) === '$';
const hasDollarKey = keys.length && keys[0].startsWith('$');

if (hasDollarKey) {
if (update.$push) {
Expand Down
4 changes: 2 additions & 2 deletions lib/helpers/updateValidators.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ module.exports = function(query, schema, castedDoc, options) {
let i;

for (i = 0; i < numKeys; ++i) {
if (keys[i].charAt(0) === '$') {
if (keys[i].startsWith('$')) {
hasDollarUpdate = true;
if (keys[i] === '$push' || keys[i] === '$addToSet') {
_keys = Object.keys(castedDoc[keys[i]]);
Expand All @@ -63,7 +63,7 @@ module.exports = function(query, schema, castedDoc, options) {
key = keys[i];
// With `$pull` we might flatten `$in`. Skip stuff nested under `$in`
// for the rest of the logic, it will get handled later.
if (updatedPath.indexOf('$') !== -1) {
if (updatedPath.includes('$')) {
continue;
}
if (key === '$set' || key === '$setOnInsert' ||
Expand Down
2 changes: 1 addition & 1 deletion lib/model.js
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ Model.prototype.$__save = function(options, callback) {

if (result != null && numAffected <= 0) {
error = new DocumentNotFoundError(result.$where,
this.constructor.modelName);
this.constructor.modelName, numAffected, result);
return this.schema.s.hooks.execPost('save:error', this, [this], { error: error }, function(error) {
callback(error);
});
Expand Down
15 changes: 14 additions & 1 deletion lib/query.js
Original file line number Diff line number Diff line change
Expand Up @@ -4358,7 +4358,20 @@ Query.prototype._castUpdate = function _castUpdate(obj, overwrite) {
useNestedStrict = this.options.useNestedStrict;
}

return castUpdate(this.schema, obj, {
let schema = this.schema;
const filter = this._conditions;
if (schema != null &&
utils.hasUserDefinedProperty(filter, schema.options.discriminatorKey) &&
typeof filter[schema.options.discriminatorKey] !== 'object' &&
schema.discriminators != null) {
const discriminatorValue = filter[schema.options.discriminatorKey];
const byValue = helpers.getDiscriminatorByValue(this.model, discriminatorValue);
schema = schema.discriminators[discriminatorValue] ||
(byValue && byValue.schema) ||
schema;
}

return castUpdate(schema, obj, {
overwrite: overwrite,
strict: strict,
omitUndefined,
Expand Down
2 changes: 1 addition & 1 deletion lib/queryhelpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,7 @@ exports.applyPaths = function applyPaths(fields, schema) {
}
// Any leftover plus paths must in the schema, so delete them (gh-7017)
for (const key of Object.keys(fields || {})) {
if (key.charAt(0) === '+') {
if (key.startsWith('+')) {
delete fields[key];
}
}
Expand Down
18 changes: 12 additions & 6 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -397,7 +397,7 @@ Schema.prototype.defaultOptions = function(options) {
Schema.prototype.add = function add(obj, prefix) {
if (obj instanceof Schema) {
merge(this, obj);
return;
return this;
}

// Special case: setting top-level `_id` to false should convert to disabling
Expand Down Expand Up @@ -605,9 +605,16 @@ Schema.prototype.path = function(path, obj) {
// The '$' is to imply this path should never be stored in MongoDB so we
// can easily build a regexp out of this path, and '*' to imply "any key."
const mapPath = path + '.$*';
this.paths[path + '.$*'] = this.interpretAsType(mapPath,
obj.of || { type: {} }, this.options);
schemaType.$__schemaType = this.paths[path + '.$*'];
let _mapType = { type: {} };
if (utils.hasUserDefinedProperty(obj, 'of')) {
const isInlineSchema = utils.isPOJO(obj.of) &&
Object.keys(obj.of).length > 0 &&
!utils.hasUserDefinedProperty(obj.of, this.options.typeKey);
_mapType = isInlineSchema ? new Schema(obj.of) : obj.of;
}
this.paths[mapPath] = this.interpretAsType(mapPath,
_mapType, this.options);
schemaType.$__schemaType = this.paths[mapPath];
}

if (schemaType.$isSingleNested) {
Expand Down Expand Up @@ -1920,8 +1927,7 @@ Schema.prototype._getPathType = function(path) {
*/

function isArrayFilter(piece) {
return piece.indexOf('$[') === 0 &&
piece.lastIndexOf(']') === piece.length - 1;
return piece.startsWith('$[') && piece.endsWith(']');
}

/*!
Expand Down
2 changes: 1 addition & 1 deletion lib/schema/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ function cast$elemMatch(val) {
for (let i = 0; i < numKeys; ++i) {
const key = keys[i];
const value = val[key];
if (key.indexOf('$') === 0 && value) {
if (key.startsWith('$') && value) {
val[key] = this.castForQuery(key, value);
}
}
Expand Down
4 changes: 2 additions & 2 deletions lib/schema/documentarray.js
Original file line number Diff line number Diff line change
Expand Up @@ -459,12 +459,12 @@ function scopePaths(array, fields, init) {

while (i--) {
key = keys[i];
if (key.indexOf(path) === 0) {
if (key.startsWith(path)) {
sub = key.substring(path.length);
if (sub === '$') {
continue;
}
if (sub.indexOf('$.') === 0) {
if (sub.startsWith('$.')) {
sub = sub.substr(2);
}
hasKeys || (hasKeys = true);
Expand Down
4 changes: 3 additions & 1 deletion lib/types/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,19 @@ function MongooseArray(values, path, doc) {
// TODO: replace this with `new CoreMongooseArray().concat()` when we remove
// support for node 4.x and 5.x, see https://i.imgur.com/UAAHk4S.png
const arr = new CoreMongooseArray();
arr[arrayAtomicsSymbol] = {};

if (Array.isArray(values)) {
const len = values.length;
for (let i = 0; i < len; ++i) {
_basePush.call(arr, values[i]);
}

arr[arrayAtomicsSymbol] = values[arrayAtomicsSymbol] || {};
}

arr[arrayPathSymbol] = path;
arr.validators = [];
arr[arrayAtomicsSymbol] = {};
arr[arraySchemaSymbol] = void 0;

// Because doc comes from the context of another function, doc === global
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "mongoose",
"description": "Mongoose MongoDB ODM",
"version": "5.5.14",
"version": "5.5.15",
"author": "Guillermo Rauch <guillermo@learnboost.com>",
"keywords": [
"mongodb",
Expand Down
15 changes: 15 additions & 0 deletions test/connection.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,21 @@ describe('connections:', function() {
});
});

it('promise is rejected even if there is an error event listener (gh-7850)', function(done) {
const db = mongoose.createConnection();

let called = 0;
db.on('error', () => ++called);

db.openUri('fail connection').catch(function(error) {
assert.ok(error);
setTimeout(() => {
assert.equal(called, 1);
done();
}, 0);
});
});

it('readyState is disconnected if initial connection fails (gh-6244)', function() {
const db = mongoose.createConnection();

Expand Down

0 comments on commit 117501e

Please sign in to comment.