"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.IndexInformationOperation = exports.IndexExistsOperation = exports.ListIndexesOperation = exports.DropIndexesOperation = exports.DropIndexOperation = exports.EnsureIndexOperation = exports.CreateIndexOperation = exports.CreateIndexesOperation = exports.IndexesOperation = void 0; const error_1 = require("../error"); const read_preference_1 = require("../read_preference"); const utils_1 = require("../utils"); const command_1 = require("./command"); const common_functions_1 = require("./common_functions"); const operation_1 = require("./operation"); const VALID_INDEX_OPTIONS = new Set([ 'background', 'unique', 'name', 'partialFilterExpression', 'sparse', 'hidden', 'expireAfterSeconds', 'storageEngine', 'collation', 'version', // text indexes 'weights', 'default_language', 'language_override', 'textIndexVersion', // 2d-sphere indexes '2dsphereIndexVersion', // 2d indexes 'bits', 'min', 'max', // geoHaystack Indexes 'bucketSize', // wildcard indexes 'wildcardProjection' ]); function isIndexDirection(x) { return (typeof x === 'number' || x === '2d' || x === '2dsphere' || x === 'text' || x === 'geoHaystack'); } function isSingleIndexTuple(t) { return Array.isArray(t) && t.length === 2 && isIndexDirection(t[1]); } function makeIndexSpec(indexSpec, options) { const key = new Map(); const indexSpecs = !Array.isArray(indexSpec) || isSingleIndexTuple(indexSpec) ? [indexSpec] : indexSpec; // Iterate through array and handle different types for (const spec of indexSpecs) { if (typeof spec === 'string') { key.set(spec, 1); } else if (Array.isArray(spec)) { key.set(spec[0], spec[1] ?? 1); } else if (spec instanceof Map) { for (const [property, value] of spec) { key.set(property, value); } } else if ((0, utils_1.isObject)(spec)) { for (const [property, value] of Object.entries(spec)) { key.set(property, value); } } } return { ...options, key }; } /** @internal */ class IndexesOperation extends operation_1.AbstractOperation { constructor(collection, options) { super(options); this.options = options; this.collection = collection; } execute(server, session, callback) { const coll = this.collection; const options = this.options; (0, common_functions_1.indexInformation)(coll.s.db, coll.collectionName, { full: true, ...options, readPreference: this.readPreference, session }, callback); } } exports.IndexesOperation = IndexesOperation; /** @internal */ class CreateIndexesOperation extends command_1.CommandOperation { constructor(parent, collectionName, indexes, options) { super(parent, options); this.options = options ?? {}; this.collectionName = collectionName; this.indexes = indexes.map(userIndex => { // Ensure the key is a Map to preserve index key ordering const key = userIndex.key instanceof Map ? userIndex.key : new Map(Object.entries(userIndex.key)); const name = userIndex.name != null ? userIndex.name : Array.from(key).flat().join('_'); const validIndexOptions = Object.fromEntries(Object.entries({ ...userIndex }).filter(([optionName]) => VALID_INDEX_OPTIONS.has(optionName))); return { ...validIndexOptions, name, key }; }); } execute(server, session, callback) { const options = this.options; const indexes = this.indexes; const serverWireVersion = (0, utils_1.maxWireVersion)(server); const cmd = { createIndexes: this.collectionName, indexes }; if (options.commitQuorum != null) { if (serverWireVersion < 9) { callback(new error_1.MongoCompatibilityError('Option `commitQuorum` for `createIndexes` not supported on servers < 4.4')); return; } cmd.commitQuorum = options.commitQuorum; } // collation is set on each index, it should not be defined at the root this.options.collation = undefined; super.executeCommand(server, session, cmd, err => { if (err) { callback(err); return; } const indexNames = indexes.map(index => index.name || ''); callback(undefined, indexNames); }); } } exports.CreateIndexesOperation = CreateIndexesOperation; /** @internal */ class CreateIndexOperation extends CreateIndexesOperation { constructor(parent, collectionName, indexSpec, options) { super(parent, collectionName, [makeIndexSpec(indexSpec, options)], options); } execute(server, session, callback) { super.execute(server, session, (err, indexNames) => { if (err || !indexNames) return callback(err); return callback(undefined, indexNames[0]); }); } } exports.CreateIndexOperation = CreateIndexOperation; /** @internal */ class EnsureIndexOperation extends CreateIndexOperation { constructor(db, collectionName, indexSpec, options) { super(db, collectionName, indexSpec, options); this.readPreference = read_preference_1.ReadPreference.primary; this.db = db; this.collectionName = collectionName; } execute(server, session, callback) { const indexName = this.indexes[0].name; const cursor = this.db.collection(this.collectionName).listIndexes({ session }); cursor.toArray().then(indexes => { indexes = Array.isArray(indexes) ? indexes : [indexes]; if (indexes.some(index => index.name === indexName)) { callback(undefined, indexName); return; } super.execute(server, session, callback); }, error => { if (error instanceof error_1.MongoError && error.code === error_1.MONGODB_ERROR_CODES.NamespaceNotFound) { // ignore "NamespaceNotFound" errors return super.execute(server, session, callback); } return callback(error); }); } } exports.EnsureIndexOperation = EnsureIndexOperation; /** @internal */ class DropIndexOperation extends command_1.CommandOperation { constructor(collection, indexName, options) { super(collection, options); this.options = options ?? {}; this.collection = collection; this.indexName = indexName; } execute(server, session, callback) { const cmd = { dropIndexes: this.collection.collectionName, index: this.indexName }; super.executeCommand(server, session, cmd, callback); } } exports.DropIndexOperation = DropIndexOperation; /** @internal */ class DropIndexesOperation extends DropIndexOperation { constructor(collection, options) { super(collection, '*', options); } execute(server, session, callback) { super.execute(server, session, err => { if (err) return callback(err, false); callback(undefined, true); }); } } exports.DropIndexesOperation = DropIndexesOperation; /** @internal */ class ListIndexesOperation extends command_1.CommandOperation { constructor(collection, options) { super(collection, options); this.options = { ...options }; delete this.options.writeConcern; this.collectionNamespace = collection.s.namespace; } execute(server, session, callback) { const serverWireVersion = (0, utils_1.maxWireVersion)(server); const cursor = this.options.batchSize ? { batchSize: this.options.batchSize } : {}; const command = { listIndexes: this.collectionNamespace.collection, cursor }; // we check for undefined specifically here to allow falsy values // eslint-disable-next-line no-restricted-syntax if (serverWireVersion >= 9 && this.options.comment !== undefined) { command.comment = this.options.comment; } super.executeCommand(server, session, command, callback); } } exports.ListIndexesOperation = ListIndexesOperation; /** @internal */ class IndexExistsOperation extends operation_1.AbstractOperation { constructor(collection, indexes, options) { super(options); this.options = options; this.collection = collection; this.indexes = indexes; } execute(server, session, callback) { const coll = this.collection; const indexes = this.indexes; (0, common_functions_1.indexInformation)(coll.s.db, coll.collectionName, { ...this.options, readPreference: this.readPreference, session }, (err, indexInformation) => { // If we have an error return if (err != null) return callback(err); // Let's check for the index names if (!Array.isArray(indexes)) return callback(undefined, indexInformation[indexes] != null); // Check in list of indexes for (let i = 0; i < indexes.length; i++) { if (indexInformation[indexes[i]] == null) { return callback(undefined, false); } } // All keys found return true return callback(undefined, true); }); } } exports.IndexExistsOperation = IndexExistsOperation; /** @internal */ class IndexInformationOperation extends operation_1.AbstractOperation { constructor(db, name, options) { super(options); this.options = options ?? {}; this.db = db; this.name = name; } execute(server, session, callback) { const db = this.db; const name = this.name; (0, common_functions_1.indexInformation)(db, name, { ...this.options, readPreference: this.readPreference, session }, callback); } } exports.IndexInformationOperation = IndexInformationOperation; (0, operation_1.defineAspects)(ListIndexesOperation, [ operation_1.Aspect.READ_OPERATION, operation_1.Aspect.RETRYABLE, operation_1.Aspect.CURSOR_CREATING ]); (0, operation_1.defineAspects)(CreateIndexesOperation, [operation_1.Aspect.WRITE_OPERATION]); (0, operation_1.defineAspects)(CreateIndexOperation, [operation_1.Aspect.WRITE_OPERATION]); (0, operation_1.defineAspects)(EnsureIndexOperation, [operation_1.Aspect.WRITE_OPERATION]); (0, operation_1.defineAspects)(DropIndexOperation, [operation_1.Aspect.WRITE_OPERATION]); (0, operation_1.defineAspects)(DropIndexesOperation, [operation_1.Aspect.WRITE_OPERATION]); //# sourceMappingURL=indexes.js.map