getIndexes.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. 'use strict';
  2. const get = require('../get');
  3. const helperIsObject = require('../isObject');
  4. const decorateDiscriminatorIndexOptions = require('../indexes/decorateDiscriminatorIndexOptions');
  5. /**
  6. * Gather all indexes defined in the schema, including single nested,
  7. * document arrays, and embedded discriminators.
  8. * @param {Schema} schema
  9. * @api private
  10. */
  11. module.exports = function getIndexes(schema) {
  12. let indexes = [];
  13. const schemaStack = new WeakMap();
  14. const indexTypes = schema.constructor.indexTypes;
  15. const indexByName = new Map();
  16. collectIndexes(schema);
  17. return indexes;
  18. function collectIndexes(schema, prefix, baseSchema) {
  19. // Ignore infinitely nested schemas, if we've already seen this schema
  20. // along this path there must be a cycle
  21. if (schemaStack.has(schema)) {
  22. return;
  23. }
  24. schemaStack.set(schema, true);
  25. prefix = prefix || '';
  26. const keys = Object.keys(schema.paths);
  27. for (const key of keys) {
  28. const path = schema.paths[key];
  29. if (baseSchema != null && baseSchema.paths[key]) {
  30. // If looking at an embedded discriminator schema, don't look at paths
  31. // that the
  32. continue;
  33. }
  34. if (path._duplicateKeyErrorMessage != null) {
  35. schema._duplicateKeyErrorMessagesByPath = schema._duplicateKeyErrorMessagesByPath || {};
  36. schema._duplicateKeyErrorMessagesByPath[key] = path._duplicateKeyErrorMessage;
  37. }
  38. if (path.$isMongooseDocumentArray || path.$isSingleNested) {
  39. if (get(path, 'options.excludeIndexes') !== true &&
  40. get(path, 'schemaOptions.excludeIndexes') !== true &&
  41. get(path, 'schema.options.excludeIndexes') !== true) {
  42. collectIndexes(path.schema, prefix + key + '.');
  43. }
  44. if (path.schema.discriminators != null) {
  45. const discriminators = path.schema.discriminators;
  46. const discriminatorKeys = Object.keys(discriminators);
  47. for (const discriminatorKey of discriminatorKeys) {
  48. collectIndexes(discriminators[discriminatorKey],
  49. prefix + key + '.', path.schema);
  50. }
  51. }
  52. // Retained to minimize risk of backwards breaking changes due to
  53. // gh-6113
  54. if (path.$isMongooseDocumentArray) {
  55. continue;
  56. }
  57. }
  58. const index = path._index || (path.embeddedSchemaType && path.embeddedSchemaType._index);
  59. if (index !== false && index != null) {
  60. const field = {};
  61. const isObject = helperIsObject(index);
  62. const options = isObject ? { ...index } : {};
  63. const type = typeof index === 'string' ? index :
  64. isObject ? index.type :
  65. false;
  66. if (type && indexTypes.indexOf(type) !== -1) {
  67. field[prefix + key] = type;
  68. } else if (options.text) {
  69. field[prefix + key] = 'text';
  70. delete options.text;
  71. } else {
  72. let isDescendingIndex = false;
  73. if (index === 'descending' || index === 'desc') {
  74. isDescendingIndex = true;
  75. } else if (index === 'ascending' || index === 'asc') {
  76. isDescendingIndex = false;
  77. } else {
  78. isDescendingIndex = Number(index) === -1;
  79. }
  80. field[prefix + key] = isDescendingIndex ? -1 : 1;
  81. }
  82. delete options.type;
  83. if (schema.options.autoIndex != null) {
  84. options._autoIndex = schema.options.autoIndex;
  85. }
  86. const indexName = options?.name;
  87. if (typeof indexName === 'string') {
  88. if (indexByName.has(indexName)) {
  89. Object.assign(indexByName.get(indexName), field);
  90. } else {
  91. indexes.push([field, options]);
  92. indexByName.set(indexName, field);
  93. }
  94. } else {
  95. indexes.push([field, options]);
  96. indexByName.set(indexName, field);
  97. }
  98. }
  99. }
  100. schemaStack.delete(schema);
  101. if (prefix) {
  102. fixSubIndexPaths(schema, prefix);
  103. } else {
  104. schema._indexes.forEach(function(index) {
  105. const options = index[1];
  106. decorateDiscriminatorIndexOptions(schema, options);
  107. });
  108. indexes = indexes.concat(schema._indexes);
  109. }
  110. }
  111. /**
  112. * Checks for indexes added to subdocs using Schema.index().
  113. * These indexes need their paths prefixed properly.
  114. *
  115. * schema._indexes = [ [indexObj, options], [indexObj, options] ..]
  116. * @param {Schema} schema
  117. * @param {String} prefix
  118. * @api private
  119. */
  120. function fixSubIndexPaths(schema, prefix) {
  121. const subindexes = schema._indexes;
  122. const len = subindexes.length;
  123. for (let i = 0; i < len; ++i) {
  124. const indexObj = subindexes[i][0];
  125. const indexOptions = subindexes[i][1];
  126. const keys = Object.keys(indexObj);
  127. const klen = keys.length;
  128. const newindex = {};
  129. // use forward iteration, order matters
  130. for (let j = 0; j < klen; ++j) {
  131. const key = keys[j];
  132. newindex[prefix + key] = indexObj[key];
  133. }
  134. const newIndexOptions = Object.assign({}, indexOptions);
  135. if (indexOptions?.partialFilterExpression != null) {
  136. newIndexOptions.partialFilterExpression = {};
  137. const partialFilterExpression = indexOptions.partialFilterExpression;
  138. for (const key of Object.keys(partialFilterExpression)) {
  139. newIndexOptions.partialFilterExpression[prefix + key] =
  140. partialFilterExpression[key];
  141. }
  142. }
  143. indexes.push([newindex, newIndexOptions]);
  144. }
  145. }
  146. };