index_job.js

// *****************************************************************************
// Copyright 2013-2023 Aerospike, Inc.
//
// Licensed under the Apache License, Version 2.0 (the 'License')
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an 'AS IS' BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// *****************************************************************************

'use strict'

const status = require('./status')
const Info = require('./info')
const Job = require('./job')

const util = require('util')

/**
 * @class IndexJob
 * @classdesc Potentially long-running index creation job.
 * @extends Job
 *
 * @see {@link Client#createIndex}
 *
 * @example
 *
 * const Aerospike = require('aerospike')
 *
 * // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
 * var config = {
 *   hosts: '192.168.33.10:3000',
 *   policies: {
 *     write : new Aerospike.WritePolicy({socketTimeout : 0, totalTimeout : 0})
 *   }
 * }
 *
 * let binName = 'food'
 * let indexName = 'foodIndex'
 * let options = {
 *   ns: 'test',
 *   set: 'demo',
 *   bin: binName,
 *   index: indexName,
 *   datatype: Aerospike.indexDataType.STRING
 * }
 *
 * Aerospike.connect(config)
 *   .then(client => {
 *     client.put(new Aerospike.Key('test', 'demo', 'mykey1'), {location: "Kale"})
 *     .then((result) => {
 *       client.createIndex(options)
 *       .then(job => job.wait())
 *       .then(() => {
 *         console.info('secondary index (SI) %s on %s was created successfully', indexName, binName)
 *         client.indexRemove('test', indexName)
 *         .then(() => {
 *           client.close()
 *         })
 *         .catch(error => {
 *           console.error('Error removing index:', error)
 *           client.close()
 *         })
 *       })
 *       .catch(error => {
 *         console.error('Error creating index:', error)
 *         client.close()
 *       })
 *     })
 *     .catch(error => {
 *         console.error('Error writing record:', error)
 *         client.close()
 *       })
 *   })
 *   .catch(error => console.error('Error connecting to cluster:',  error))
 */
function IndexJob (client, namespace, indexName) {
  this.client = client
  this.namespace = namespace
  this.indexName = indexName
}
util.inherits(IndexJob, Job)

/**
 * @private
 */
IndexJob.prototype.hasCompleted = function (sindexInfo) {
  if (sindexInfo.length === 0) {
    return false
  }

  return sindexInfo.every(info => info.load_pct === 100)
}

/**
 * Fetches info for the SI from each cluster node.
 *
 * @private
 */
IndexJob.prototype.info = function () {
  const sindex = 'sindex/' + this.namespace + '/' + this.indexName
  return this.client.infoAll(sindex)
    .catch(error => {
      if (error.code === status.ERR_INDEX_NOT_FOUND) {
        return [{ load_pct: 0 }]
      } else {
        throw error
      }
    })
    .then(responses => responses
      .map(response => Info.parse(response.info))
      .map(info => info[sindex] || { load_pct: 0 }))
}

/**
 * @function IndexJob#wait
 *
 * @summary Wait until the job of creating a SI has completed.
 *
 * @param {number} [pollInterval=1000] - Interval in milliseconds to use when polling the cluster nodes.
 * @param {doneCallback} [callback] - The function to call when the operation completes.
 *
 * @return {?Promise} If no callback function is passed, the function returns a
 * Promise that resolves once the job is completed.
 */

module.exports = IndexJob