Skip to content
2 changes: 1 addition & 1 deletion lib/cast.js
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ module.exports = function cast(schema, obj, options, context) {
}

if (geo) {
const numbertype = new Types.Number('__QueryCasting__');
const numbertype = new Types.Number('__QueryCasting__', null, null, schema);
let value = val[geo];

if (val.$maxDistance != null) {
Expand Down
10 changes: 10 additions & 0 deletions lib/helpers/discriminator/mergeDiscriminatorSchema.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ const schemaMerge = require('../schema/merge');
const specialProperties = require('../../helpers/specialProperties');
const isBsonType = require('../../helpers/isBsonType');
const ObjectId = require('../../types/objectid');
const SchemaType = require('../../schemaType');
const isObject = require('../../helpers/isObject');
/**
* Merges `from` into `to` without overwriting existing properties.
Expand Down Expand Up @@ -69,6 +70,15 @@ module.exports = function mergeDiscriminatorSchema(to, from, path, seen) {
} else if (isBsonType(from[key], 'ObjectId')) {
to[key] = new ObjectId(from[key]);
continue;
} else if (from[key] instanceof SchemaType) {
if (to[key] == null) {
to[key] = from[key].clone();
}
// For container types with nested schemas, we need to continue to the
// recursive merge below to properly merge the nested schemas
if (!from[key].$isMongooseDocumentArray && !from[key].$isSingleNested) {
continue;
}
}
}
mergeDiscriminatorSchema(to[key], from[key], path ? path + '.' + key : key, seen);
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 @@ -428,7 +428,7 @@ function walkUpdatePath(schema, obj, op, options, context, filter, prefix) {
if (obj[key] == null) {
throw new CastError('String', obj[key], `${prefix}${key}.$rename`);
}
const schematype = new SchemaString(`${prefix}${key}.$rename`);
const schematype = new SchemaString(`${prefix}${key}.$rename`, null, null, schema);
obj[key] = schematype.castForQuery(null, obj[key], context);
continue;
}
Expand Down
62 changes: 10 additions & 52 deletions lib/schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -1572,7 +1572,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
let name;

if (utils.isPOJO(type) || type === 'mixed') {
return new MongooseTypes.Mixed(path, obj);
return new MongooseTypes.Mixed(path, obj, null, this);
}

if (Array.isArray(type) || type === Array || type === 'array' || type === MongooseTypes.Array) {
Expand All @@ -1595,7 +1595,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
`${path}: new Schema(...)`);
}
}
return new MongooseTypes.DocumentArray(path, cast, obj);
return new MongooseTypes.DocumentArray(path, cast, obj, null, this);
}
if (cast &&
cast[options.typeKey] &&
Expand All @@ -1612,14 +1612,14 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
`${path}: new Schema(...)`);
}
}
return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast);
return new MongooseTypes.DocumentArray(path, cast[options.typeKey], obj, cast, this);
}
if (typeof cast !== 'undefined') {
if (Array.isArray(cast) || cast.type === Array || cast.type == 'Array') {
if (cast && cast.type == 'Array') {
cast.type = Array;
}
return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj);
return new MongooseTypes.Array(path, this.interpretAsType(path, cast, options), obj, null, this);
}
}

Expand Down Expand Up @@ -1660,10 +1660,10 @@ Schema.prototype.interpretAsType = function(path, obj, options) {

const childSchema = new Schema(castFromTypeKey, childSchemaOptions);
childSchema.$implicitlyCreated = true;
return new MongooseTypes.DocumentArray(path, childSchema, obj);
return new MongooseTypes.DocumentArray(path, childSchema, obj, null, this);
} else {
// Special case: empty object becomes mixed
return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj);
return new MongooseTypes.Array(path, MongooseTypes.Mixed, obj, null, this);
}
}

Expand All @@ -1672,7 +1672,7 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
? cast[options.typeKey]
: cast;
if (Array.isArray(type)) {
return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj);
return new MongooseTypes.Array(path, this.interpretAsType(path, type, options), obj, null, this);
}

name = typeof type === 'string'
Expand All @@ -1696,11 +1696,11 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
}
}

return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options);
return new MongooseTypes.Array(path, cast || MongooseTypes.Mixed, obj, options, this);
}

if (type && type.instanceOfSchema) {
return new MongooseTypes.Subdocument(type, path, obj);
return new MongooseTypes.Subdocument(type, path, obj, this);
}

if (Buffer.isBuffer(type)) {
Expand Down Expand Up @@ -1739,53 +1739,11 @@ Schema.prototype.interpretAsType = function(path, obj, options) {
'https://bit.ly/mongoose-schematypes for a list of valid schema types.');
}

if (name === 'Union') {
obj.parentSchema = this;
}
const schemaType = new MongooseTypes[name](path, obj, options);

if (schemaType.$isSchemaMap) {
createMapNestedSchemaType(this, schemaType, path, obj, options);
}
const schemaType = new MongooseTypes[name](path, obj, options, this);

return schemaType;
};

/*!
* ignore
*/

function createMapNestedSchemaType(schema, schemaType, path, obj, options) {
const mapPath = 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, schema.options.typeKey);
if (isInlineSchema) {
_mapType = { [schema.options.typeKey]: new Schema(obj.of) };
} else if (utils.isPOJO(obj.of)) {
_mapType = Object.assign({}, obj.of);
} else {
_mapType = { [schema.options.typeKey]: obj.of };
}

if (_mapType[schema.options.typeKey] && _mapType[schema.options.typeKey].instanceOfSchema) {
const subdocumentSchema = _mapType[schema.options.typeKey];
subdocumentSchema.eachPath((subpath, type) => {
if (type.options.select === true || type.options.select === false) {
throw new MongooseError('Cannot use schema-level projections (`select: true` or `select: false`) within maps at path "' + path + '.' + subpath + '"');
}
});
}

if (utils.hasUserDefinedProperty(obj, 'ref')) {
_mapType.ref = obj.ref;
}
}
schemaType.$__schemaType = schema.interpretAsType(mapPath, _mapType, options);
}

/**
* Iterates the schemas paths similar to Array#forEach.
*
Expand Down
13 changes: 9 additions & 4 deletions lib/schema/array.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ const emptyOpts = Object.freeze({});
* @param {SchemaType} cast
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaArray(key, cast, options, schemaOptions) {
function SchemaArray(key, cast, options, schemaOptions, parentSchema) {
// lazy load
EmbeddedDoc || (EmbeddedDoc = require('../types').Embedded);

Expand Down Expand Up @@ -92,7 +93,11 @@ function SchemaArray(key, cast, options, schemaOptions) {
!caster.$isArraySubdocument &&
!caster.$isSchemaMap) {
const path = this.caster instanceof EmbeddedDoc ? null : key;
this.caster = new caster(path, castOptions);
if (caster === SchemaArray) {
this.caster = new caster(path, castOptions, schemaOptions, null, parentSchema);
} else {
this.caster = new caster(path, castOptions, schemaOptions, parentSchema);
}
} else {
this.caster = caster;
if (!(this.caster instanceof EmbeddedDoc)) {
Expand All @@ -105,7 +110,7 @@ function SchemaArray(key, cast, options, schemaOptions) {

this.$isMongooseArray = true;

SchemaType.call(this, key, options, 'Array');
SchemaType.call(this, key, options, 'Array', parentSchema);

let defaultArr;
let fn;
Expand Down Expand Up @@ -494,7 +499,7 @@ SchemaArray.prototype.discriminator = function(...args) {

SchemaArray.prototype.clone = function() {
const options = Object.assign({}, this.options);
const schematype = new this.constructor(this.path, this.caster, options, this.schemaOptions);
const schematype = new this.constructor(this.path, this.caster, options, this.schemaOptions, this.parentSchema);
schematype.validators = this.validators.slice();
if (this.requiredValidator !== undefined) {
schematype.requiredValidator = this.requiredValidator;
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/bigint.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeD
*
* @param {String} path
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaBigInt(path, options) {
SchemaType.call(this, path, options, 'BigInt');
function SchemaBigInt(path, options, _schemaOptions, parentSchema) {
SchemaType.call(this, path, options, 'BigInt', parentSchema);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/boolean.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeD
*
* @param {String} path
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaBoolean(path, options) {
SchemaType.call(this, path, options, 'Boolean');
function SchemaBoolean(path, options, _schemaOptions, parentSchema) {
SchemaType.call(this, path, options, 'Boolean', parentSchema);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ const CastError = SchemaType.CastError;
*
* @param {String} key
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaBuffer(key, options) {
SchemaType.call(this, key, options, 'Buffer');
function SchemaBuffer(key, options, _schemaOptions, parentSchema) {
SchemaType.call(this, key, options, 'Buffer', parentSchema);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/date.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ const CastError = SchemaType.CastError;
*
* @param {String} key
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaDate(key, options) {
SchemaType.call(this, key, options, 'Date');
function SchemaDate(key, options, _schemaOptions, parentSchema) {
SchemaType.call(this, key, options, 'Date', parentSchema);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/decimal128.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@ const isBsonType = require('../helpers/isBsonType');
*
* @param {String} key
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaDecimal128(key, options) {
SchemaType.call(this, key, options, 'Decimal128');
function SchemaDecimal128(key, options, _schemaOptions, parentSchema) {
SchemaType.call(this, key, options, 'Decimal128', parentSchema);
}

/**
Expand Down
26 changes: 19 additions & 7 deletions lib/schema/documentArray.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,12 @@ let Subdocument;
* @param {Schema} schema
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaArray
* @api public
*/

function SchemaDocumentArray(key, schema, options, schemaOptions) {
function SchemaDocumentArray(key, schema, options, schemaOptions, parentSchema) {
if (schema.options && schema.options.timeseries) {
throw new InvalidSchemaOptionError(key, 'timeseries');
}
Expand All @@ -59,7 +60,7 @@ function SchemaDocumentArray(key, schema, options, schemaOptions) {
const EmbeddedDocument = _createConstructor(schema, options);
EmbeddedDocument.prototype.$basePath = key;

SchemaArray.call(this, key, EmbeddedDocument, options);
SchemaArray.call(this, key, EmbeddedDocument, options, null, parentSchema);

this.schema = schema;
// EmbeddedDocument schematype options
Expand All @@ -83,10 +84,15 @@ function SchemaDocumentArray(key, schema, options, schemaOptions) {
}

const $parentSchemaType = this;
this.$embeddedSchemaType = new DocumentArrayElement(key + '.$', {
...(schemaOptions || {}),
$parentSchemaType
});
this.$embeddedSchemaType = new DocumentArrayElement(
key + '.$',
{
...(schemaOptions || {}),
$parentSchemaType
},
schemaOptions,
parentSchema
);

this.$embeddedSchemaType.caster = this.Constructor;
this.$embeddedSchemaType.schema = this.schema;
Expand Down Expand Up @@ -529,7 +535,13 @@ SchemaDocumentArray.prototype.cast = function(value, doc, init, prev, options) {

SchemaDocumentArray.prototype.clone = function() {
const options = Object.assign({}, this.options);
const schematype = new this.constructor(this.path, this.schema, options, this.schemaOptions);
const schematype = new this.constructor(
this.path,
this.schema,
options,
this.schemaOptions,
this.parentSchema
);
schematype.validators = this.validators.slice();
if (this.requiredValidator !== undefined) {
schematype.requiredValidator = this.requiredValidator;
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/documentArrayElement.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,18 +14,20 @@ const getConstructor = require('../helpers/discriminator/getConstructor');
*
* @param {String} path
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaDocumentArrayElement(path, options) {
function SchemaDocumentArrayElement(path, options, _schemaOptions, parentSchema) {
this.$parentSchemaType = options && options.$parentSchemaType;
if (!this.$parentSchemaType) {
throw new MongooseError('Cannot create DocumentArrayElement schematype without a parent');
}
delete options.$parentSchemaType;

SchemaType.call(this, path, options, 'DocumentArrayElement');
SchemaType.call(this, path, options, 'DocumentArrayElement', parentSchema);

this.$isMongooseDocumentArrayElement = true;
}
Expand Down
6 changes: 4 additions & 2 deletions lib/schema/double.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@ const createJSONSchemaTypeDefinition = require('../helpers/createJSONSchemaTypeD
*
* @param {String} path
* @param {Object} options
* @param {Object} schemaOptions
* @param {Schema} parentSchema
* @inherits SchemaType
* @api public
*/

function SchemaDouble(path, options) {
SchemaType.call(this, path, options, 'Double');
function SchemaDouble(path, options, _schemaOptions, parentSchema) {
SchemaType.call(this, path, options, 'Double', parentSchema);
}

/**
Expand Down
Loading
Loading