indexes.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270
  1. "use strict";
  2. Object.defineProperty(exports, "__esModule", { value: true });
  3. exports.IndexInformationOperation = exports.IndexExistsOperation = exports.ListIndexesOperation = exports.DropIndexesOperation = exports.DropIndexOperation = exports.EnsureIndexOperation = exports.CreateIndexOperation = exports.CreateIndexesOperation = exports.IndexesOperation = void 0;
  4. const error_1 = require("../error");
  5. const read_preference_1 = require("../read_preference");
  6. const utils_1 = require("../utils");
  7. const command_1 = require("./command");
  8. const common_functions_1 = require("./common_functions");
  9. const operation_1 = require("./operation");
  10. const VALID_INDEX_OPTIONS = new Set([
  11. 'background',
  12. 'unique',
  13. 'name',
  14. 'partialFilterExpression',
  15. 'sparse',
  16. 'hidden',
  17. 'expireAfterSeconds',
  18. 'storageEngine',
  19. 'collation',
  20. 'version',
  21. // text indexes
  22. 'weights',
  23. 'default_language',
  24. 'language_override',
  25. 'textIndexVersion',
  26. // 2d-sphere indexes
  27. '2dsphereIndexVersion',
  28. // 2d indexes
  29. 'bits',
  30. 'min',
  31. 'max',
  32. // geoHaystack Indexes
  33. 'bucketSize',
  34. // wildcard indexes
  35. 'wildcardProjection'
  36. ]);
  37. function isIndexDirection(x) {
  38. return (typeof x === 'number' || x === '2d' || x === '2dsphere' || x === 'text' || x === 'geoHaystack');
  39. }
  40. function isSingleIndexTuple(t) {
  41. return Array.isArray(t) && t.length === 2 && isIndexDirection(t[1]);
  42. }
  43. function makeIndexSpec(indexSpec, options) {
  44. const key = new Map();
  45. const indexSpecs = !Array.isArray(indexSpec) || isSingleIndexTuple(indexSpec) ? [indexSpec] : indexSpec;
  46. // Iterate through array and handle different types
  47. for (const spec of indexSpecs) {
  48. if (typeof spec === 'string') {
  49. key.set(spec, 1);
  50. }
  51. else if (Array.isArray(spec)) {
  52. key.set(spec[0], spec[1] ?? 1);
  53. }
  54. else if (spec instanceof Map) {
  55. for (const [property, value] of spec) {
  56. key.set(property, value);
  57. }
  58. }
  59. else if ((0, utils_1.isObject)(spec)) {
  60. for (const [property, value] of Object.entries(spec)) {
  61. key.set(property, value);
  62. }
  63. }
  64. }
  65. return { ...options, key };
  66. }
  67. /** @internal */
  68. class IndexesOperation extends operation_1.AbstractCallbackOperation {
  69. constructor(collection, options) {
  70. super(options);
  71. this.options = options;
  72. this.collection = collection;
  73. }
  74. executeCallback(server, session, callback) {
  75. const coll = this.collection;
  76. const options = this.options;
  77. (0, common_functions_1.indexInformation)(coll.s.db, coll.collectionName, { full: true, ...options, readPreference: this.readPreference, session }, callback);
  78. }
  79. }
  80. exports.IndexesOperation = IndexesOperation;
  81. /** @internal */
  82. class CreateIndexesOperation extends command_1.CommandCallbackOperation {
  83. constructor(parent, collectionName, indexes, options) {
  84. super(parent, options);
  85. this.options = options ?? {};
  86. this.collectionName = collectionName;
  87. this.indexes = indexes.map(userIndex => {
  88. // Ensure the key is a Map to preserve index key ordering
  89. const key = userIndex.key instanceof Map ? userIndex.key : new Map(Object.entries(userIndex.key));
  90. const name = userIndex.name != null ? userIndex.name : Array.from(key).flat().join('_');
  91. const validIndexOptions = Object.fromEntries(Object.entries({ ...userIndex }).filter(([optionName]) => VALID_INDEX_OPTIONS.has(optionName)));
  92. return {
  93. ...validIndexOptions,
  94. name,
  95. key
  96. };
  97. });
  98. }
  99. executeCallback(server, session, callback) {
  100. const options = this.options;
  101. const indexes = this.indexes;
  102. const serverWireVersion = (0, utils_1.maxWireVersion)(server);
  103. const cmd = { createIndexes: this.collectionName, indexes };
  104. if (options.commitQuorum != null) {
  105. if (serverWireVersion < 9) {
  106. callback(new error_1.MongoCompatibilityError('Option `commitQuorum` for `createIndexes` not supported on servers < 4.4'));
  107. return;
  108. }
  109. cmd.commitQuorum = options.commitQuorum;
  110. }
  111. // collation is set on each index, it should not be defined at the root
  112. this.options.collation = undefined;
  113. super.executeCommandCallback(server, session, cmd, err => {
  114. if (err) {
  115. callback(err);
  116. return;
  117. }
  118. const indexNames = indexes.map(index => index.name || '');
  119. callback(undefined, indexNames);
  120. });
  121. }
  122. }
  123. exports.CreateIndexesOperation = CreateIndexesOperation;
  124. /** @internal */
  125. class CreateIndexOperation extends CreateIndexesOperation {
  126. constructor(parent, collectionName, indexSpec, options) {
  127. super(parent, collectionName, [makeIndexSpec(indexSpec, options)], options);
  128. }
  129. executeCallback(server, session, callback) {
  130. super.executeCallback(server, session, (err, indexNames) => {
  131. if (err || !indexNames)
  132. return callback(err);
  133. return callback(undefined, indexNames[0]);
  134. });
  135. }
  136. }
  137. exports.CreateIndexOperation = CreateIndexOperation;
  138. /** @internal */
  139. class EnsureIndexOperation extends CreateIndexOperation {
  140. constructor(db, collectionName, indexSpec, options) {
  141. super(db, collectionName, indexSpec, options);
  142. this.readPreference = read_preference_1.ReadPreference.primary;
  143. this.db = db;
  144. this.collectionName = collectionName;
  145. }
  146. executeCallback(server, session, callback) {
  147. const indexName = this.indexes[0].name;
  148. const cursor = this.db.collection(this.collectionName).listIndexes({ session });
  149. cursor.toArray().then(indexes => {
  150. indexes = Array.isArray(indexes) ? indexes : [indexes];
  151. if (indexes.some(index => index.name === indexName)) {
  152. callback(undefined, indexName);
  153. return;
  154. }
  155. super.executeCallback(server, session, callback);
  156. }, error => {
  157. if (error instanceof error_1.MongoError && error.code === error_1.MONGODB_ERROR_CODES.NamespaceNotFound) {
  158. // ignore "NamespaceNotFound" errors
  159. return super.executeCallback(server, session, callback);
  160. }
  161. return callback(error);
  162. });
  163. }
  164. }
  165. exports.EnsureIndexOperation = EnsureIndexOperation;
  166. /** @internal */
  167. class DropIndexOperation extends command_1.CommandCallbackOperation {
  168. constructor(collection, indexName, options) {
  169. super(collection, options);
  170. this.options = options ?? {};
  171. this.collection = collection;
  172. this.indexName = indexName;
  173. }
  174. executeCallback(server, session, callback) {
  175. const cmd = { dropIndexes: this.collection.collectionName, index: this.indexName };
  176. super.executeCommandCallback(server, session, cmd, callback);
  177. }
  178. }
  179. exports.DropIndexOperation = DropIndexOperation;
  180. /** @internal */
  181. class DropIndexesOperation extends DropIndexOperation {
  182. constructor(collection, options) {
  183. super(collection, '*', options);
  184. }
  185. executeCallback(server, session, callback) {
  186. super.executeCallback(server, session, err => {
  187. if (err)
  188. return callback(err, false);
  189. callback(undefined, true);
  190. });
  191. }
  192. }
  193. exports.DropIndexesOperation = DropIndexesOperation;
  194. /** @internal */
  195. class ListIndexesOperation extends command_1.CommandCallbackOperation {
  196. constructor(collection, options) {
  197. super(collection, options);
  198. this.options = { ...options };
  199. delete this.options.writeConcern;
  200. this.collectionNamespace = collection.s.namespace;
  201. }
  202. executeCallback(server, session, callback) {
  203. const serverWireVersion = (0, utils_1.maxWireVersion)(server);
  204. const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {};
  205. const command = { listIndexes: this.collectionNamespace.collection, cursor };
  206. // we check for undefined specifically here to allow falsy values
  207. // eslint-disable-next-line no-restricted-syntax
  208. if (serverWireVersion >= 9 && this.options.comment !== undefined) {
  209. command.comment = this.options.comment;
  210. }
  211. super.executeCommandCallback(server, session, command, callback);
  212. }
  213. }
  214. exports.ListIndexesOperation = ListIndexesOperation;
  215. /** @internal */
  216. class IndexExistsOperation extends operation_1.AbstractCallbackOperation {
  217. constructor(collection, indexes, options) {
  218. super(options);
  219. this.options = options;
  220. this.collection = collection;
  221. this.indexes = indexes;
  222. }
  223. executeCallback(server, session, callback) {
  224. const coll = this.collection;
  225. const indexes = this.indexes;
  226. (0, common_functions_1.indexInformation)(coll.s.db, coll.collectionName, { ...this.options, readPreference: this.readPreference, session }, (err, indexInformation) => {
  227. // If we have an error return
  228. if (err != null)
  229. return callback(err);
  230. // Let's check for the index names
  231. if (!Array.isArray(indexes))
  232. return callback(undefined, indexInformation[indexes] != null);
  233. // Check in list of indexes
  234. for (let i = 0; i < indexes.length; i++) {
  235. if (indexInformation[indexes[i]] == null) {
  236. return callback(undefined, false);
  237. }
  238. }
  239. // All keys found return true
  240. return callback(undefined, true);
  241. });
  242. }
  243. }
  244. exports.IndexExistsOperation = IndexExistsOperation;
  245. /** @internal */
  246. class IndexInformationOperation extends operation_1.AbstractCallbackOperation {
  247. constructor(db, name, options) {
  248. super(options);
  249. this.options = options ?? {};
  250. this.db = db;
  251. this.name = name;
  252. }
  253. executeCallback(server, session, callback) {
  254. const db = this.db;
  255. const name = this.name;
  256. (0, common_functions_1.indexInformation)(db, name, { ...this.options, readPreference: this.readPreference, session }, callback);
  257. }
  258. }
  259. exports.IndexInformationOperation = IndexInformationOperation;
  260. (0, operation_1.defineAspects)(ListIndexesOperation, [
  261. operation_1.Aspect.READ_OPERATION,
  262. operation_1.Aspect.RETRYABLE,
  263. operation_1.Aspect.CURSOR_CREATING
  264. ]);
  265. (0, operation_1.defineAspects)(CreateIndexesOperation, [operation_1.Aspect.WRITE_OPERATION]);
  266. (0, operation_1.defineAspects)(CreateIndexOperation, [operation_1.Aspect.WRITE_OPERATION]);
  267. (0, operation_1.defineAspects)(EnsureIndexOperation, [operation_1.Aspect.WRITE_OPERATION]);
  268. (0, operation_1.defineAspects)(DropIndexOperation, [operation_1.Aspect.WRITE_OPERATION]);
  269. (0, operation_1.defineAspects)(DropIndexesOperation, [operation_1.Aspect.WRITE_OPERATION]);
  270. //# sourceMappingURL=indexes.js.map