discriminator.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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) {
  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. set: function(newName) {
  31. if (newName === name) {
  32. return name;
  33. }
  34. throw new Error('Can\'t set discriminator key "' + key + '", "' +
  35. name + '" !== "' + newName + '"');
  36. },
  37. $skipDiscriminatorCheck: true
  38. };
  39. baseSchemaAddition[key][model.schema.options.typeKey] = String;
  40. model.schema.add(baseSchemaAddition);
  41. defineKey(key, null, model.prototype, null, [key], model.schema.options);
  42. if (schema.path(key) && schema.path(key).options.$skipDiscriminatorCheck !== true) {
  43. throw new Error('Discriminator "' + name +
  44. '" cannot have field with name "' + key + '"');
  45. }
  46. function merge(schema, baseSchema) {
  47. if (baseSchema.paths._id &&
  48. baseSchema.paths._id.options &&
  49. !baseSchema.paths._id.options.auto) {
  50. var originalSchema = schema;
  51. utils.merge(schema, originalSchema, { retainKeyOrder: true });
  52. delete schema.paths._id;
  53. delete schema.tree._id;
  54. }
  55. utils.merge(schema, baseSchema, {
  56. retainKeyOrder: true,
  57. omit: { discriminators: true }
  58. });
  59. var obj = {};
  60. obj[key] = {
  61. default: name,
  62. select: true,
  63. set: function(newName) {
  64. if (newName === name) {
  65. return name;
  66. }
  67. throw new Error('Can\'t set discriminator key "' + key + '"');
  68. },
  69. $skipDiscriminatorCheck: true
  70. };
  71. obj[key][schema.options.typeKey] = String;
  72. schema.add(obj);
  73. schema.discriminatorMapping = {key: key, value: name, isRoot: false};
  74. if (baseSchema.options.collection) {
  75. schema.options.collection = baseSchema.options.collection;
  76. }
  77. var toJSON = schema.options.toJSON;
  78. var toObject = schema.options.toObject;
  79. var _id = schema.options._id;
  80. var id = schema.options.id;
  81. var keys = Object.keys(schema.options);
  82. schema.options.discriminatorKey = baseSchema.options.discriminatorKey;
  83. for (var i = 0; i < keys.length; ++i) {
  84. var _key = keys[i];
  85. if (!CUSTOMIZABLE_DISCRIMINATOR_OPTIONS[_key]) {
  86. if (!utils.deepEqual(schema.options[_key], baseSchema.options[_key])) {
  87. throw new Error('Can\'t customize discriminator option ' + _key +
  88. ' (can only modify ' +
  89. Object.keys(CUSTOMIZABLE_DISCRIMINATOR_OPTIONS).join(', ') +
  90. ')');
  91. }
  92. }
  93. }
  94. schema.options = utils.clone(baseSchema.options);
  95. if (toJSON) schema.options.toJSON = toJSON;
  96. if (toObject) schema.options.toObject = toObject;
  97. if (typeof _id !== 'undefined') {
  98. schema.options._id = _id;
  99. }
  100. schema.options.id = id;
  101. schema.s.hooks = model.schema.s.hooks.merge(schema.s.hooks);
  102. schema.plugins = Array.prototype.slice(baseSchema.plugins);
  103. schema.callQueue = baseSchema.callQueue.
  104. concat(schema.callQueue.slice(schema._defaultMiddleware.length));
  105. schema._requiredpaths = undefined; // reset just in case Schema#requiredPaths() was called on either schema
  106. }
  107. // merges base schema into new discriminator schema and sets new type field.
  108. merge(schema, model.schema);
  109. if (!model.discriminators) {
  110. model.discriminators = {};
  111. }
  112. if (!model.schema.discriminatorMapping) {
  113. model.schema.discriminatorMapping = {key: key, value: null, isRoot: true};
  114. model.schema.discriminators = {};
  115. }
  116. model.schema.discriminators[name] = schema;
  117. if (model.discriminators[name]) {
  118. throw new Error('Discriminator with name "' + name + '" already exists');
  119. }
  120. return schema;
  121. };