deno.land / x / mongoose@6.7.5 / lib / helpers / model / discriminator.js
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216'use strict';
const Mixed = require('../../schema/mixed');const defineKey = require('../document/compile').defineKey;const get = require('../get');const utils = require('../../utils');const mergeDiscriminatorSchema = require('../../helpers/discriminator/mergeDiscriminatorSchema');
const CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = { toJSON: true, toObject: true, _id: true, id: true, virtuals: true, methods: true};
/*! * ignore */
module.exports = function discriminator(model, name, schema, tiedValue, applyPlugins, mergeHooks) { if (!(schema && schema.instanceOfSchema)) { throw new Error('You must pass a valid discriminator Schema'); }
mergeHooks = mergeHooks == null ? true : mergeHooks;
if (model.schema.discriminatorMapping && !model.schema.discriminatorMapping.isRoot) { throw new Error('Discriminator "' + name + '" can only be a discriminator of the root model'); }
if (applyPlugins) { const applyPluginsToDiscriminators = get(model.base, 'options.applyPluginsToDiscriminators', false) || !mergeHooks; // Even if `applyPluginsToDiscriminators` isn't set, we should still apply // global plugins to schemas embedded in the discriminator schema (gh-7370) model.base._applyPlugins(schema, { skipTopLevel: !applyPluginsToDiscriminators }); }
const key = model.schema.options.discriminatorKey;
const existingPath = model.schema.path(key); if (existingPath != null) { if (!utils.hasUserDefinedProperty(existingPath.options, 'select')) { existingPath.options.select = true; } existingPath.options.$skipDiscriminatorCheck = true; } else { const baseSchemaAddition = {}; baseSchemaAddition[key] = { default: void 0, select: true, $skipDiscriminatorCheck: true }; baseSchemaAddition[key][model.schema.options.typeKey] = String; model.schema.add(baseSchemaAddition); defineKey({ prop: key, prototype: model.prototype, options: model.schema.options }); }
if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) { throw new Error('Discriminator "' + name + '" cannot have field with name "' + key + '"'); }
let value = name; if ((typeof tiedValue === 'string' && tiedValue.length) || tiedValue != null) { value = tiedValue; }
function merge(schema, baseSchema) { // Retain original schema before merging base schema schema._baseSchema = baseSchema; if (baseSchema.paths._id && baseSchema.paths._id.options && !baseSchema.paths._id.options.auto) { schema.remove('_id'); }
// Find conflicting paths: if something is a path in the base schema // and a nested path in the child schema, overwrite the base schema path. // See gh-6076 const baseSchemaPaths = Object.keys(baseSchema.paths); const conflictingPaths = [];
for (const path of baseSchemaPaths) { if (schema.nested[path]) { conflictingPaths.push(path); continue; }
if (path.indexOf('.') === -1) { continue; } const sp = path.split('.').slice(0, -1); let cur = ''; for (const piece of sp) { cur += (cur.length ? '.' : '') + piece; if (schema.paths[cur] instanceof Mixed || schema.singleNestedPaths[cur] instanceof Mixed) { conflictingPaths.push(path); } } }
mergeDiscriminatorSchema(schema, baseSchema, { omit: { discriminators: true, base: true, _applyDiscriminators: true }, omitNested: conflictingPaths.reduce((cur, path) => { cur['tree.' + path] = true; return cur; }, {}) });
// Clean up conflicting paths _after_ merging re: gh-6076 for (const conflictingPath of conflictingPaths) { delete schema.paths[conflictingPath]; }
// Rebuild schema models because schemas may have been merged re: #7884 schema.childSchemas.forEach(obj => { obj.model.prototype.$__setSchema(obj.schema); });
const obj = {}; obj[key] = { default: value, select: true, set: function(newName) { if (newName === value || (Array.isArray(value) && utils.deepEqual(newName, value))) { return value; } throw new Error('Can\'t set discriminator key "' + key + '"'); }, $skipDiscriminatorCheck: true }; obj[key][schema.options.typeKey] = existingPath ? existingPath.options[schema.options.typeKey] : String; schema.add(obj);
schema.discriminatorMapping = { key: key, value: value, isRoot: false };
if (baseSchema.options.collection) { schema.options.collection = baseSchema.options.collection; }
const toJSON = schema.options.toJSON; const toObject = schema.options.toObject; const _id = schema.options._id; const id = schema.options.id;
const keys = Object.keys(schema.options); schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
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(', ') + ')'); } } } schema.options = utils.clone(baseSchema.options); if (toJSON) schema.options.toJSON = toJSON; if (toObject) schema.options.toObject = toObject; if (typeof _id !== 'undefined') { schema.options._id = _id; } schema.options.id = id; if (mergeHooks) { schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks); } if (applyPlugins) { schema.plugins = Array.prototype.slice.call(baseSchema.plugins); } schema.callQueue = baseSchema.callQueue.concat(schema.callQueue); delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema }
// merges base schema into new discriminator schema and sets new type field. merge(schema, model.schema);
if (!model.discriminators) { model.discriminators = {}; }
if (!model.schema.discriminatorMapping) { model.schema.discriminatorMapping = { key: key, value: null, isRoot: true }; } if (!model.schema.discriminators) { model.schema.discriminators = {}; }
model.schema.discriminators[name] = schema;
if (model.discriminators[name] && !schema.options.overwriteModels) { throw new Error('Discriminator with name "' + name + '" already exists'); }
return schema;};
Version Info