// ***************************************************************************** // Copyright 2013-2022 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 path = require('path') const util = require('util') const EventEmitter = require('events') const as = require('bindings')('aerospike.node') const AerospikeError = require('./error') const Commands = require('./commands') const Config = require('./config') const EventLoop = require('./event_loop') const IndexJob = require('./index_job') const Query = require('./query') const Scan = require('./scan') const UdfJob = require('./udf_job') const operations = require('./operations') const utils = require('./utils') // number of client instances currently connected to any Aerospike cluster let _connectedClients = 0 // callback function for cluster events (node added/removed, etc.) function eventsCallback (event) { /** * @event Client#nodeAdded * @type {object} * @property {string} nodeName - Name of the cluster node that triggered this event. * @property {string} nodeAddress - IP address & port of the cluster node that triggered this event. * @since v2.7.0 */ /** * @event Client#nodeRemoved * @type {object} * @property {string} nodeName - Name of the cluster node that triggered this event. * @property {string} nodeAddress - IP address & port of the cluster node that triggered this event. * @since v2.7.0 */ /** * @event Client#disconnected * @since v2.7.0 * * @example * * const Aerospike = require('aerospike') * * Aerospike.connect((error, client) => { * if (error) throw error * * client.on('disconnected', () => { * console.warn('Client got disconnected from cluster') * }) * * // client is now ready to accept commands, e.g. get/put/... * * }) */ this.emit(event.name, event) /** * @event Client#event * @description Instead of adding listeners for the {@link * event:Client#nodeAdded|nodeAdded}, {@link * event:Client#nodeRemoved|nodeRemoved} and {@link * event:Client#disconnected|disconnected} events, applications can also * subscribe to the <code>event</code> event to receive callbacks for any * kind of cluster event. * * @type {object} * @property {string} name - Name of the event. * @property {string} [nodeName] - Name of the cluster node that triggered this event. * @property {string} [nodeAddress] - IP address & port of the cluster node that triggered this event. * @since v2.7.0 * * @example * * const Aerospike = require('aerospike') * * Aerospike.connect((error, client) => { * if (error) throw error * * client.on('event', (event) => { * var now = new Date().toUTCString() * console.info(now, event.name, event.nodeName) // Example output: * // Thu, 13 Jul 2017 06:47:35 GMT nodeAdded BB94DC07D270009 * // Thu, 13 Jul 2017 06:47:35 GMT nodeAdded C1D4DC0AD270002 * // Thu, 13 Jul 2017 06:48:52 GMT nodeRemoved C1D4DC0AD270002 * // Thu, 13 Jul 2017 06:49:08 GMT nodeRemoved BB94DC07D270009 * // Thu, 13 Jul 2017 06:49:08 GMT disconnected * }) * * // client is now ready to accept commands, e.g. get/put/... * * }) */ this.emit('event', event) } /** * @class Client * @classdesc Aerospike client * * @summary Construct a new Aerospike client instance. * * @param {Config} config - Configuration used to initialize the client. */ function Client (config) { EventEmitter.call(this) /** * @name Client#config * * @summary A copy of the configuration with which the client was initialized. * * @type {Config} */ this.config = new Config(config) /** @private */ this.as_client = as.client(this.config) /** @private */ this.connected = false /** * @name Client#captureStackTraces * * @summary Set to <code>true</code> to enable capturing of debug stacktraces for * every database command. * * @description The client will capture a stacktrace before each database * command is executed, instead of capturing the stacktrace only when an * error is raised. This generally results in much more useful stacktraces * that include stackframes from the calling application issuing the database * command. * * **Note:** Enabling this feature incurs a significant performance overhead for * every database command. It is recommended to leave this feature disabled * in production environments. * * By default, the client will set this flag to true, if the * <code>AEROSPIKE_DEBUG_STACKTRACES</code> environment variable is set (to * any value). * * @type {boolean} * @default <code>true</code>, if * <code>process.env.AEROSPIKE_DEBUG_STACKTRACES</code> is set; * <code>false</code> otherwise. */ this.captureStackTraces = !!process.env.AEROSPIKE_DEBUG_STACKTRACES } util.inherits(Client, EventEmitter) /** * @private */ Client.prototype.asExec = function (cmd, args) { return this.as_client[cmd].apply(this.as_client, args) } /** * @function Client#getNodes * * @summary Returns a list of all cluster nodes known to the client. * * @return {Array.<{name: string, address: string}>} List of node objects * * @since v2.6.0 * * @example * * const Aerospike = require('aerospike') * Aerospike.connect((error, client) => { * if (err) throw err * console.log(client.getNodes()) // [ { name: 'BB9EB63B2005452', address: '127.0.0.1:3000' }, * // { name: 'C1DEB63B2005452', address: '127.0.0.1:3100' } ] * client.close() * }) * */ Client.prototype.getNodes = function () { return this.as_client.getNodes() } /** * @function Client#addSeedHost * * @summary Adds a seed host to the cluster. * * @param {String} hostname - Hostname/IP address of the new seed host * @param {Number} [port=3000] - Port number; defaults to {@link Config#port} or 3000. * * @since v2.6.0 */ Client.prototype.addSeedHost = function (hostname, port) { port = port || this.config.port this.as_client.addSeedHost(hostname, port) } /** * @function Client#removeSeedHost * * @summary Removes a seed host from the cluster. * * @param {String} hostname - Hostname/IP address of the seed host * @param {Number} [port=3000] - Port number; defaults to {@link Config#port} or 3000. * * @since v2.6.0 */ Client.prototype.removeSeedHost = function (hostname, port) { port = port || this.config.port this.as_client.removeSeedHost(hostname, port) } /** * @function Client#batchExists * * @summary Checks the existence of a batch of records from the database cluster. * * @param {Key[]} keys - An array of Keys used to locate the records in the cluster. * @param {BatchPolicy} [policy] - The Batch Policy to use for this operation. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @deprecated since v2.0 - use {@link Client#batchRead} instead. * * @example * * var keys = [ * new Key('test', 'demo', 'key1'), * new Key('test', 'demo', 'key2'), * new Key('test', 'demo', 'key3') * ] * * client.batchExists(keys, function (error, results) { * if (error) { * // handle failure * } else { * results.forEach(function (result) { * switch (result.status) { * case status.OK: * // record found * break * case status.ERR_RECORD_NOT_FOUND: * // record not found * break * default: * // error while reading record * break * } * }) * } * }) */ Client.prototype.batchExists = function (keys, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.BatchExists(this, [keys, policy], callback) return cmd.execute() } /** * @function Client#batchGet * * @summary Reads a batch of records from the database cluster. * * @param {Key[]} keys - An array of keys, used to locate the records in the cluster. * @param {BatchPolicy} [policy] - The Batch Policy to use for this operation. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @deprecated since v2.0 - use {@link Client#batchRead} instead. * * @example * * var keys = [ * new Key('test', 'demo', 'key1'), * new Key('test', 'demo', 'key2'), * new Key('test', 'demo', 'key3') * ] * * client.batchGet(keys, function (error, results) { * if (error) { * // handle failure * } else { * results.forEach(function (result) { * switch (result.status) { * case status.OK: * // record found - bin values are available in result.record * break * case status.ERR_RECORD_NOT_FOUND: * // record not found * break * default: * // error while reading record * break * } * }) * } * }) */ Client.prototype.batchGet = function (keys, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.BatchGet(this, [keys, policy], callback) return cmd.execute() } /** * @function Client#batchRead * * @summary Read multiple records for specified batch keys in one batch call. * * @description * * This method allows different namespaces/bins to be requested for each key in * the batch. This method requires server >= 3.6.0. * * @param {object[]} records - {@link Record} List of keys and bins to retrieve. * @param {number} records[].type - {@link Record#type} Batch type. * @param {Key} records[].key - Record Key. * @param {string[]} [records[].bins] - List of bins to retrieve. * @param {boolean} [records[].readAllBins] - Whether to retrieve all bins or * just the meta data of the record. If true, ignore <code>bins</code> and read * all bins; if false and <code>bins</code> is specified, read specified bins; * if false and <code>bins</code> is not specified, read only record meta data * (generation, expiration, etc.) * @param {BatchPolicy} [policy] - The Batch Policy to use for this operation. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @since v2.0 * * @example * * const Aerospike = require('aerospike') * var batchRecords = [ * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key1'), bins: ['i', 's'] }, * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key2'), readAllBins: true }, * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key3'), * ops:[ * op.read('blob-bin') * ]} * ] * Aerospike.connect(function (error, client) { * if (error) throw error * client.batchRead(batchRecords, function (error, results) { * if (error) throw error * results.forEach(function (result) { * console.log(result) * }) * }) * }) */ Client.prototype.batchRead = function (records, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.BatchRead(this, [records, policy], callback) return cmd.execute() } /** * @function Client#batchWrite * * @summary Read/Write multiple records for specified batch keys in one batch call. * * @description * * This method allows different sub-commands for each key in the batch. * This method requires server >= 6.0.0. * * @param {object[]} records - {@link Record} List of batch sub-commands to perform. * @param {number} records[].type - {@link Record#type} Batch type. * @param {Key} records[].key - Record Key. * @param {BatchPolicy} [policy] - The Batch Policy to use for this operation. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @since v5.0.0 * * @example * const Aerospike = require('aerospike') * const batchType = Aerospike.batchType * var batchRecords = [ * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key1'), * bins: ['i', 's'] }, * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key2'), * readAllBins: true }, * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key3'), * ops:[ * op.read('blob') * ]}, * { type: batchType.BATCH_WRITE, * key: new Aerospike.Key('test', 'demo', 'key4'), * ops:[ * op.write('geo', new GeoJSON({ type: 'Point', coordinates: [123.456, 1.308] })), * op.write('blob', Buffer.from('bar')) * ], * policy: new Aerospike.BatchWritePolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * gen: Aerospike.policy.gen.EQ, * exists: Aerospike.policy.exists.CREATE, * durableDelete: true * })}, * { type: batchType.BATCH_REMOVE, * key: new Aerospike.Key('test', 'demo', 'key5'), * policy: new Aerospike.BatchRemovePolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * gen: Aerospike.policy.gen.EQ, * durableDelete: true * })}, * } * ] * Aerospike.connect(function (error, client) { * if (error) throw error * client.batchWrite(batchRecords, function (error, results) { * if (error) throw error * results.forEach(function (result) { * console.log(result) * }) * }) * }) */ Client.prototype.batchWrite = function (records, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.BatchWrite(this, [records, policy], callback) return cmd.execute() } /** * @function Client#batchApply * * @summary Apply UDF (user defined function) on multiple keys. * * @description * * This method allows multiple sub-commands for each key in the batch. * This method requires server >= 6.0.0. * * @param {object[]} records - {@link Record} List of batch sub-commands to perform. * @param {number} records[].type - {@link Record#type} Batch type. * @param {Key} records[].key - Record Key. * @param {object[]} udf - Server UDF module/function and argList to apply. * @param {BatchPolicy} [batchPolicy] - The Batch Policy to use for this operation. * @param {BatchApplyPolicy} [batchApplyPolicy] UDF policy configuration parameters. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @since v5.0.0 * * @example * const Aerospike = require('aerospike') * const batchType = Aerospike.batchType * var batchRecords = [ * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key1'), * bins: ['i', 's'] }, * { type: batchType.BATCH_READ, * key: new Aerospike.Key('test', 'demo', 'key2'), * readAllBins: true }, * { type: batchType.BATCH_APPLY, * key: new Aerospike.Key('test', 'demo', 'key4'), * policy: new Aerospike.BatchApplyPolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * durableDelete: true * }), * udf: { * module: 'udf', * funcname: 'function1', * args: [[1, 2, 3]] * } * }, * { type: batchType.BATCH_APPLY, * key: new Aerospike.Key('test', 'demo', 'key5'), * policy: new Aerospike.BatchApplyPolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * durableDelete: true * }), * udf: { * module: 'udf', * funcname: 'function2', * args: [[1, 2, 3]] * } * } * ] * Aerospike.connect(function (error, client) { * if (error) throw error * client.batchApply(batchRecords, udf, function (error, results) { * if (error) throw error * results.forEach(function (result) { * console.log(result) * }) * }) * }) */ Client.prototype.batchApply = function (records, udf, batchPolicy, batchApplyPolicy, callback) { if (typeof batchPolicy === 'function') { callback = batchPolicy batchPolicy = null batchApplyPolicy = null } else { if (typeof batchApplyPolicy === 'function') { callback = batchApplyPolicy batchApplyPolicy = null } } const cmd = new Commands.BatchApply(this, [records, udf, batchPolicy, batchApplyPolicy], callback) return cmd.execute() } /** * @function Client#batchRemove * * @summary Remove multiple records. * * @description * * This method allows multi sub-commands for each key in the batch. * This method requires server >= 6.0.0. * * @param {object[]} records - {@link Record} List of batch sub-commands to perform. * @param {number} records[].type - {@link Record#type} Batch type. * @param {Key} records[].key - Record Key. * @param {BatchPolicy} [batchPolicy] - The Batch Policy to use for this operation. * @param {BatchRemovePolicy} [batchRemovePolicy] Remove policy configuration parameters. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @since v5.0.0 * * @example * const Aerospike = require('aerospike') * const batchType = Aerospike.batchType * var batchRecords = [ * { type: batchType.BATCH_REMOVE, * key: new Aerospike.Key('test', 'demo', 'key5'), * policy: new Aerospike.BatchRemovePolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * gen: Aerospike.policy.gen.EQ, * durableDelete: true * })}, * }, * { type: batchType.BATCH_REMOVE, * key: new Aerospike.Key('test', 'demo', 'key6'), * policy: new Aerospike.BatchRemovePolicy({ * filterExpression: exp.eq(exp.binInt('i'), exp.int(37)), * key: Aerospike.policy.key.SEND, * commitLevel: Aerospike.policy.commitLevel.ALL, * gen: Aerospike.policy.gen.EQ, * durableDelete: true * })}, * } * ] * Aerospike.connect(function (error, client) { * if (error) throw error * client.batchRemove(batchRecords, function (error, results) { * if (error) throw error * results.forEach(function (result) { * console.log(result) * }) * }) * }) */ Client.prototype.batchRemove = function (records, batchPolicy, batchRemovePolicy, callback) { if (typeof batchPolicy === 'function') { callback = batchPolicy batchPolicy = null batchRemovePolicy = null } else { if (typeof batchRemovePolicy === 'function') { callback = batchRemovePolicy batchRemovePolicy = null } } const cmd = new Commands.BatchRemove(this, [records, batchPolicy, batchRemovePolicy], callback) return cmd.execute() } /** * @function Client#batchSelect * * @summary Reads a subset of bins for a batch of records from the database cluster. * * @param {Key[]} keys - An array of keys, used to locate the records in the cluster. * @param {string[]} bins - An array of bin names for the bins to be returned for the given keys. * @param {BatchPolicy} [policy] - The Batch Policy to use for this operation. * @param {batchRecordsCallback} [callback] - The function to call when * the operation completes, with the results of the batch operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the batch operation. * * @deprecated since v2.0 - use {@link Client#batchRead} instead. * * @example * * var keys = [ * new Key('test', 'demo', 'key1'), * new Key('test', 'demo', 'key2'), * new Key('test', 'demo', 'key3') * ] * var bins = ['s', 'i'] * * client.batchSelect(keys, bins, function (error, results) { * if (error) { * // handle failure * } else { * results.forEach(function (result) { * switch (result.status) { * case status.OK: * // record found - selected bins are available in result.record. * break * case status.ERR_RECORD_NOT_FOUND: * // record not found * break * default: * // error while reading record * break * } * }) * } * }) */ Client.prototype.batchSelect = function (keys, bins, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.BatchSelect(this, [keys, bins, policy], callback) return cmd.execute() } /** * @function Client#close * * @summary Closes the client connection to the cluster. * * @param {boolean} [releaseEventLoop=false] - Whether to release the event loop handle after the client is closed. * * @see module:aerospike.releaseEventLoop * * @example * * const Aerospike = require('aerospike') * Aerospike.connect(function (error, client) { * if (error) throw error * // client ready to receive commands * client.close() * }) */ Client.prototype.close = function (releaseEventLoop = false) { if (this.isConnected(false)) { this.connected = false this.as_client.close() _connectedClients -= 1 } if (_connectedClients === 0) { if (releaseEventLoop) { EventLoop.releaseEventLoop() } else { EventLoop.unreferenceEventLoop() } } } /** * @function Client#connect * * @summary Establishes the connection to the cluster. * * @description * * Once the client is connected to at least one server node, it will start * polling each cluster node regularly to discover the current cluster status. * As new nodes are added to the cluster, or existing nodes are removed, the * client will establish or close down connections to these nodes. If the * client gets disconnected from the cluster, it will keep polling the last * known server endpoints, and will reconnect automatically if the connection * is reestablished. * * @param {connectCallback} [callback] - The function to call once the * client connection has been established successfully and the client is ready * to accept commands. * * @return {?Promise} If no callback function is passed, the function returns * a Promise resolving to the connected client. * * @throws {AerospikeError} if event loop resources have already been released. * * @see {@link Config#connTimeoutMs} - Initial host connection timeout in milliseconds. * @see {@link module:aerospike.connect} - Initial host connection timeout in milliseconds. * @see module:aerospike.releaseEventLoop * * @example <caption>Using callback function</caption> * * const Aerospike = require('aerospike') * * var config = { ... } * var client = Aerospike.client(config) * client.connect((error) => { * if (error) { * console.error('Failed to connect to cluster: %s', error.message) * process.exit() * } else { * // client is ready to accept commands * } * }) */ Client.prototype.connect = function (callback) { EventLoop.registerASEventLoop() const eventsCb = eventsCallback.bind(this) this.as_client.setupEventCb(eventsCb) const cmd = new Commands.Connect(this) return cmd.execute() .then(() => { this.connected = true _connectedClients += 1 if (callback) callback(null, this) else return this }) .catch(error => { this.as_client.close() if (callback) callback(error) else throw error }) } /** * @function Client#createIndex * * @summary Creates a secondary index (SI). * * @description * * Calling the <code>createIndex</code> method issues an * index create command to the Aerospike cluster and returns immediately. To * verify that the index has been created and populated with all the data use * the {@link IndexJob} instance returned by the callback. * * Aerospike currently supports indexing of strings, integers and geospatial * information in GeoJSON format. * * ##### String Indexes * * A string index allows for equality lookups. An equality lookup means that if * you query for an indexed bin with value "abc", then only records containing * bins with "abc" will be returned. * * ##### Integer Indexes * * An integer index allows for either equality or range lookups. An equality * lookup means that if you query for an indexed bin with value 123, then only * records containing bins with the value 123 will be returned. A range lookup * means that if you can query bins within a range. So, if your range is * (1...100), then all records containing a value in that range will be * returned. * * ##### Geo 2D Sphere Indexes * * A geo 2d sphere index allows either "contains" or "within" lookups. A * "contains" lookup means that if you query for an indexed bin with GeoJSON * point element, then only records containing bins with a GeoJSON element * containing that point will be returned. A "within" lookup means that if you * query for an indexed bin with a GeoJSON polygon element, then all records * containing bins with a GeoJSON element wholly contained within that polygon * will be returned. * * @param {Object} options - Options for creating the index. * @param {string} options.ns - The namespace on which the index is to be created. * @param {string} options.set - The set on which the index is to be created. * @param {string} options.bin - The name of the bin which values are to be indexed. * @param {string} options.index - The name of the index to be created. * @param {module:aerospike.indexType} [options.type] - Type of index to be * created based on the type of values stored in the bin. This option needs to * be specified if the bin to be indexed contains list or map values and the * individual entries of the list or keys/values of the map should be indexed. * @param {module:aerospike.indexDataType} options.datatype - The data type of * the index to be created, e.g. Numeric, String or Geo. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the operation completes. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that will resolve to an {@link IndexJob} instance. * * @see {@link module:aerospike.indexType} for enumeration of supported index types. * @see {@link module:aerospike.indexDataType} for enumeration of supported data types. * @see {@link IndexJob} * * @since v2.0 * * @example * * const Aerospike = require('aerospike') * Aerospike.connect((error, client) => { * if (error) throw error * * // create index over user's recent locations * let namespace = 'test' * let set = 'demo' * let binName = 'rloc' // recent locations * let indexName = 'recentLocationsIdx' * let indexType = Aerospike.indexType.LIST * let dataType = Aerospike.indexDataType.GEO2DSPHERE * let options = { ns: namespace, * set: set, * bin: binName, * index: indexName, * type: indexType, * datatype: dataType } * let policy = new Aerospike.InfoPolicy({ timeout: 100 }) * * client.createIndex(options, policy, (error, job) => { * if (error) throw error * * // wait for index creation to complete * var pollInterval = 100 * job.waitUntilDone(pollInterval, (error) => { * if (error) throw error * console.info('SI %s on %s was created successfully', indexName, binName) * client.close() * }) * }) * }) */ Client.prototype.createIndex = function (options, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const args = [ options.ns, options.set, options.bin, options.index, options.type || as.indexType.DEFAULT, options.datatype, policy ] const cmd = new Commands.IndexCreate(this, args, callback) cmd.convertResult = () => new IndexJob(this, options.ns, options.index) return cmd.execute() } /** * @function Client#createIntegerIndex * * @summary Creates a SI of type Integer. * * @description This is a short-hand for calling {@link Client#createIndex} * with the <code>datatype</code> option set to <code>Aerospike.indexDataType.NUMERIC</code>. * * @param {Object} options - Options for creating the index. * @param {string} options.ns - The namespace on which the index is to be created. * @param {string} options.set - The set on which the index is to be created. * @param {string} options.bin - The name of the bin which values are to be indexed. * @param {string} options.index - The name of the index to be created. * @param {module:aerospike.indexType} [options.type] - Type of index to be * created based on the type of values stored in the bin. This option needs to * be specified if the bin to be indexed contains list or map values and the * individual entries of the list or keys/values of the map should be indexed. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the operation completes. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that will resolve to an {@link IndexJob} instance. * * @see {@link Client#indexCreate} * * @example * * const Aerospike = require('aerospike') * Aerospike.connect((error, client) => { * if (error) throw error * * var binName = 'age' * var indexName = 'ageIndex' * var options = { ns: 'test', * set: 'demo', * bin: binName, * index: indexName } * * client.createIntegerIndex(options, function (error) { * if (error) throw error * console.info('SI %s on %s was created successfully', indexName, binName) * client.close() * }) * }) */ Client.prototype.createIntegerIndex = function (options, policy, callback) { options.datatype = as.indexDataType.NUMERIC return this.createIndex(options, policy, callback) } /** * @function Client#createStringIndex * * @summary Creates a SI of type String. * * @description This is a short-hand for calling {@link Client#createIndex} * with the <code>datatype</code> option set to <code>Aerospike.indexDataType.STRING</code>. * * @param {Object} options - Options for creating the index. * @param {string} options.ns - The namespace on which the index is to be created. * @param {string} options.set - The set on which the index is to be created. * @param {string} options.bin - The name of the bin which values are to be indexed. * @param {string} options.index - The name of the index to be created. * @param {module:aerospike.indexType} [options.type] - Type of index to be * created based on the type of values stored in the bin. This option needs to * be specified if the bin to be indexed contains list or map values and the * individual entries of the list or keys/values of the map should be indexed. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the operation completes. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that will resolve to an {@link IndexJob} instance. * * @see {@link Client#indexCreate} * * @example * * const Aerospike = require('aerospike') * Aerospike.connect((error, client) => { * if (error) throw error * * var binName = 'name' * var indexName = 'nameIndex' * var options = { ns: 'test', * set: 'demo', * bin: binName, * index: indexName } * * client.createStringIndex(options, function (error) { * if (error) throw error * console.info('SI %s on %s was created successfully', indexName, binName) * client.close() * }) * }) */ Client.prototype.createStringIndex = function (options, policy, callback) { options.datatype = as.indexDataType.STRING return this.createIndex(options, policy, callback) } /** * @function Client#createGeo2DSphereIndex * * @summary Creates a secondary, geospatial index. * * @description This is a short-hand for calling {@link Client#createIndex} * with the <code>datatype</code> option set to <code>Aerospike.indexDataType.GEO2DSPHERE</code>. * * @param {Object} options - Options for creating the index. * @param {string} options.ns - The namespace on which the index is to be created. * @param {string} options.set - The set on which the index is to be created. * @param {string} options.bin - The name of the bin which values are to be indexed. * @param {string} options.index - The name of the index to be created. * @param {module:aerospike.indexType} [options.type] - Type of index to be * created based on the type of values stored in the bin. This option needs to * be specified if the bin to be indexed contains list or map values and the * individual entries of the list or keys/values of the map should be indexed. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the operation completes. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that will resolve to an {@link IndexJob} instance. * * @see {@link Client#indexCreate} * * @example * * const Aerospike = require('aerospike') * Aerospike.connect((error, client) => { * if (error) throw error * * var binName = 'location' * var indexName = 'locationIndex' * var options = { ns: 'test', * set: 'demo', * bin: binName, * index: indexName } * * client.createGeo2DSphereIndex(options, function (error) { * if (error) throw error * console.info('SI %s on %s was created successfully', indexName, binName) * client.close() * }) * }) */ Client.prototype.createGeo2DSphereIndex = function (options, policy, callback) { options.datatype = as.indexDataType.GEO2DSPHERE return this.createIndex(options, policy, callback) } /** * @function Client#apply * * @summary Applies a User Defined Function (UDF) on a record in the database. * @description Use this function to apply a * <a href="http://www.aerospike.com/docs/guide/record_udf.html">⇑Record UDF</a> * on a single record and return the result of the UDF function call. Record * UDFs can be used to augment both read and write behavior. * * For additional information please refer to the section on * <a href="https://www.aerospike.com/docs/udf/developing_record_udfs.html">⇑Developing Record UDFs</a> * in the Aerospike technical documentation. * * @param {Key} key - The key, used to locate the record in the cluster. * @param {Object} udfArgs - Parameters used to specify which UDF function to execute. * @param {string} udfArgs.module - The name of the UDF module that was registered with the cluster. * @param {string} udfArgs.funcname - The name of the UDF function within the module. * @param {Array.<(number|string)>} udfArgs.args - List of arguments to pass to the UDF function. * @param {ApplyPolicy} [policy] - The Apply Policy to use for this operation. * @param {valueCallback} [callback] - This function will be called with the * result returned by the Record UDF function call. * * @returns {?Promise} If no callback function is passed, the function returns * a Promise that resolves to the value returned by the UDF. * * @since v2.0 * * @see {@link Client#udfRegister} to register a UDF module to use with <code>apply()</code>. * @see {@link Query#background} and {@link Scan#background} to apply a Record UDF function to multiple records instead. * * @example * * var key = new Key('test', 'demo', value') * var udfArgs = { * module: 'my_udf_module', * funcname: 'my_udf_function', * args: ['abc', 123, 4.5] * } * * client.apply(key, udfArgs, (error, result) => { * if (error) throw error * * console.log('Result of calling my_udf_function:', result) * }) * */ Client.prototype.apply = function (key, udfArgs, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Apply(this, [key, udfArgs, policy], callback) return cmd.execute() } /** * @function Client#exists * * @summary Checks the existance of a record in the database cluster. * * @param {Key} key - The key of the record to check for existance. * @param {ReadPolicy} [policy] - The Read Policy to use for this operation. * @param {valueCallback} [callback] - The function to call when the * operation completes; the passed value is <code>true</code> if the record * exists or <code>false</code> otherwise. * * @returns {?Promise} If no callback function is passed, the function returns * a Promise that resolves to <code>true</code> if the record exists or * <code>false</code> otherwise. * * @example * * const Aerospike = require('aerospike') * * let key = new Aerospike.Key('test', 'demo', 'key1') * Aerospike.connect() * .then(client => { * return client.exists(key) * .then(exists => console.info('Key "%s" exists: %s', key.key, exists)) * .then(() => client.close()) * .catch(error => { * console.error('Error checking existance of key:', error) * client.close() * }) * }) * .catch(error => { * console.error('Error connecting to cluster:', error) * }) */ Client.prototype.exists = function exists (key, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Exists(this, [key, policy], callback) return cmd.execute() } /** * @function Client#get * * @summary Using the key provided, reads a record from the database cluster. * * @param {Key} key - The key used to locate the record in the cluster. * @param {ReadPolicy} [policy] - The Read Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation; if no callback * function is provided, the method returns a <code>Promise<code> instead. * * @returns {?Promise} If no callback function is passed, the function returns * a <code>Promise</code> that resolves to a {@link Record}. * * @example * * var key = new Key('test', 'demo', 'key1') * * client.get(key, (error, record) => { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.get = function (key, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Get(this, key, [policy], callback) return cmd.execute() } /** * @function Client#indexRemove * * @summary Removes the specified index. * * @param {string} namespace - The namespace on which the index was created. * @param {string} index - The name of the index. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {doneCallback} [callback] - The function to call when the * operation completes with the result of the operation. * * @returns {?Promise} If no callback function is passed, the function returns * a <code>Promise</code> that resolves once the operation completes. * * @example * * client.indexRemove('test', 'index', function (error) { * if (error) { * // handle failure * } else { * // handle success * } * }); */ Client.prototype.indexRemove = function (namespace, index, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.IndexRemove(this, [namespace, index, policy], callback) return cmd.execute() } /** * @function Client#info * * @summary Sends an info query to a specific cluster node. * * @description The <code>request</code> parameter is a string representing an * info request. If it is not specified, a default set of info values will be * returned. * * Please refer to the * <a href="http://www.aerospike.com/docs/reference/info">Info Command Reference</a> * for a list of all available info commands. * * @param {?String} request - The info request to send. * @param {Object} host - The address of the cluster host to send the request to. * @param {string} host.addr - The IP address or host name of the host. * @param {number} [host.port=3000] - The port of the host. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {infoCallback} [callback] - The function to call when an info response from a cluster host is received. * * @see <a href="http://www.aerospike.com/docs/reference/info" title="Info Command Reference">⇑Info Command Reference</a> * * @deprecated since v3.11.0 - use {@link Client#infoNode} or {@link Client#infoAny} instead. * * @example <caption>Sending a 'statistics' info query to a single host</caption> * * client.info('statistics', {addr: '127.0.0.1', port: 3000}, function (error, response) { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.info = function (request, host, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } if (typeof host === 'string') { host = utils.parseHostString(host) } const cmd = new Commands.InfoHost(this, [request, host, policy], callback) return cmd.execute() } /** * @function Client#infoAny * * @summary Sends an info query to a single, randomly selected cluster node. * * @description The <code>request</code> parameter is a string representing an * info request. If it is not specified, a default set of info values will be * returned. * * @param {string} [request] - The info request to send. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {infoCallback} [callback] - The function to call once the node * returns the response to the info command; if no callback function is * provided, the method returns a <code>Promise<code> instead. * * @see <a href="http://www.aerospike.com/docs/reference/info" title="Info Command Reference">⇑Info Command Reference</a> * * @since v2.4.0 * * @example <caption>Sending 'statistics' info command to random cluster node</caption> * * client.infoAny('statistics', function (error, response) { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.infoAny = function (request, policy, callback) { if (typeof request === 'function') { callback = request request = null policy = null } else if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.InfoAny(this, [request, policy], callback) return cmd.execute() } /** * @function Client#infoAll * * @summary Sends an info query to all nodes in the cluster and collects the * results. * * @description The <code>request</code> parameter is a string representing an * info request. If it is not specified, a default set of info values will be * returned. * * @param {string} [request] - The info request to send. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {infoCallback} [callback] - The function to call once all nodes have * returned a response to the info command; if no callback function is * provided, the method returns a <code>Promise<code> instead. * * @see <a href="http://www.aerospike.com/docs/reference/info" title="Info Command Reference">⇑Info Command Reference</a> * * @since v2.3.0 * * @example <caption>Sending info command to whole cluster</caption> * * client.infoAll('statistics', function (error, responses) { * if (error) { * // handle failure * } else { * responses.forEach((function (info) { * // handle response * }) * } * }) */ Client.prototype.infoAll = function (request, policy, callback) { if (typeof request === 'function') { callback = request request = null policy = null } else if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.InfoForeach(this, [request, policy], callback) return cmd.execute() } /** * @function Client#infoNode * * @summary Sends an info query to a single node in the cluster. * * @description The <code>request</code> parameter is a string representing an * info request. If it is not specified, a default set of info values will be * returned. * * @param {?string} request - The info request to send. * @param {object} node - The node to send the request to. * @param {string} node.name - The node name. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {infoCallback} [callback] - The function to call once the node * returns the response to the info command; if no callback function is * provided, the method returns a <code>Promise<code> instead. * * @see <a href="http://www.aerospike.com/docs/reference/info" title="Info Command Reference">⇑Info Command Reference</a> * * @since v3.11.0 * * @example <caption>Sending 'statistics' info command to specific cluster node</caption> * * const node = client.getNodes().pop() * client.infoNode('statistics', node).then(info => { * // process info * }) */ Client.prototype.infoNode = function (request, node, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.InfoNode(this, [request, node.name, policy], callback) return cmd.execute() } /** * @function Client#isConnected * * @summary Is client connected to any server nodes. * * @param {boolean} [checkTenderErrors=true] - Whether to consider a server * node connection that has had 5 consecutive info request failures during * cluster tender. * * @returns {boolean} <code>true</code> if the client is currently connected to any server nodes. * * @since v2.0 */ Client.prototype.isConnected = function (checkTenderErrors) { if (typeof checkTenderErrors === 'undefined') { checkTenderErrors = true } let connected = this.connected if (connected && checkTenderErrors) { connected = this.as_client.isConnected() } return connected } /** * @function Client#operate * * @summary Performs multiple operations on a single record. * * @description Operations can be created using the methods in one of the * following modules: * * {@link module:aerospike/operations} - General operations on all types. * * {@link module:aerospike/lists} - Operations on CDT List values. * * {@link module:aerospike/maps} - Operations on CDT Map values. * * {@link module:aerospike/bitwise} - Operations on Bytes values. * * @param {Key} key - The key of the record. * @param {module:aerospike/operations~Operation[]} operations - List of operations to perform on the record. * @param {Object} [metadata] - Meta data. * @param {OperatePolicy} [policy] - The Operate Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation; if no callback * function is provided, the method returns a <code>Promise<code> instead. * * @example * * const Aerospike = require('aerospike') * const op = Aerospike.operations * * var key = new Aerospike.Key('test', 'demo', 'mykey1') * var ops = [ * op.append('a', 'xyz'), * op.incr('b', 10), * op.read('b') * ] * * Aerospike.connect(function (error, client) { * client.operate(key, ops, function (error, record) { * if (error) { * // handle failure * } else { * console.log('b', record.bins['b']) // value of 'b' returned by the `read` operation * } * }) * client.close() * }) */ Client.prototype.operate = function (key, operations, metadata, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } else if (typeof metadata === 'function') { callback = metadata metadata = null } const cmd = new Commands.Operate(this, key, [operations, metadata, policy], callback) return cmd.execute() } /** * @function Client#append * * @summary Shortcut for applying the {@link * module:aerospike/operations.append} operation to one or more record bins. * * @description This function works on bins of type string or bytes; to append * a new value (of any type) to a bin containing a list of existing values, use * the {@link module:aerospike/lists.append} operation instead. * * @param {Key} key - The key of the record. * @param {Object[]} bins - The key-value mapping of bin names and the * corresponding values to append to the bin value. The bins must contain * either string or byte array values and the values to append must be of the * same type. * @param {Object} [metadata] - Meta data. * @param {OperatePolicy} [policy] - The Operate Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the opertion. * * @see {@link Client#operate} * @see {@link module:aerospike/operations.append} */ /** * @function Client#prepend * * @summary Shortcut for applying the {@link module:aerospike/operations.prepend} operation to one or more record bins. * * @param {Key} key - The key of the record. * @param {Object[]} bins - The key-value mapping of bin names and the corresponding values to prepend to the bin value. * @param {Object} [metadata] - Meta data. * @param {OperatePolicy} [policy] - The Operate Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the opertion. * * @see {@link Client#operate} * @see {@link module:aerospike/operations.prepend} */ /** * @function Client#add * * @summary Shortcut for applying the {@link module:aerospike/operations.add} operation to one or more record bins. * * @param {Key} key - The key of the record. * @param {Object[]} bins - The key-value mapping of bin names and the corresponding values to use to increment the bin values with. * @param {Object} [metadata] - Meta data. * @param {OperatePolicy} [policy] - The Operate Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the opertion. * * @since v2.0 * * @see {@link Client#operate} * @see {@link module:aerospike/operations.incr} */ // Shortcuts for some operations ;['append', 'prepend', 'add'].forEach(op => { Client.prototype[op] = function (key, bins, metadata, policy, callback) { const ops = Object.keys(bins).map(bin => operations[op](bin, bins[bin])) return this.operate(key, ops, metadata, policy, callback) } }) /** * @function Client#incr * * @summary Alias for {@link Client#add}. * * @param {Key} key - The key of the record. * @param {Object[]} bins - The key-value mapping of bin names and the corresponding values to use to increment the bin values with. * @param {Object} [metadata] - Meta data. * @param {OperatePolicy} [policy] - The Operate Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation. * * @returns {?Promise} - If no callback function is passed, the function * returns a Promise that resolves to the results of the opertion. */ Client.prototype.incr = Client.prototype.add /** * @function Client#put * * @summary Writes a record to the database cluster. * * @description * If the record exists, it modifies the record with bins provided. * To remove a bin, set its value to <code>null</code>. * * __Note:__ The client does not perform any automatic data type conversions. * Attempting to write an unsupported data type (e.g. boolean) into a record * bin will cause an error to be returned. Setting an <code>undefined</code> * value will also cause an error. * * @param {Key} key - The key of the record. * @param {object} bins - A record object used for specifying the fields to store. * @param {object} [meta] - Meta data. * @param {WritePolicy} [policy] - The Write Policy to use for this operation. * @param {writeCallback} [callback] - The function to call when the operation completes with the result of the operation. * * @example * * const Key = Aerospike.Key * * var key = new Key('test', 'demo', 'key1') * var rec = { * a: 'xyz', * b: 123 * } * * client.put(key, rec, function (error, key) { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.put = function (key, bins, meta, policy, callback) { if (typeof meta === 'function') { callback = meta meta = null } else if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Put(this, key, [bins, meta, policy], callback) return cmd.execute() } /** * @function Client#query * * @summary Creates a new {@link Query} instance, which is used to define query * in the database. * * @param {string} ns - The namespace to be queried. * @param {string} set - The set on which the query is to be executed. * @param {object} [options] - Query parameters. See {@link Query} constructor for details. * * @see {@link Query} * * @example * * const filter = Aerospike.filter * * var statement = {} * statment.filters: [filter.equal('color', 'blue')] * * var query = client.query(ns, set, statment) * var stream = query.execute() */ Client.prototype.query = function (ns, set, options) { options = options || {} if (!this.isConnected()) { throw new AerospikeError('Not connected.') } return new Query(this, ns, set, options) } /** * @function Client#remove * * @summary Removes a record with the specified key from the database cluster. * * @param {Key} key - The key of the record. * @param {RemovePolicy} [policy] - The Remove Policy to use for this operation. * @param {writeCallback} [callback] - The function to call when the operation completes with the results of the operation. * * @example * * const Key = Aerospike.Key * client.remove(new Key('test', 'demo', 'key1'), function (error, key) { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.remove = function (key, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Remove(this, key, [policy], callback) return cmd.execute() } /** * @function Client#scan * * @summary Creates a new {@link Scan} instance in order to execute a database * scan using the Scan API. * * @see {@link Scan} constructor for options that can be used to initialize a * new instance. * * @param {string} ns - The namescape. * @param {string} set - The name of a set. * @param {object} [options] - Scan parameters. See {@link Scan} constructor for details. * * @since v2.0 */ Client.prototype.scan = function (ns, set, options) { options = options || {} if (!this.isConnected()) { throw new AerospikeError('Not connected.') } return new Scan(this, ns, set, options) } /** * @function Client#select * * @summary Retrieves selected bins for a record of given key from the database cluster. * * @param {Key} key - The key of the record. * @param {string[]} bins - A list of bin names for the bins to be returned. * @param {ReadPolicy} [policy] - The Read Policy to use for this operation. * @param {recordCallback} [callback] - The function to call when the * operation completes with the results of the operation; if no callback * function is provided, the method returns a <code>Promise<code> instead. * * @example * * var key = new Key('test', 'demo', 'key1') * * client.select(key, ['name', 'age'], (error, record) => { * if (error) { * // handle failure * } else { * // handle success * } * }) */ Client.prototype.select = function (key, bins, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Select(this, key, [bins, policy], callback) return cmd.execute() } /** * @function Client#truncate * * @summary Removes records in specified namespace/set efficiently. * * @description This method is many orders of magnitude faster than deleting * records one at a time. It requires server 3.12 or later. * * @param {string} ns - Required namespace. * @param {string} set - Optional set name. Set to <code>null</code> to delete * all sets in namespace. * @param {number} before_nanos - Optionally delete records before given last * update time. Units are in nanoseconds since unix epoch (1970-01-01). If * specified, the value must be before the current time. Pass in 0 to delete * all records in namespace/set regardless of last udpate time. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {doneCallback} [callback] - The function to call when the * operation completes with the result of the operation. * * @see https://www.aerospike.com/docs/reference/info#truncate */ Client.prototype.truncate = function (ns, set, beforeNanos, policy, callback) { if (typeof set === 'function') { callback = set set = null beforeNanos = 0 policy = null } else if (typeof beforeNanos === 'function') { callback = beforeNanos beforeNanos = 0 policy = null } else if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.Truncate(this, [ns, set, beforeNanos, policy], callback) return cmd.execute() } /** * @function Client#udfRegister * * @summary Registers a UDF module with the database cluster. * * @description This method loads a Lua script from the local filesystem into * the Aerospike database cluster and registers it for use as a UDF module. The * client uploads the module to a single cluster node. It then gets distributed * within the whole cluster automatically. The callback function is called once * the initial upload into the cluster has completed (or if an error occurred * during the upload). One of the callback parameters is a {@link UdfJob} * instance that can be used to verify that the module has been registered * successfully on the entire cluster. * * @param {string} path - The file path to the Lua script to load into the server. * @param {number} [udfType] - Language of the UDF script. Lua is the default * and only supported scripting language for UDF modules at the moment; ref. * {@link module:aerospike.language}. * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the * operation completes with the result of the operation. * * @example * * const Aerospike = require('aerospike') * * Aerospike.connect((error, client) => { * if (error) throw error * * var path = './udf/my_module.lua' * client.udfRegister(path, (error, job) => { * if (error) throw error * * job.waitUntilDone(100, (error) => { * if (error) throw error * * // UDF module was successfully registered on all cluster nodes * * client.close() * }) * }) * }) */ Client.prototype.udfRegister = function (udfPath, udfType, policy, callback) { if (typeof udfType === 'function') { callback = udfType udfType = null } else if (typeof policy === 'function') { callback = policy policy = null } if (typeof udfType === 'object') { policy = udfType udfType = null } const cmd = new Commands.UdfRegister(this, [udfPath, udfType, policy], callback) cmd.convertResult = () => { const module = path.basename(udfPath) return new UdfJob(this, module, UdfJob.REGISTER) } return cmd.execute() } /** * @function Client#stats * * @summary Returns runtime stats about the client instance. * * @returns {ClientStats} client stats * * @since v3.8.0 * * @example * require('aerospike').connect().then(client => { * const stats = client.stats() * console.info(stats) // => { commands: { inFlight: 0, queued: 0 }, * // nodes: * // [ { name: 'BB94DC08D270008', * // syncConnections: { inPool: 1, inUse: 0 }, * // asyncConnections: { inPool: 0, inUse: 0 } }, * // { name: 'C1D4DC08D270008', * // syncConnections: { inPool: 0, inUse: 0 }, * // asyncConnections: { inPool: 0, inUse: 0 } } ] } * client.close() * }) */ Client.prototype.stats = function () { return this.as_client.getStats() } /** * @function Client#udfRemove * * @summary Removes a UDF module from the cluster. * * @description The info command to deregister the UDF module is sent to a * single cluster node by the client. It then gets distributed within the whole * cluster automatically. The callback function is called once the initial info * command has succeeded (or if an error occurred). One of the callback * parameters is a {@link UdfJob} instance that can be used to verify that the * module has been removed successfully from the entire cluster. * * For server versions 4.5.0 and before, trying to delete an UDF module that * does not exist on the server, will return an error. Starting with server * version 4.5.1, the server no longer returns an error and the command will * succeed. * * @param {string} udfModule - The basename of the UDF module, without the * local pathname but including the file extension (".lua"). * @param {InfoPolicy} [policy] - The Info Policy to use for this operation. * @param {jobCallback} [callback] - The function to call when the * operation completes which the result of the operation. * * @example * * const Aerospike = require('aerospike') * * Aerospike.connect((error, client) => { * if (error) throw error * * var module = 'my_module.lua' * client.udfRemove(module, (error, job) => { * if (error) throw error * * job.waitUntilDone(100, (error) => { * if (error) throw error * * // UDF module was successfully removed from all cluster nodes * * client.close() * }) * }) * }) */ Client.prototype.udfRemove = function (udfModule, policy, callback) { if (typeof policy === 'function') { callback = policy policy = null } const cmd = new Commands.UdfRemove(this, [udfModule, policy], callback) cmd.convertResult = () => new UdfJob(this, udfModule, UdfJob.UNREGISTER) return cmd.execute() } Client.prototype.updateLogging = function (logConfig) { this.as_client.updateLogging(logConfig) } module.exports = Client