/node_modules/mongoose/lib/services/model/discriminator.js

https://bitbucket.org/coleman333/smartsite · JavaScript · 163 lines · 128 code · 27 blank · 8 comment · 26 complexity · 66ff6f84db1b75f46630430151fb60f5 MD5 · raw file

  1. 'use strict';
  2. var defineKey = require('../document/compile').defineKey;
  3. var utils = require('../../utils');
  4. var CUSTOMIZABLE_DISCRIMINATOR_OPTIONS = {
  5. toJSON: true,
  6. toObject: true,
  7. _id: true,
  8. id: true
  9. };
  10. /*!
  11. * ignore
  12. */
  13. module.exports = function discriminator(model, name, schema, tiedValue) {
  14. if (!(schema && schema.instanceOfSchema)) {
  15. throw new Error('You must pass a valid discriminator Schema');
  16. }
  17. if (model.base && model.base.options.applyPluginsToDiscriminators) {
  18. model.base._applyPlugins(schema);
  19. }
  20. if (model.schema.discriminatorMapping &&
  21. !model.schema.discriminatorMapping.isRoot) {
  22. throw new Error('Discriminator "' + name +
  23. '" can only be a discriminator of the root model');
  24. }
  25. var key = model.schema.options.discriminatorKey;
  26. var baseSchemaAddition = {};
  27. baseSchemaAddition[key] = {
  28. default: void 0,
  29. select: true,
  30. $skipDiscriminatorCheck: true
  31. };
  32. baseSchemaAddition[key][model.schema.options.typeKey] = String;
  33. model.schema.add(baseSchemaAddition);
  34. defineKey(key, null, model.prototype, null, [key], model.schema.options);
  35. if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) {
  36. throw new Error('Discriminator "' + name +
  37. '" cannot have field with name "' + key + '"');
  38. }
  39. var value = name;
  40. if (typeof tiedValue == 'string' && tiedValue.length) {
  41. value = tiedValue;
  42. }
  43. function merge(schema, baseSchema) {
  44. if (baseSchema.paths._id &&
  45. baseSchema.paths._id.options &&
  46. !baseSchema.paths._id.options.auto) {
  47. var originalSchema = schema;
  48. utils.merge(schema, originalSchema);
  49. delete schema.paths._id;
  50. delete schema.tree._id;
  51. }
  52. // Find conflicting paths: if something is a path in the base schema
  53. // and a nested path in the child schema, overwrite the base schema path.
  54. // See gh-6076
  55. var baseSchemaPaths = Object.keys(baseSchema.paths);
  56. var conflictingPaths = [];
  57. for (i = 0; i < baseSchemaPaths.length; ++i) {
  58. if (schema.nested[baseSchemaPaths[i]]) {
  59. conflictingPaths.push(baseSchemaPaths[i]);
  60. }
  61. }
  62. utils.merge(schema, baseSchema, {
  63. omit: { discriminators: true },
  64. omitNested: conflictingPaths.reduce((cur, path) => {
  65. cur['tree.' + path] = true;
  66. return cur;
  67. }, {})
  68. });
  69. // Clean up conflicting paths _after_ merging re: gh-6076
  70. for (i = 0; i < conflictingPaths.length; ++i) {
  71. delete schema.paths[conflictingPaths[i]];
  72. }
  73. var obj = {};
  74. obj[key] = {
  75. default: value,
  76. select: true,
  77. set: function(newName) {
  78. if (newName === value) {
  79. return value;
  80. }
  81. throw new Error('Can\'t set discriminator key "' + key + '"');
  82. },
  83. $skipDiscriminatorCheck: true
  84. };
  85. obj[key][schema.options.typeKey] = String;
  86. schema.add(obj);
  87. schema.discriminatorMapping = {key: key, value: value, isRoot: false};
  88. if (baseSchema.options.collection) {
  89. schema.options.collection = baseSchema.options.collection;
  90. }
  91. var toJSON = schema.options.toJSON;
  92. var toObject = schema.options.toObject;
  93. var _id = schema.options._id;
  94. var id = schema.options.id;
  95. var keys = Object.keys(schema.options);
  96. schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
  97. for (var i = 0; i < keys.length; ++i) {
  98. var _key = keys[i];
  99. if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
  100. if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
  101. throw new Error('Can\'t customize discriminator option ' + _key +
  102. ' (can only modify ' +
  103. Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
  104. ')');
  105. }
  106. }
  107. }
  108. schema.options = utils.clone(baseSchema.options);
  109. if (toJSON) schema.options.toJSON = toJSON;
  110. if (toObject) schema.options.toObject = toObject;
  111. if (typeof _id !== 'undefined') {
  112. schema.options._id = _id;
  113. }
  114. schema.options.id = id;
  115. schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
  116. schema.plugins = Array.prototype.slice(baseSchema.plugins);
  117. schema.callQueue = baseSchema.callQueue.concat(schema.callQueue);
  118. delete schema._requiredpaths; // reset just in case Schema#requiredPaths() was called on either schema
  119. }
  120. // merges base schema into new discriminator schema and sets new type field.
  121. merge(schema, model.schema);
  122. if (!model.discriminators) {
  123. model.discriminators = {};
  124. }
  125. if (!model.schema.discriminatorMapping) {
  126. model.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
  127. }
  128. if (!model.schema.discriminators) {
  129. model.schema.discriminators = {};
  130. }
  131. model.schema.discriminators[name] = schema;
  132. if (model.discriminators[name]) {
  133. throw new Error('Discriminator with name "' + name + '" already exists');
  134. }
  135. return schema;
  136. };