import { Cluster } from './cluster' ; import { CouchbaseError , IndexExistsError , IndexNotFoundError } from './errors' ; import { HttpExecutor } from './httpexecutor' ; import { RequestSpan } from './tracing' ; import { CompoundTimeout , NodeCallback , PromiseHelper } from './utilities' ; category export class QueryIndex { /**
     * The name of the index.
     */ name : string ; /**
     * Whether this is a primary or secondary index.
     */ isPrimary : boolean ; /**
     * The type of index.
     */ type : string ; /**
     * The current state of the index.
     */ state : string ; /**
     * The keyspace which this index covers.
     */ keyspace : string ; /**
     * The keys for this index.
     */ indexKey : string [ ] ; /**
     * The conditional expression to limit the indexes scope.
     */ condition ? : string ; /**
     * Information about the partitioning of this index.
     */ partition ? : string ; internal constructor ( data ) { this . name = data . name ; this . isPrimary = data . isPrimary ; this . type = data . type ; this . state = data . state ; this . keyspace = data . keyspace ; this . indexKey = data . indexKey ; this . condition = data . condition ; this . partition = data . partition ; } } category export interface CreateQueryIndexOptions { /**
     * Whether or not the call should ignore the index already existing when
     * determining whether the call was successful.
     */ ignoreIfExists ? : boolean ; /**
     * The number of replicas of this index that should be created.
     */ numReplicas ? : number ; /**
     * Specifies whether this index creation should be deferred until a later
     * point in time when they can be explicitly built together.
     */ deferred ? : boolean ; /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface CreatePrimaryQueryIndexOptions { /**
     * The name of this primary index.
     */ name ? : string ; /**
     * Whether or not the call should ignore the index already existing when
     * determining whether the call was successful.
     */ ignoreIfExists ? : boolean ; /**
     * The number of replicas of this index that should be created.
     */ numReplicas ? : number ; /**
     * Specifies whether this index creation should be deferred until a later
     * point in time when they can be explicitly built together.
     */ deferred ? : boolean ; /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface DropQueryIndexOptions { /**
     * Whether or not the call should ignore the index already existing when
     * determining whether the call was successful.
     */ ignoreIfNotExists ? : boolean ; /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface DropPrimaryQueryIndexOptions { /**
     * The name of the primary index to drop.
     */ name ? : string ; /**
     * Whether or not the call should ignore the index already existing when
     * determining whether the call was successful.
     */ ignoreIfNotExists ? : boolean ; /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface GetAllQueryIndexesOptions { /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface BuildQueryIndexOptions { /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; /**
     * The timeout for this operation, represented in milliseconds.
     */ timeout ? : number ; } category export interface WatchQueryIndexOptions { /**
     * Specifies whether the primary indexes should be watched as well.
     */ watchPrimary ? : boolean ; /**
     * The parent tracing span that this operation will be part of.
     */ parentSpan ? : RequestSpan ; } category export class QueryIndexManager { private _cluster : Cluster ; internal constructor ( cluster ) { this . _cluster = cluster ; } private get _http ( ) { return new HttpExecutor ( this . _cluster . _getClusterConn ( ) ) ; } private async _createIndex ( bucketName , options , callback ? ) { let qs = '' ; if ( ! options . fields ) { qs += 'CREATE PRIMARY INDEX' ; } else { qs += 'CREATE INDEX' ; } if ( options . name ) { qs += ' `' + options . name + '`' ; } qs += ' ON `' + bucketName + '`' ; if ( options . fields && options . fields . length > 0 ) { qs += '(' ; for ( let i = 0 ; i < options . fields . length ; ++ i ) { if ( i > 0 ) { qs += ', ' ; } qs += '`' + options . fields [ i ] + '`' ; } qs += ')' ; } const withOpts = { } ; if ( options . deferred ) { withOpts . defer_build = true ; } if ( options . numReplicas ) { withOpts . num_replica = options . numReplicas ; } if ( Object . keys ( withOpts ) . length > 0 ) { qs += ' WITH ' + JSON . stringify ( withOpts ) ; } return PromiseHelper . wrapAsync ( async ( ) => { try { await this . _cluster . query ( qs , { parentSpan : options . parentSpan , timeout : options . timeout , } ) ; } catch ( err ) { if ( options . ignoreIfExists && err instanceof IndexExistsError ) { } else { throw err ; } } } , callback ) ; } param bucketName param indexName param fields param options param callback async createIndex ( bucketName , indexName , fields , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 3 ] ; options = undefined ; } if ( ! options ) { options = { } ; } return this . _createIndex ( bucketName , { name : indexName , fields : fields , ignoreIfExists : options . ignoreIfExists , numReplicas : options . numReplicas , deferred : options . deferred , parentSpan : options . parentSpan , timeout : options . timeout , } , callback ) ; } param bucketName param options param callback async createPrimaryIndex ( bucketName , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 1 ] ; options = undefined ; } if ( ! options ) { options = { } ; } return this . _createIndex ( bucketName , { name : options . name , ignoreIfExists : options . ignoreIfExists , deferred : options . deferred , parentSpan : options . parentSpan , timeout : options . timeout , } , callback ) ; } private async _dropIndex ( bucketName , options , callback ? ) { const timeout = options . timeout ; return PromiseHelper . wrapAsync ( async ( ) => { let qs = '' ; if ( ! options . name ) { qs += 'DROP PRIMARY INDEX `' + bucketName + '`' ; } else { qs += 'DROP INDEX `' + bucketName + '`.`' + options . name + '`' ; } try { await this . _cluster . query ( qs , { parentSpan : options . parentSpan , timeout : timeout , } ) ; } catch ( err ) { if ( options . ignoreIfNotExists && err instanceof IndexNotFoundError ) { } else { throw err ; } } } , callback ) ; } param bucketName param indexName param options param callback async dropIndex ( bucketName , indexName , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 2 ] ; options = undefined ; } if ( ! options ) { options = { } ; } return this . _dropIndex ( bucketName , { name : indexName , ignoreIfNotExists : options . ignoreIfNotExists , parentSpan : options . parentSpan , timeout : options . timeout , } , callback ) ; } param bucketName param options param callback async dropPrimaryIndex ( bucketName , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 1 ] ; options = undefined ; } if ( ! options ) { options = { } ; } return this . _dropIndex ( bucketName , { name : options . name , ignoreIfNotExists : options . ignoreIfNotExists , parentSpan : options . parentSpan , timeout : options . timeout , } , callback ) ; } param bucketName param options param callback async getAllIndexes ( bucketName , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 1 ] ; options = undefined ; } if ( ! options ) { options = { } ; } const qs = `SELECT idx.* FROM system:indexes AS idx
              WHERE (
                (\`bucket_id\` IS MISSING AND \`keyspace_id\`="${ bucketName }")
                OR \`bucket_id\`="${ bucketName }"
              ) AND \`using\`="gsi" ORDER BY is_primary DESC, name ASC` ; const parentSpan = options . parentSpan ; const timeout = options . timeout ; return PromiseHelper . wrapAsync ( async ( ) => { const res = await this . _cluster . query ( qs , { parentSpan : parentSpan , timeout : timeout , } ) ; const indexes = res . rows . map ( ( row ) => new QueryIndex ( { name : row . name , isPrimary : row . is_primary , type : row . using , state : row . state , keyspace : row . keyspace_id , indexKey : row . index_key , condition : row . condition , partition : row . partition , } ) ) ; return indexes ; } , callback ) ; } param bucketName param options param callback async buildDeferredIndexes ( bucketName , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 1 ] ; options = undefined ; } if ( ! options ) { options = { } ; } const parentSpan = options . parentSpan ; const timeout = options . timeout ; const timer = new CompoundTimeout ( timeout ) ; return PromiseHelper . wrapAsync ( async ( ) => { const indexes = await this . getAllIndexes ( bucketName , { parentSpan : parentSpan , timeout : timer . left ( ) , } ) ; const deferredList = indexes . filter ( ( index ) => index . state === 'deferred' || index . state === 'pending' ) . map ( ( index ) => index . name ) ; if ( deferredList . length === 0 ) { return [ ] ; } let qs = '' ; qs += 'BUILD INDEX ON `' + bucketName + '` ' ; qs += '(' ; for ( let j = 0 ; j < deferredList . length ; ++ j ) { if ( j > 0 ) { qs += ', ' ; } qs += '`' + deferredList [ j ] + '`' ; } qs += ')' ; await this . _cluster . query ( qs , { timeout : timer . left ( ) , } ) ; return deferredList ; } , callback ) ; } param bucketName param indexNames param timeout param options param callback async watchIndexes ( bucketName , indexNames , timeout , options ? , callback ? ) { if ( options instanceof Function ) { callback = arguments [ 3 ] ; options = undefined ; } if ( ! options ) { options = { } ; } if ( options . watchPrimary ) { indexNames = [ ... indexNames , '#primary' ] ; } const parentSpan = options . parentSpan ; const timer = new CompoundTimeout ( timeout ) ; return PromiseHelper . wrapAsync ( async ( ) => { let curInterval = 50 ; for ( ; ; ) { const foundIdxs = await this . getAllIndexes ( bucketName , { parentSpan : parentSpan , timeout : timer . left ( ) , } ) ; const onlineIdxs = foundIdxs . filter ( ( idx ) => idx . state === 'online' ) ; const onlineIdxNames = onlineIdxs . map ( ( idx ) => idx . name ) ; let allOnline = true ; indexNames . forEach ( ( indexName ) => { allOnline = allOnline && onlineIdxNames . indexOf ( indexName ) !== - 1 ; } ) ; if ( allOnline ) { break ; } curInterval = Math . min ( 1000 , curInterval + 500 ) ; const userTimeLeft = timer . left ( ) ; if ( userTimeLeft !== undefined ) { curInterval = Math . min ( curInterval , userTimeLeft ) ; } if ( curInterval <= 0 ) { throw new CouchbaseError ( 'Failed to find all indexes online within the alloted time.' ) ; } await new Promise ( ( resolve ) => setTimeout ( ( ) => resolve ( true ) , curInterval ) ) ; } } , callback ) ; } }