From 01b182e7d68b8a9907f5926178afe95a4c2e1d79 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 1 Aug 2019 12:17:47 -0400 Subject: [PATCH] refactor: handle arrays underneath mixed paths for #6405 --- lib/document.js | 11 ++++++- lib/helpers/updateValidators.js | 2 +- lib/schema.js | 49 ++++++++++++++++++++++++------- test/document.test.js | 2 +- test/es-next/virtuals.test.es6.js | 4 +++ 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/lib/document.js b/lib/document.js index 5fee1744249..327627500fe 100644 --- a/lib/document.js +++ b/lib/document.js @@ -1346,7 +1346,16 @@ Document.prototype.get = function(path, type, options) { adhoc = this.schema.interpretAsType(path, type, this.schema.options); } - const schema = this.$__path(path) || this.schema.virtualpath(path); + let schema = this.$__path(path); + if (schema == null) { + schema = this.schema.virtualpath(path); + } + if (schema instanceof MixedSchema) { + const virtual = this.schema.virtualpath(path); + if (virtual != null) { + schema = virtual; + } + } const pieces = path.split('.'); let obj = this._doc; diff --git a/lib/helpers/updateValidators.js b/lib/helpers/updateValidators.js index a8618961432..4eeecbb2c5e 100644 --- a/lib/helpers/updateValidators.js +++ b/lib/helpers/updateValidators.js @@ -100,7 +100,7 @@ module.exports = function(query, schema, castedDoc, options) { // gh-4305: `_getSchema()` will report all sub-fields of a 'Mixed' path // as 'Mixed', so avoid double validating them. - if (schemaPath instanceof Mixed && schemaPath.$fullPath !== updates[i]) { + if (schemaPath instanceof Mixed && schemaPath.path !== updates[i]) { return; } diff --git a/lib/schema.js b/lib/schema.js index b036c1d8165..ec310157dcb 100644 --- a/lib/schema.js +++ b/lib/schema.js @@ -543,16 +543,11 @@ warnings.increment = '`increment` should not be used as a schema path name ' + Schema.prototype.path = function(path, obj) { // Convert to '.$' to check subpaths re: gh-6405 - const cleanPath = path.replace(/\.\d+\./g, '.$.').replace(/\.\d+$/, '.$'); + const cleanPath = _pathToPositionalSyntax(path); if (obj === undefined) { - if (this.paths.hasOwnProperty(path)) { - return this.paths[path]; - } - if (this.subpaths.hasOwnProperty(cleanPath)) { - return this.subpaths[cleanPath]; - } - if (this.singleNestedPaths.hasOwnProperty(cleanPath)) { - return this.singleNestedPaths[cleanPath]; + let schematype = _getPath(this, path, cleanPath); + if (schematype != null) { + return schematype; } // Look for maps @@ -561,6 +556,12 @@ Schema.prototype.path = function(path, obj) { return mapPath; } + // Look if a parent of this path is mixed + schematype = this.hasMixedParent(cleanPath); + if (schematype != null) { + return schematype; + } + // subpaths? return /\.\d+\.?.*$/.test(path) ? getPositionalPath(this, path) @@ -705,6 +706,32 @@ function gatherChildSchemas(schema) { return childSchemas; } +/*! + * ignore + */ + +function _getPath(schema, path, cleanPath) { + if (schema.paths.hasOwnProperty(path)) { + return schema.paths[path]; + } + if (schema.subpaths.hasOwnProperty(cleanPath)) { + return schema.subpaths[cleanPath]; + } + if (schema.singleNestedPaths.hasOwnProperty(cleanPath)) { + return schema.singleNestedPaths[cleanPath]; + } + + return null; +} + +/*! + * ignore + */ + +function _pathToPositionalSyntax(path) { + return path.replace(/\.\d+\./g, '.$.').replace(/\.\d+$/, '.$'); +} + /*! * ignore */ @@ -1014,11 +1041,11 @@ Schema.prototype.hasMixedParent = function(path) { path = i > 0 ? path + '.' + subpaths[i] : subpaths[i]; if (path in this.paths && this.paths[path] instanceof MongooseTypes.Mixed) { - return true; + return this.paths[path]; } } - return false; + return null; }; /** diff --git a/test/document.test.js b/test/document.test.js index fae94375437..ce7d79df7f1 100644 --- a/test/document.test.js +++ b/test/document.test.js @@ -7860,7 +7860,7 @@ describe('document', function() { }); }); - it('allows saving an unchanged document if populated path is null (gh-8018)', function() { + it('allows saving an unchanged document if required populated path is null (gh-8018)', function() { const schema = Schema({ test: String }); const schema2 = Schema({ keyToPopulate: { diff --git a/test/es-next/virtuals.test.es6.js b/test/es-next/virtuals.test.es6.js index 959f78496b4..03bc01b72c2 100644 --- a/test/es-next/virtuals.test.es6.js +++ b/test/es-next/virtuals.test.es6.js @@ -158,6 +158,10 @@ describe('Virtuals', function() { const User = mongoose.model('User', userSchema); const BlogPost = mongoose.model('BlogPost', blogPostSchema); + // acquit:ignore:start + await BlogPost.deleteMany({}); + await User.deleteMany({}); + // acquit:ignore:end await BlogPost.create({ title: 'Introduction to Mongoose', authorId: 1 }); await User.create({ _id: 1, email: 'test@gmail.com' });