diff --git a/.github/workflows/linux-build.yml b/.github/workflows/linux-build.yml
index 92026c2d..6dfc7812 100644
--- a/.github/workflows/linux-build.yml
+++ b/.github/workflows/linux-build.yml
@@ -29,12 +29,12 @@ jobs:
- 20.x
continue-on-error: true
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
submodules: recursive
- run: ./scripts/build-c-client.sh
- name: Use Node.js ${{ matrix.node-version }}
- uses: actions/setup-node@v2
+ uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
diff --git a/.github/workflows/macOS-build.yml b/.github/workflows/macOS-build.yml
index e4365da8..2df7672d 100644
--- a/.github/workflows/macOS-build.yml
+++ b/.github/workflows/macOS-build.yml
@@ -27,7 +27,7 @@ jobs:
needs: setup
runs-on: macos-latest # https://github.com/actions/virtual-environments
steps:
- - uses: actions/checkout@v2
+ - uses: actions/checkout@v3
with:
submodules: recursive
- run: ./scripts/build-package.sh 1
diff --git a/CHANGELOG.md b/CHANGELOG.md
index baac19b1..c48f8ba6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,31 @@
# Changelog
All notable changes to this project will be documented in this file.
+## [5.6.0]
+
+* **New Features**
+ * [CLIENT-1803] - Added support for creation of a secondary index on elements within a Collection Data Type.
+ * [CLIENT-1990] - Added support for Collection Data Type (CDT) Context Base64 serialization.
+ * [CLIENT-2085] - Added support for rack aware queries and scans.
+ * [CLIENT-2347] - Added the 'replica' property to the QueryPolicy and ScanPolicy Classes.
+ * [CLIENT-2348] - Added filter support for secondary indices on elements within a Collection Data Type.
+
+* **Improvements**
+ * [CLIENT-1823] - Changed the example and parameters in the API Documentation for Client.batchApply.
+ * [CLIENT-2345] - Improved Client.indexRemove unit test by verifying deletion with a query.
+ * [CLIENT-2373] - Modified Query.where() to replace the current filter rather than add a filter to Query.filters.
+ * [CLIENT-2376] - Removed dynamic linking to OpenSSL.
+
+* **Updates**
+ * The typescript description file 'index.d.ts' has not been updated. The next release will update 'index.d.ts' and restore typescript support.
+
## [5.5.0]
* **Breaking Changes**
* [CLIENT-2343] - Dropped support for Node.js 14
* **New Features**
- * [CLIENT-2108] - Added pagination support for queries and scans.
+ * [CLIENT-2108] - Added pagination support for queries and scans. Requires Aerospike Server version 6.0 or above.
* [CLIENT-2224] - Added support for rack aware reads when the replication factor is three.
* [CLIENT-2303] - Added support for Amazon Linux 2023.
* [CLIENT-2342] - Added support for Node.js 20
diff --git a/README.md b/README.md
index 66c9b64a..a1effea2 100644
--- a/README.md
+++ b/README.md
@@ -197,43 +197,72 @@ Before starting with the Aerospike Nodejs Client, verify that you have the follo
- Xcode 5 or greater.
-**Openssl Library**
+**OpenSSL Library**
+OpenSSL is needed to build the Aerospike C Client. If downloading from NPM at version 5.6.0 or later, you will not need to
+have OpenSSL installed to use the Aerospike Nodejs Client. If you are using version 5.5.0 and below, you will need to do
+some additional linking to use the client, which is specified below.
-The below example shows how to install the Openssl library.
+We recommend using brew to install OpenSSL:
```bash
brew install openssl
-unlink /usr/local/opt/openssl
-# Change the below linking based on openssl version and installation path
-ln -s /usr/local/Cellar/openssl@x/y.z/ /usr/local/opt/openssl
```
-If you are looking upgrade switch from openssl@1 or an older version of openssl@3, add these variables to your .bashrc, .profile, or .zshrc file.
+For a Mac using ARM architecture, OpenSSL should be linked as shown below:
+
+```bash
+# When building from source before version 5.6 with openssl@3
+sudo ln -s /opt/homebrew/opt/openssl@3/ /usr/local/opt/openssl;
+# link here if before version 5.6.0
+sudo ln -s /opt/homebrew/opt/openssl@3/ /usr/local/opt/openssl@3;
+
+# When building from source with openssl@1.1
+sudo ln -s /opt/homebrew/opt/openssl@1.1/ /usr/local/opt/openssl;
+# link here if before version 5.6.0 with openssl@1.1
+sudo ln -s /opt/homebrew/opt/openssl@1.1/ /usr/local/opt/openssl@1.1;
+```
+
+For a Mac using X86 architecture, OpenSSL should be linked as shown below:
+
+```bash
+# When building from source before version 5.6 with openssl@1.1
+sudo ln -s /opt/homebrew/opt/openssl@1.1/ /usr/local/opt/openssl;
+# link here if before version 5.6.0 with openssl@1.1
+sudo ln -s /opt/homebrew/opt/openssl@1.1/ /usr/local/opt/openssl@1.1;
+```
+
+Before version 5.6.0, you may need to some add some variables to your profile for OpenSSL to be found.
+
```bash
export PATH="/usr/local/bin/:/usr/local/opt/openssl/bin:$PATH"
export LDFLAGS="-L/usr/local/opt/openssl/lib"
export CPPFLAGS="-I/usr/local/opt/openssl/include"
export EXT_CFLAGS="-I/usr/local/opt/openssl/include"
-```
-
-Afterwards, source the file that was changed and confirm the desired openssl version is installed
-```bash
+# Make sure to source the changes to your shell profile
source ~/.bashrc
source ~/.profile
source ~/.zshrc
-openssl version
```
-For 4x client support, install openssl@1.1 version.
+
+For 4x client support, install OpenSSL@1.1 version.
**LIBUV Library**
-The example below shows how to install the LIBUV library.
+Libuv is needed to build the Aerospike C Client. If downloading from NPM at version 5.6.0 or later, you will not need to
+have Libuv installed to use the Aerospike Nodejs Client. If you are using version 5.5.0 and below, you will need to do
+some additional linking to use the client, which is specified below.
+
+We recommend using brew to install Libuv:
```bash
brew install libuv
-unlink /usr/local/opt/libuv
-# Change the below linking based on libuv version and installation path
-ln -s /usr/local/Cellar/libuv/1.44.1_1/ /usr/local/opt/libuv
+```
+
+For a Mac using ARM architecture, Libuv should be linked as shown below:
+
+```bash
+# When building from source before version 5.6
+sudo ln -s /opt/homebrew/opt/libuv/ /usr/local/opt/libuv;
```
### Git Repository Installations
diff --git a/aerospike-client-c b/aerospike-client-c
index 294084c7..ad90542b 160000
--- a/aerospike-client-c
+++ b/aerospike-client-c
@@ -1 +1 @@
-Subproject commit 294084c753f98d44317b85611915bb43362ed1ca
+Subproject commit ad90542b50c36a31614f31d46207cff0d89ef963
diff --git a/binding.gyp b/binding.gyp
index e94a6884..f1a6456d 100644
--- a/binding.gyp
+++ b/binding.gyp
@@ -151,8 +151,7 @@
['OS=="linux"',{
'libraries': [
'../aerospike-client-c/target/Linux-<(build_arch)/lib/libaerospike.a',
- '-lz',
- '-lssl'
+ '-lz'
],
'defines': [
'AS_USE_LIBUV'
@@ -168,8 +167,7 @@
['OS=="mac"',{
'libraries': [
'../aerospike-client-c/target/Darwin-<(build_arch)/lib/libaerospike.a',
- '-lz',
- '-lssl'
+ '-lz'
],
'defines': [
'AS_USE_LIBUV'
diff --git a/lib/cdt_context.js b/lib/cdt_context.js
index 6a9a6ef8..eb504dde 100644
--- a/lib/cdt_context.js
+++ b/lib/cdt_context.js
@@ -33,6 +33,10 @@ const MAP_RANK = 0x21
const MAP_KEY = 0x22
/** @private **/
const MAP_VALUE = 0x23
+/** @private **/
+const int32Max = 2147483647
+/** @private **/
+const int32Min = -2147483648
/**
* @summary Nested CDT context type.
@@ -63,6 +67,11 @@ class CdtContext {
* @return {CdtContext} Updated CDT context, so calls can be chained.
*/
addListIndex (index) {
+ if (int32Max < index) {
+ throw new Error('index overflow, largest supported integer is ' + int32Max)
+ } else if (int32Min > index) {
+ throw new Error('index underflow, smallest supported integer is ' + int32Min)
+ }
return this.add(LIST_INDEX, index)
}
@@ -82,6 +91,11 @@ class CdtContext {
* @return {CdtContext} Updated CDT context, so calls can be chained.
*/
addListIndexCreate (index, order, pad) {
+ if (int32Max < index) {
+ throw new Error('index overflow, largest supported integer is' + int32Max)
+ } else if (int32Min > index) {
+ throw new Error('index underflow, smallest supported integer is' + int32Min)
+ }
if (order) {
return this.add(LIST_INDEX | 0xc0, index)
} else {
@@ -106,6 +120,11 @@ class CdtContext {
* @return {CdtContext} Updated CDT context, so calls can be chained.
*/
addListRank (rank) {
+ if (int32Max < rank) {
+ throw new Error('rank overflow, largest supported integer is' + int32Max)
+ } else if (int32Min > rank) {
+ throw new Error('rank underflow, smallest supported integer is' + int32Min)
+ }
return this.add(LIST_RANK, rank)
}
@@ -135,6 +154,11 @@ class CdtContext {
* @return {CdtContext} Updated CDT context, so calls can be chained.
*/
addMapIndex (index) {
+ if (int32Max < index) {
+ throw new Error('index overflow, largest supported integer is' + int32Max)
+ } else if (int32Min > index) {
+ throw new Error('index underflow, smallest supported integer is' + int32Min)
+ }
return this.add(MAP_INDEX, index)
}
@@ -151,6 +175,11 @@ class CdtContext {
* @return {CdtContext} Updated CDT context, so calls can be chained.
*/
addMapRank (rank) {
+ if (int32Max < rank) {
+ throw new Error('rank overflow, largest supported integer is' + int32Max)
+ } else if (int32Min > rank) {
+ throw new Error('rank underflow, smallest supported integer is' + int32Min)
+ }
return this.add(MAP_RANK, rank)
}
diff --git a/lib/client.js b/lib/client.js
index 8917cc47..f4962898 100644
--- a/lib/client.js
+++ b/lib/client.js
@@ -22,6 +22,7 @@ const EventEmitter = require('events')
const as = require('bindings')('aerospike.node')
const AerospikeError = require('./error')
+const Context = require('./cdt_context')
const Commands = require('./commands')
const Config = require('./config')
const EventLoop = require('./event_loop')
@@ -210,6 +211,95 @@ Client.prototype.getNodes = function () {
return this.as_client.getNodes()
}
+/**
+ * @function Client#contextToBase64
+ *
+ * @summary Returns a serialized CDT Context
+ *
+ * @param {Object} context - {@link CdtContext}
+ *
+ * @return {String} serialized context - base64 representation of the CDT Context
+ *
+ * @since v5.6.0
+ *
+ * @example
How to use CDT context serialization
+ *
+ * const Aerospike = require('aerospike');
+ * const Context = Aerospike.cdt.Context
+ * // Define host configuration
+ * let config = {
+ * hosts: '192.168.33.10:3000',
+ * policies: {
+ * operate : new Aerospike.OperatePolicy({socketTimeout : 0, totalTimeout : 0}),
+ * write : new Aerospike.WritePolicy({socketTimeout : 0, totalTimeout : 0}),
+ * read : new Aerospike.ReadPolicy({socketTimeout : 0, totalTimeout : 0})
+ * }
+ * }
+ *
+ *
+ * Aerospike.connect(config, async (error, client) => {
+ * // Create a context
+ * let context = new Context().addMapKey('nested')
+ *
+ * // Create keys for records to be written
+ * let recordKey = new Aerospike.Key('test', 'demo', 'record')
+ * let contextKey = new Aerospike.Key('test', 'demo', 'context')
+ *
+ * // Put record with a CDT
+ * await client.put(recordKey, {exampleBin: {nested: {food: 'blueberry', drink: 'koolaid'}}})
+ *
+ * // Test the context with client.operate()
+ * var ops = [
+ * Aerospike.maps.getByKey('exampleBin', 'food', Aerospike.maps.returnType.KEY_VALUE).withContext(context)
+ * ]
+ * let results = await client.operate(recordKey, ops)
+ * console.log(results.bins.exampleBin) // [ 'food', 'blueberry' ]
+ *
+ * // Serialize CDT Context
+ * let serializedContext = client.contextToBase64(context)
+ *
+ * // Put record with bin containing the serialized record
+ * await client.put(contextKey, {context: serializedContext})
+ *
+ * // Get context when needed for operation
+ * let contextRecord = await client.get(contextKey)
+ *
+ * // Deserialize CDT Context
+ * context = client.contextFromBase64(contextRecord.bins.context)
+ *
+ * // Test the context with client.operate()
+ * ops = [
+ * Aerospike.maps.getByKey('exampleBin', 'food', Aerospike.maps.returnType.KEY_VALUE).withContext(context)
+ * ]
+ * results = await client.operate(recordKey, ops)
+ * console.log(results.bins.exampleBin) // [ 'food', 'blueberry' ]
+ *
+ * // Close the client
+ * client.close()
+ * })
+ */
+Client.prototype.contextToBase64 = function (context) {
+ return this.as_client.contextToBase64({ context })
+}
+
+/**
+ * @function Client#contextFromBase64
+ *
+ * @summary Returns a deserialized CDT Context
+ *
+ * @param {String} serializedContext - base64 serialized {@link CdtContext}
+ *
+ * @return {CdtContext} Deserialized CDT Context
+ *
+ * @since v5.6.0
+ *
+ */
+Client.prototype.contextFromBase64 = function (serializedContext) {
+ const context = new Context()
+ context.items = this.as_client.contextFromBase64({ context: serializedContext })
+ return context
+}
+
/**
* @function Client#addSeedHost
*
@@ -579,9 +669,7 @@ Client.prototype.batchWrite = function (records, policy, callback) {
* 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 {Key[]} keys - An array of keys, used to locate the records in the cluster.
* @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.
@@ -595,6 +683,8 @@ Client.prototype.batchWrite = function (records, policy, callback) {
*
* @example
* const Aerospike = require('aerospike')
+ * const batchType = Aerospike.batchType;
+ *
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* const config = {
* hosts: '192.168.33.10:3000',
@@ -604,52 +694,34 @@ Client.prototype.batchWrite = function (records, policy, callback) {
* }
* }
*
- * 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(config, (error, client) => {
- * if (error) throw error
- * client.batchApply(batchRecords, (error, results) => {
- * if (error) throw error
- * results.forEach(function (result) {
- * console.log(result)
- * })
- * })
- * })
+ * // Create batch of keys
+ * let keys = [];
+ * for(i = 0; i < 10; i++){
+ * keys.push(new Aerospike.Key('sandbox', 'ufodata', i + 1));
+ * }
+ *
+ * ;(async () => {
+ * // Establishes a connection to the server
+ * let client = await Aerospike.connect(config);
+ *
+ * // Execute the UDF
+ * let batchResult = await client.batchApply(batchRecords,
+ * {
+ * module: 'example',
+ * funcname: 'getDaysBetween',
+ * args: ['occurred', 'posted']
+ * }
+ * );
+ *
+ * // Access the records
+ * batchResult.forEach(result => {
+ * // Do something
+ * console.info("%o days between occurrence and post", result.record.bins.SUCCESS);
+ * });
+ *
+ * // Close the connection to the server
+ * client.close();
+ * })();
*/
Client.prototype.batchApply = function (records, udf, batchPolicy, batchApplyPolicy, callback) {
if (typeof batchPolicy === 'function') {
@@ -993,6 +1065,7 @@ Client.prototype.connect = function (callback) {
* 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 {Object} options.context - The {@link CdtContext} on which the index is to be created.
* @param {InfoPolicy} [policy] - The Info Policy to use for this operation.
* @param {jobCallback} [callback] - The function to call when the operation completes.
*
@@ -1008,6 +1081,7 @@ Client.prototype.connect = function (callback) {
* @example
*
* const Aerospike = require('aerospike')
+ * const Context = Aerospike.cdt.Context
*
* // INSERT HOSTNAME AND PORT NUMBER OF AEROSPIKE SERVER NODE HERE!
* var config = {
@@ -1024,12 +1098,15 @@ Client.prototype.connect = function (callback) {
* let indexName = 'recentLocationsIdx'
* let indexType = Aerospike.indexType.LIST
* let dataType = Aerospike.indexDataType.GEO2DSPHERE
+ * let context = new Context().addListIndex(0)
* let options = { ns: namespace,
* set: set,
* bin: binName,
* index: indexName,
* type: indexType,
- * datatype: dataType }
+ * datatype: dataType,
+ * context: context }
+ *
* let policy = new Aerospike.InfoPolicy({ timeout: 100 })
*
* client.createIndex(options, policy, (error, job) => {
@@ -1058,6 +1135,7 @@ Client.prototype.createIndex = function (options, policy, callback) {
options.index,
options.type || as.indexType.DEFAULT,
options.datatype,
+ { context: options.context },
policy
]
diff --git a/lib/filter.js b/lib/filter.js
index 9152ca94..0c820786 100644
--- a/lib/filter.js
+++ b/lib/filter.js
@@ -30,10 +30,11 @@ const GeoJSON = require('./geojson')
* @example
*
* const Aerospike = require('aerospike')
+ * const Context = Aerospike.cdt.Context
*
* Aerospike.connect().then(async (client) => {
* // find any records that have a recent location within 1000m radius of the specified coordinates
- * let geoFilter = Aerospike.filter.geoWithinRadius('recent', 103.8, 1.305, 1000, Aerospike.indexType.LIST)
+ * let geoFilter = Aerospike.filter.geoWithinRadius('recent', 103.8, 1.305, 1000, Aerospike.indexType.LIST, new Context().addListIndex(0))
* let query = client.query('test', 'demo')
* query.where(geoFilter)
*
@@ -53,11 +54,12 @@ const GeoJSON = require('./geojson')
* module:aerospike/filter} module.
*/
class SindexFilterPredicate {
- constructor (predicate, bin, dataType, indexType, props) {
+ constructor (predicate, bin, dataType, indexType, context, props) {
this.predicate = predicate
this.bin = bin
this.datatype = dataType
this.type = indexType || as.indexType.DEFAULT
+ this.context = context
if (props) {
Object.assign(this, props)
}
@@ -66,16 +68,16 @@ class SindexFilterPredicate {
exports.SindexFilterPredicate = SindexFilterPredicate
class EqualPredicate extends SindexFilterPredicate {
- constructor (bin, value, dataType, indexType) {
- super(as.predicates.EQUAL, bin, dataType, indexType, {
+ constructor (bin, value, dataType, indexType, context) {
+ super(as.predicates.EQUAL, bin, dataType, indexType, context, {
val: value
})
}
}
class RangePredicate extends SindexFilterPredicate {
- constructor (bin, min, max, dataType, indexType) {
- super(as.predicates.RANGE, bin, dataType, indexType, {
+ constructor (bin, min, max, dataType, indexType, context) {
+ super(as.predicates.RANGE, bin, dataType, indexType, context, {
min,
max
})
@@ -83,8 +85,8 @@ class RangePredicate extends SindexFilterPredicate {
}
class GeoPredicate extends SindexFilterPredicate {
- constructor (bin, value, indexType) {
- super(as.predicates.RANGE, bin, as.indexDataType.GEO2DSPHERE, indexType, {
+ constructor (bin, value, indexType, context) {
+ super(as.predicates.RANGE, bin, as.indexDataType.GEO2DSPHERE, indexType, context, {
val: value
})
}
@@ -124,12 +126,13 @@ function dataTypeOf (value) {
* @param {number} max - Upper end of the range (inclusive).
* @param {number} [indexType=Aerospike.indexType.DEFAULT] - One of {@link
* module:aerospike.indexType}, i.e. LIST or MAPVALUES.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*/
-exports.range = function (bin, min, max, indexType) {
+exports.range = function (bin, min, max, indexType, context) {
const dataType = as.indexDataType.NUMERIC
- return new RangePredicate(bin, min, max, dataType, indexType)
+ return new RangePredicate(bin, min, max, dataType, indexType, context)
}
/**
@@ -157,14 +160,15 @@ exports.equal = function (bin, value) {
* list or map in the bin.
* @param {number} indexType - One of {@link module:aerospike.indexType},
* i.e. LIST, MAPVALUES or MAPKEYS.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*
* @since v2.0
*/
-exports.contains = function (bin, value, indexType) {
+exports.contains = function (bin, value, indexType, context) {
const dataType = dataTypeOf(value)
- return new EqualPredicate(bin, value, dataType, indexType)
+ return new EqualPredicate(bin, value, dataType, indexType, context)
}
/**
@@ -178,18 +182,19 @@ exports.contains = function (bin, value, indexType) {
* @param {GeoJSON} value - GeoJSON region value.
* @param {number} [indexType=Aerospike.indexType.DEFAULT] - One of {@link
* module:aerospike.indexType}, i.e. LIST or MAPVALUES.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*
* @since v2.0
*/
-exports.geoWithinGeoJSONRegion = function (bin, value, indexType) {
+exports.geoWithinGeoJSONRegion = function (bin, value, indexType, context) {
if (value instanceof GeoJSON) {
value = value.toString()
} else if (typeof value === 'object') {
value = JSON.stringify(value)
}
- return new GeoPredicate(bin, value, indexType)
+ return new GeoPredicate(bin, value, indexType, context)
}
/**
@@ -203,18 +208,19 @@ exports.geoWithinGeoJSONRegion = function (bin, value, indexType) {
* @param {GeoJSON} value - GeoJSON point value.
* @param {number} [indexType=Aerospike.indexType.DEFAULT] - One of {@link
* module:aerospike.indexType}, i.e. LIST or MAPVALUES.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*
* @since v2.0
*/
-exports.geoContainsGeoJSONPoint = function (bin, value, indexType) {
+exports.geoContainsGeoJSONPoint = function (bin, value, indexType, context) {
if (value instanceof GeoJSON) {
value = value.toString()
} else if (typeof value === 'object') {
value = JSON.stringify(value)
}
- return new GeoPredicate(bin, value, indexType)
+ return new GeoPredicate(bin, value, indexType, context)
}
/**
@@ -230,14 +236,15 @@ exports.geoContainsGeoJSONPoint = function (bin, value, indexType) {
* @param {number} radius - Radius in meters.
* @param {number} [indexType=Aerospike.indexType.DEFAULT] - One of {@link
* module:aerospike.indexType}, i.e. LIST or MAPVALUES.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*
* @since v2.0
*/
-exports.geoWithinRadius = function (bin, lon, lat, radius, indexType) {
+exports.geoWithinRadius = function (bin, lon, lat, radius, indexType, context) {
const circle = GeoJSON.Circle(lon, lat, radius)
- return new GeoPredicate(bin, circle.toString(), indexType)
+ return new GeoPredicate(bin, circle.toString(), indexType, context)
}
/**
@@ -252,12 +259,13 @@ exports.geoWithinRadius = function (bin, lon, lat, radius, indexType) {
* @param {number} lat - Latitude of the point.
* @param {number} [indexType=Aerospike.indexType.DEFAULT] - One of {@link
* module:aerospike.indexType}, i.e. LIST or MAPVALUES.
+ * @param {Object} context - The {@link CdtContext} of the index.
* @returns {module:aerospike/filter~SindexFilterPredicate} SI
* filter predicate, that can be applied to queries using {@link Query#where}.
*
* @since v2.0
*/
-exports.geoContainsPoint = function (bin, lon, lat, indexType) {
+exports.geoContainsPoint = function (bin, lon, lat, indexType, context) {
const point = GeoJSON.Point(lon, lat)
- return new GeoPredicate(bin, point.toString(), indexType)
+ return new GeoPredicate(bin, point.toString(), indexType, context)
}
diff --git a/lib/policies/query_policy.js b/lib/policies/query_policy.js
index 1e6f0d86..bc9da5a8 100644
--- a/lib/policies/query_policy.js
+++ b/lib/policies/query_policy.js
@@ -34,6 +34,14 @@ class QueryPolicy extends BasePolicy {
props = props || {}
super(props)
+ /**
+ * Specifies the replica to be consulted for the query operation.
+ *
+ * @type number
+ * @see {@link module:aerospike/policy.replica} for supported policy values.
+ */
+ this.replica = props.replica
+
/**
* Should CDT data types (Lists / Maps) be deserialized to JS data types
* (Arrays / Objects) or returned as raw bytes (Buffer).
diff --git a/lib/policies/scan_policy.js b/lib/policies/scan_policy.js
index ee8f03bf..7488f3c4 100644
--- a/lib/policies/scan_policy.js
+++ b/lib/policies/scan_policy.js
@@ -34,6 +34,14 @@ class ScanPolicy extends BasePolicy {
props = props || {}
super(props)
+ /**
+ * Specifies the replica to be consulted for the scan operation.
+ *
+ * @type number
+ * @see {@link module:aerospike/policy.replica} for supported policy values.
+ */
+ this.replica = props.replica
+
/**
* Specifies whether a {@link
* http://www.aerospike.com/docs/guide/durable_deletes.html|tombstone}
diff --git a/lib/query.js b/lib/query.js
index e8b047a2..2344cf19 100644
--- a/lib/query.js
+++ b/lib/query.js
@@ -473,20 +473,6 @@ Query.prototype.select = function (bins) {
* })
*
* @see {@link module:aerospike/filter} to create SI filters.
- *
- * const Aerospike = require('aerospike')
- *
- * Aerospike.connect().then(client => {
- * let query = client.query('test', 'demo')
- *
- * let tagsFilter = Aerospike.filter.contains('tags', 'blue', Aerospike.indexType.LIST)
- * query.where(tagsFilter)
- *
- * let stream = query.foreach()
- * stream.on('data', record => { console.info(record.bins.tags) })
- * stream.on('error', error => { throw error })
- * stream.on('end', () => client.close())
- * })
*/
Query.prototype.where = function (indexFilter) {
if (indexFilter instanceof filter.SindexFilterPredicate) {
@@ -497,7 +483,7 @@ Query.prototype.where = function (indexFilter) {
}
Query.prototype.setSindexFilter = function (sindexFilter) {
- this.filters = this.filters || []
+ this.filters = []
this.filters.push(sindexFilter)
}
@@ -586,6 +572,7 @@ Query.prototype.foreach = function (policy, dataCb, errorCb, endCb) {
if (this.paginate) {
args.push(this.queryState)
args.push(this.maxRecords)
+ args.push(this.filters[0].context ? { context: this.filters[0].context } : null)
cmd = new Commands.QueryPages(stream, args)
} else {
cmd = new Commands.Query(stream, args)
diff --git a/package-lock.json b/package-lock.json
index 7a47961b..84789e21 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "aerospike",
- "version": "5.5.0",
+ "version": "5.6.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "aerospike",
- "version": "5.5.0",
+ "version": "5.6.0",
"cpu": [
"x64",
"arm64"
@@ -60,42 +60,42 @@
}
},
"node_modules/@babel/code-frame": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.21.4.tgz",
- "integrity": "sha512-LYvhNKfwWSPpocw8GI7gpK2nq3HSDuEPC/uSYaALSJu9xjsalaaYFOq0Pwt5KmVqwEbZlDu81aLXwBOmD/Fv9g==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.5.tgz",
+ "integrity": "sha512-Xmwn266vad+6DAqEB2A6V/CcZVp62BbwVmcOJc2RPuwih1kw02TjQvWVWlcKGbBPd+8/0V5DEkOcizRGYsspYQ==",
"dev": true,
"dependencies": {
- "@babel/highlight": "^7.18.6"
+ "@babel/highlight": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/compat-data": {
- "version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.9.tgz",
- "integrity": "sha512-FUGed8kfhyWvbYug/Un/VPJD41rDIgoVVcR+FuzhzOYyRz5uED+Gd3SLZml0Uw2l2aHFb7ZgdW5mGA3G2cCCnQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.22.5.tgz",
+ "integrity": "sha512-4Jc/YuIaYqKnDDz892kPIledykKg12Aw1PYX5i/TY28anJtacvM1Rrr8wbieB9GfEJwlzqT0hUEao0CxEebiDA==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/core": {
- "version": "7.21.8",
- "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.21.8.tgz",
- "integrity": "sha512-YeM22Sondbo523Sz0+CirSPnbj9bG3P0CdHcBZdqUuaeOaYEFbOLoGU7lebvGP6P5J/WE9wOn7u7C4J9HvS1xQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.22.5.tgz",
+ "integrity": "sha512-SBuTAjg91A3eKOvD+bPEz3LlhHZRNu1nFOVts9lzDJTXshHTjII0BAtDS3Y2DAkdZdDKWVZGVwkDfc4Clxn1dg==",
"dev": true,
"dependencies": {
"@ampproject/remapping": "^2.2.0",
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.21.5",
- "@babel/helper-compilation-targets": "^7.21.5",
- "@babel/helper-module-transforms": "^7.21.5",
- "@babel/helpers": "^7.21.5",
- "@babel/parser": "^7.21.8",
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.5",
- "@babel/types": "^7.21.5",
+ "@babel/code-frame": "^7.22.5",
+ "@babel/generator": "^7.22.5",
+ "@babel/helper-compilation-targets": "^7.22.5",
+ "@babel/helper-module-transforms": "^7.22.5",
+ "@babel/helpers": "^7.22.5",
+ "@babel/parser": "^7.22.5",
+ "@babel/template": "^7.22.5",
+ "@babel/traverse": "^7.22.5",
+ "@babel/types": "^7.22.5",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.2",
@@ -120,12 +120,12 @@
}
},
"node_modules/@babel/generator": {
- "version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.21.9.tgz",
- "integrity": "sha512-F3fZga2uv09wFdEjEQIJxXALXfz0+JaOb7SabvVMmjHxeVTuGW8wgE8Vp1Hd7O+zMTYtcfEISGRzPkeiaPPsvg==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.22.5.tgz",
+ "integrity": "sha512-+lcUbnTRhd0jOewtFSedLyiPsD5tswKkbgcezOqqWFUVNEwoUTlpPOBmvhG7OXWLR4jMdv0czPGH5XbflnD1EA==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.21.5",
+ "@babel/types": "^7.22.5",
"@jridgewell/gen-mapping": "^0.3.2",
"@jridgewell/trace-mapping": "^0.3.17",
"jsesc": "^2.5.1"
@@ -135,13 +135,13 @@
}
},
"node_modules/@babel/helper-compilation-targets": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.21.5.tgz",
- "integrity": "sha512-1RkbFGUKex4lvsB9yhIfWltJM5cZKUftB2eNajaDv3dCMEp49iBG0K14uH8NnX9IPux2+mK7JGEOB0jn48/J6w==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.5.tgz",
+ "integrity": "sha512-Ji+ywpHeuqxB8WDxraCiqR0xfhYjiDE/e6k7FuIaANnoOFxAHskHChz4vA1mJC9Lbm01s1PVAGhQY4FUKSkGZw==",
"dev": true,
"dependencies": {
- "@babel/compat-data": "^7.21.5",
- "@babel/helper-validator-option": "^7.21.0",
+ "@babel/compat-data": "^7.22.5",
+ "@babel/helper-validator-option": "^7.22.5",
"browserslist": "^4.21.3",
"lru-cache": "^5.1.1",
"semver": "^6.3.0"
@@ -178,142 +178,142 @@
"dev": true
},
"node_modules/@babel/helper-environment-visitor": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.21.5.tgz",
- "integrity": "sha512-IYl4gZ3ETsWocUWgsFZLM5i1BYx9SoemminVEXadgLBa9TdeorzgLKm8wWLA6J1N/kT3Kch8XIk1laNzYoHKvQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.5.tgz",
+ "integrity": "sha512-XGmhECfVA/5sAt+H+xpSg0mfrHq6FzNr9Oxh7PSEBBRUb/mL7Kz3NICXb194rCqAEdxkhPT1a88teizAFyvk8Q==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-function-name": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.21.0.tgz",
- "integrity": "sha512-HfK1aMRanKHpxemaY2gqBmL04iAPOPRj7DxtNbiDOrJK+gdwkiNRVpCpUJYbUT+aZyemKN8brqTOxzCaG6ExRg==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.22.5.tgz",
+ "integrity": "sha512-wtHSq6jMRE3uF2otvfuD3DIvVhOsSNshQl0Qrd7qC9oQJzHvOL4qQXlQn2916+CXGywIjpGuIkoyZRRxHPiNQQ==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.20.7",
- "@babel/types": "^7.21.0"
+ "@babel/template": "^7.22.5",
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-hoist-variables": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz",
- "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz",
+ "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-imports": {
- "version": "7.21.4",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.21.4.tgz",
- "integrity": "sha512-orajc5T2PsRYUN3ZryCEFeMDYwyw09c/pZeaQEZPH0MpKzSvn3e0uXsDBu3k03VI+9DBiRo+l22BfKTpKwa/Wg==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.5.tgz",
+ "integrity": "sha512-8Dl6+HD/cKifutF5qGd/8ZJi84QeAKh+CEe1sBzz8UayBBGg1dAIJrdHOcOM5b2MpzWL2yuotJTtGjETq0qjXg==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.21.4"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-module-transforms": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.21.5.tgz",
- "integrity": "sha512-bI2Z9zBGY2q5yMHoBvJ2a9iX3ZOAzJPm7Q8Yz6YeoUjU/Cvhmi2G4QyTNyPBqqXSgTjUxRg3L0xV45HvkNWWBw==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.22.5.tgz",
+ "integrity": "sha512-+hGKDt/Ze8GFExiVHno/2dvG5IdstpzCq0y4Qc9OJ25D4q3pKfiIP/4Vp3/JvhDkLKsDK2api3q3fpIgiIF5bw==",
"dev": true,
"dependencies": {
- "@babel/helper-environment-visitor": "^7.21.5",
- "@babel/helper-module-imports": "^7.21.4",
- "@babel/helper-simple-access": "^7.21.5",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/helper-validator-identifier": "^7.19.1",
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.5",
- "@babel/types": "^7.21.5"
+ "@babel/helper-environment-visitor": "^7.22.5",
+ "@babel/helper-module-imports": "^7.22.5",
+ "@babel/helper-simple-access": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
+ "@babel/template": "^7.22.5",
+ "@babel/traverse": "^7.22.5",
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-simple-access": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.21.5.tgz",
- "integrity": "sha512-ENPDAMC1wAjR0uaCUwliBdiSl1KBJAVnMTzXqi64c2MG8MPR6ii4qf7bSXDqSFbr4W6W028/rf5ivoHop5/mkg==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
+ "integrity": "sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.21.5"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-split-export-declaration": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz",
- "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.5.tgz",
+ "integrity": "sha512-thqK5QFghPKWLhAV321lxF95yCg2K3Ob5yw+M3VHWfdia0IkPXUtoLH8x/6Fh486QUvzhb8YOWHChTVen2/PoQ==",
"dev": true,
"dependencies": {
- "@babel/types": "^7.18.6"
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-string-parser": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.21.5.tgz",
- "integrity": "sha512-5pTUx3hAJaZIdW99sJ6ZUUgWq/Y+Hja7TowEnLNMm1VivRgZQL3vpBY3qUACVsvw+yQU6+YgfBVmcbLaZtrA1w==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz",
+ "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.19.1",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
- "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.5.tgz",
+ "integrity": "sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helper-validator-option": {
- "version": "7.21.0",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.21.0.tgz",
- "integrity": "sha512-rmL/B8/f0mKS2baE9ZpyTcTavvEuWhTTW8amjzXNvYG4AwBsqTLikfXsEofsJEfKHf+HQVQbFOHy6o+4cnC/fQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.22.5.tgz",
+ "integrity": "sha512-R3oB6xlIVKUnxNUxbmgq7pKjxpru24zlimpE8WK47fACIlM0II/Hm1RS8IaOI7NgCr6LNS+jl5l75m20npAziw==",
"dev": true,
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/helpers": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.21.5.tgz",
- "integrity": "sha512-BSY+JSlHxOmGsPTydUkPf1MdMQ3M81x5xGCOVgWM3G8XH77sJ292Y2oqcp0CbbgxhqBuI46iUz1tT7hqP7EfgA==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.22.5.tgz",
+ "integrity": "sha512-pSXRmfE1vzcUIDFQcSGA5Mr+GxBV9oiRKDuDxXvWQQBCh8HoIjs/2DlDB7H8smac1IVrB9/xdXj2N3Wol9Cr+Q==",
"dev": true,
"dependencies": {
- "@babel/template": "^7.20.7",
- "@babel/traverse": "^7.21.5",
- "@babel/types": "^7.21.5"
+ "@babel/template": "^7.22.5",
+ "@babel/traverse": "^7.22.5",
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/highlight": {
- "version": "7.18.6",
- "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz",
- "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.5.tgz",
+ "integrity": "sha512-BSKlD1hgnedS5XRnGOljZawtag7H1yPfQp0tdNJCHoH6AZ+Pcm9VvkrK59/Yy593Ypg0zMxH2BxD1VPYUQ7UIw==",
"dev": true,
"dependencies": {
- "@babel/helper-validator-identifier": "^7.18.6",
+ "@babel/helper-validator-identifier": "^7.22.5",
"chalk": "^2.0.0",
"js-tokens": "^4.0.0"
},
@@ -322,9 +322,9 @@
}
},
"node_modules/@babel/parser": {
- "version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.21.9.tgz",
- "integrity": "sha512-q5PNg/Bi1OpGgx5jYlvWZwAorZepEudDMCLtj967aeS7WMont7dUZI46M2XwcIQqvUlMxWfdLFu4S/qSxeUu5g==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.22.5.tgz",
+ "integrity": "sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==",
"dev": true,
"bin": {
"parser": "bin/babel-parser.js"
@@ -334,33 +334,33 @@
}
},
"node_modules/@babel/template": {
- "version": "7.21.9",
- "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.21.9.tgz",
- "integrity": "sha512-MK0X5k8NKOuWRamiEfc3KEJiHMTkGZNUjzMipqCGDDc6ijRl/B7RGSKVGncu4Ro/HdyzzY6cmoXuKI2Gffk7vQ==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.5.tgz",
+ "integrity": "sha512-X7yV7eiwAxdj9k94NEylvbVHLiVG1nvzCV2EAowhxLTwODV1jl9UzZ48leOC0sH7OnuHrIkllaBgneUykIcZaw==",
"dev": true,
"dependencies": {
- "@babel/code-frame": "^7.21.4",
- "@babel/parser": "^7.21.9",
- "@babel/types": "^7.21.5"
+ "@babel/code-frame": "^7.22.5",
+ "@babel/parser": "^7.22.5",
+ "@babel/types": "^7.22.5"
},
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/traverse": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.21.5.tgz",
- "integrity": "sha512-AhQoI3YjWi6u/y/ntv7k48mcrCXmus0t79J9qPNlk/lAsFlCiJ047RmbfMOawySTHtywXhbXgpx/8nXMYd+oFw==",
- "dev": true,
- "dependencies": {
- "@babel/code-frame": "^7.21.4",
- "@babel/generator": "^7.21.5",
- "@babel/helper-environment-visitor": "^7.21.5",
- "@babel/helper-function-name": "^7.21.0",
- "@babel/helper-hoist-variables": "^7.18.6",
- "@babel/helper-split-export-declaration": "^7.18.6",
- "@babel/parser": "^7.21.5",
- "@babel/types": "^7.21.5",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.22.5.tgz",
+ "integrity": "sha512-7DuIjPgERaNo6r+PZwItpjCZEa5vyw4eJGufeLxrPdBXBoLcCJCIasvK6pK/9DVNrLZTLFhUGqaC6X/PA007TQ==",
+ "dev": true,
+ "dependencies": {
+ "@babel/code-frame": "^7.22.5",
+ "@babel/generator": "^7.22.5",
+ "@babel/helper-environment-visitor": "^7.22.5",
+ "@babel/helper-function-name": "^7.22.5",
+ "@babel/helper-hoist-variables": "^7.22.5",
+ "@babel/helper-split-export-declaration": "^7.22.5",
+ "@babel/parser": "^7.22.5",
+ "@babel/types": "^7.22.5",
"debug": "^4.1.0",
"globals": "^11.1.0"
},
@@ -369,13 +369,13 @@
}
},
"node_modules/@babel/types": {
- "version": "7.21.5",
- "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.21.5.tgz",
- "integrity": "sha512-m4AfNvVF2mVC/F7fDEdH2El3HzUg9It/XsCxZiOTTA3m3qYfcSVSbTfM6Q9xG+hYDniZssYhlXKKUMD5m8tF4Q==",
+ "version": "7.22.5",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.22.5.tgz",
+ "integrity": "sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==",
"dev": true,
"dependencies": {
- "@babel/helper-string-parser": "^7.21.5",
- "@babel/helper-validator-identifier": "^7.19.1",
+ "@babel/helper-string-parser": "^7.22.5",
+ "@babel/helper-validator-identifier": "^7.22.5",
"to-fast-properties": "^2.0.0"
},
"engines": {
@@ -943,9 +943,9 @@
"dev": true
},
"node_modules/browserslist": {
- "version": "4.21.5",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz",
- "integrity": "sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==",
+ "version": "4.21.9",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.21.9.tgz",
+ "integrity": "sha512-M0MFoZzbUrRU4KNfCrDLnvyE7gub+peetoTid3TBIqtunaDJyXlwhakT+/VkvSXcfIzFfK/nkCs4nmyTmxdNSg==",
"dev": true,
"funding": [
{
@@ -955,13 +955,17 @@
{
"type": "tidelift",
"url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
}
],
"dependencies": {
- "caniuse-lite": "^1.0.30001449",
- "electron-to-chromium": "^1.4.284",
- "node-releases": "^2.0.8",
- "update-browserslist-db": "^1.0.10"
+ "caniuse-lite": "^1.0.30001503",
+ "electron-to-chromium": "^1.4.431",
+ "node-releases": "^2.0.12",
+ "update-browserslist-db": "^1.0.11"
},
"bin": {
"browserslist": "cli.js"
@@ -1045,9 +1049,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001489",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001489.tgz",
- "integrity": "sha512-x1mgZEXK8jHIfAxm+xgdpHpk50IN3z3q3zP261/WS+uvePxW8izXuCu6AHz0lkuYTlATDehiZ/tNyYBdSQsOUQ==",
+ "version": "1.0.30001506",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001506.tgz",
+ "integrity": "sha512-6XNEcpygZMCKaufIcgpQNZNf00GEqc7VQON+9Rd0K1bMYo8xhMZRAo5zpbnbMNizi4YNgIDAFrdykWsvY3H4Hw==",
"dev": true,
"funding": [
{
@@ -1377,9 +1381,9 @@
}
},
"node_modules/electron-to-chromium": {
- "version": "1.4.405",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.405.tgz",
- "integrity": "sha512-JdDgnwU69FMZURoesf9gNOej2Cms1XJFfLk24y1IBtnAdhTcJY/mXnokmpmxHN59PcykBP4bgUU98vLY44Lhuw==",
+ "version": "1.4.436",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.436.tgz",
+ "integrity": "sha512-aktOxo8fnrMC8vOIBMVS3PXbT1nrPQ+SouUuN7Y0a+Rw3pOMrvIV92Ybnax7x4tugA+ZpYA5fOHTby7ama8OQQ==",
"dev": true
},
"node_modules/emoji-regex": {
@@ -5191,9 +5195,9 @@
"dev": true
},
"node_modules/semver": {
- "version": "7.5.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.1.tgz",
- "integrity": "sha512-Wvss5ivl8TMRZXXESstBA4uR5iXgEN/VC5/sOcuXdVLzcdkz4HWetIoRfG5gb5X+ij/G9rw9YoGn3QoQ8OCSpw==",
+ "version": "7.5.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz",
+ "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==",
"dependencies": {
"lru-cache": "^6.0.0"
},
diff --git a/package.json b/package.json
index 7efe612c..2f3a2cb3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "aerospike",
- "version": "5.5.0",
+ "version": "5.6.0",
"description": "Aerospike Client Library",
"keywords": [
"aerospike",
@@ -40,7 +40,6 @@
"scripts": {
"preinstall": "npm install @mapbox/node-pre-gyp",
"install": "node-pre-gyp install --fallback-to-build",
- "postinstall": "node scripts/postinstall.js",
"test": "mocha",
"test-noserver": "GLOBAL_CLIENT=false mocha -g '#noserver'",
"lint": "standard",
@@ -81,7 +80,8 @@
"ignore": [
"apidocs",
"tmp-*.js",
- "/*.js"
+ "/*.js",
+ "libuv-v*"
]
},
"files": [
diff --git a/scripts/build-c-client.sh b/scripts/build-c-client.sh
index e85a04f5..48dbbd8c 100755
--- a/scripts/build-c-client.sh
+++ b/scripts/build-c-client.sh
@@ -25,15 +25,11 @@
CWD=$(pwd)
SCRIPT_DIR=$(dirname $0)
BASE_DIR=$(cd "${SCRIPT_DIR}/.."; pwd)
-SETUP=$1
. ${SCRIPT_DIR}/build-commands.sh
-if [ -n "$1" ]; then setup
-fi
download_libuv
rebuild_libuv
-check_libuv
rebuild_c_client
diff --git a/scripts/build-commands.sh b/scripts/build-commands.sh
index 5ca06a8b..adf39dd2 100755
--- a/scripts/build-commands.sh
+++ b/scripts/build-commands.sh
@@ -28,10 +28,10 @@ BASE_DIR=$(cd "${SCRIPT_DIR}/.."; pwd)
AEROSPIKE_C_HOME=${CWD}/aerospike-client-c
OS_FLAVOR=linux
AEROSPIKE_NODEJS_RELEASE_HOME=${CWD}/lib/binding
-LIBUV_VERSION=1.8.0
+LIBUV_VERSION=1.45.0
LIBUV_DIR=libuv-v${LIBUV_VERSION}
LIBUV_TAR=${LIBUV_DIR}.tar.gz
-LIBUV_URL=http://dist.libuv.org/dist/v1.8.0/${LIBUV_TAR}
+LIBUV_URL=http://dist.libuv.org/dist/v1.45.0/${LIBUV_TAR}
LIBUV_ABS_DIR=${CWD}/${LIBUV_DIR}
LIBUV_BUILD=0
build_arch=$(uname -m)
@@ -113,35 +113,6 @@ rebuild_libuv() {
fi
}
-check_libuv() {
-
- cd ${CWD}
-
- printf "\n" >&1
-
- if [ $LIBUV_BUILD -eq 1 ]; then
- if [ -f ${LIBUV_LIBRARY} ]; then
- printf " [✓] %s\n" "${LIBUV_LIBRARY}" >&1
- else
- printf " [✗] %s\n" "${LIBUV_LIBRARY}" >&1
- FAILED=1
- fi
- fi
-
- if [ -f ${LIBUV_INCLUDE_DIR}/uv.h ]; then
- printf " [✓] %s\n" "${LIBUV_INCLUDE_DIR}/uv.h" >&1
- else
- printf " [✗] %s\n" "${LIBUV_INCLUDE_DIR}/uv.h" >&1
- FAILED=1
- fi
-
- printf "\n" >&1
-
- if [ $FAILED ]; then
- exit 1
- fi
-}
-
rebuild_c_client() {
# if [ ! -f ${AEROSPIKE_LIBRARY} ]; then
cd ${AEROSPIKE_C_HOME}
@@ -151,29 +122,6 @@ rebuild_c_client() {
# fi
}
-setup() {
- if [[ "$OSTYPE" == "darwin"* ]]; then
- # # install xcode CLI
- # xcode-select —-install
- # Check for Homebrew to be present, install if it's missing
- if test ! $(which brew); then
- echo "Installing homebrew..."
- ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- fi
- brew update
- PACKAGES=(
- openssl
- )
- echo "Installing packages..."
- brew install ${PACKAGES[@]}
- # link openssl
- unlink /usr/local/opt/openssl
- ln -s /usr/local/Cellar/openssl@3/*/ /usr/local/opt/openssl
- export LDFLAGS="-L/usr/local/opt/openssl@3/lib"
- export CPPFLAGS="-I/usr/local/opt/openssl@3/include"
- fi
-}
-
check_aerospike() {
cd ${CWD}
@@ -208,6 +156,5 @@ perform_check() {
printf "\n" >&1
printf "CHECK\n" >&1
- check_libuv
check_aerospike
}
diff --git a/scripts/build-package.sh b/scripts/build-package.sh
index b4e02ee6..1795ea88 100755
--- a/scripts/build-package.sh
+++ b/scripts/build-package.sh
@@ -44,11 +44,8 @@ build_nodejs_client() {
configure_nvm
-if [ -n "$1" ]; then setup
-fi
download_libuv
rebuild_libuv
-check_libuv
rebuild_c_client
diff --git a/scripts/postinstall.js b/scripts/postinstall.js
deleted file mode 100644
index 1489e3d5..00000000
--- a/scripts/postinstall.js
+++ /dev/null
@@ -1,12 +0,0 @@
-const fs = require('fs')
-
-function createDir (folder) {
- if (!fs.existsSync(folder)) {
- fs.mkdirSync(folder, { recursive: true })
-
- console.log('Folder Created Successfully: ' + folder)
- }
-}
-
-createDir('./lib/binding/openssl@1')
-createDir('./lib/binding/openssl@3')
diff --git a/scripts/prebuiltBinding.js b/scripts/prebuiltBinding.js
old mode 100755
new mode 100644
index 3a1e179f..5bd14e3b
--- a/scripts/prebuiltBinding.js
+++ b/scripts/prebuiltBinding.js
@@ -1,73 +1,73 @@
-const { exec } = require('child_process')
const fs = require('fs')
-exec('openssl version', (error, stdout, stderr) => {
- if (error) throw error
- exec('uname -s', (error, OS, stderr) => {
- if (error) throw error
- exec('uname -m', (error, arch, stderr) => {
- if (error) throw error
- const openssl = stdout.split(' ')
- OS = OS.trim()
- arch = arch.trim()
- const dict = {
- v19: 'v111',
- v18: 'v108',
- v16: 'v93',
- v14: 'v83',
- Linux: 'linux',
- Darwin: 'darwin',
- arm64: 'arm64',
- x86_64: 'x64',
- aarch64: 'arm64'
- }
- if ((openssl[1][0] === '3') && (openssl[0] !== 'LibreSSL')) {
- console.log('lib/binding/openssl@3/node-v111-' + dict[OS] + '-' + dict[arch])
- fs.rename('lib/binding/openssl@3/node-v111-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v111-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@3/node-v108-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v108-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@3/node-v93-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v93-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@3/node-v115-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v115-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) return
- fs.rm('lib/binding/openssl@3', { recursive: true, force: true }, (e) => {
- if (e) throw e
- })
- fs.rm('lib/binding/openssl@1', { recursive: true, force: true }, (e) => {
- if (e) throw e
- })
- })
- })
- })
- })
- } else {
- fs.rename('lib/binding/openssl@1/node-v111-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v111-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@1/node-v108-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v108-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@1/node-v93-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v93-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) throw err
- fs.rename('lib/binding/openssl@1/node-v115-' + dict[OS] + '-' + dict[arch],
- 'lib/binding/node-v115-' + dict[OS] + '-' + dict[arch], (err) => {
- if (err) return
- fs.rm('lib/binding/openssl@3', { recursive: true, force: true }, (e) => {
- if (e) throw e
- })
- fs.rm('lib/binding/openssl@1', { recursive: true, force: true }, (e) => {
- if (e) throw e
- })
- })
- })
- })
- })
- }
- })
- })
-})
+const util = require('util')
+const exec = util.promisify(require('child_process').exec)
+const rename = util.promisify(fs.rename)
+const rm = util.promisify(fs.rm)
+const dict = {
+ Linux: 'linux',
+ Darwin: 'darwin',
+ arm64: 'arm64',
+ x86_64: 'x64',
+ aarch64: 'arm64'
+}
+
+;(async function () {
+ let output = await exec('uname -s')
+ const os = output.stdout.trim()
+ output = await exec('uname -m')
+ const arch = output.stdout.trim()
+ output = await exec('openssl version')
+ const version = output.stdout
+ const openssl = version.split(' ')[1].slice(0, 1)
+ const deleteList = []
+ if (dict[os] === 'linux') {
+ deleteList.push('lib/binding/node-v115-darwin-arm64')
+ deleteList.push('lib/binding/node-v111-darwin-arm64')
+ deleteList.push('lib/binding/node-v108-darwin-arm64')
+ deleteList.push('lib/binding/node-v93-darwin-arm64')
+ deleteList.push('lib/binding/node-v115-darwin-x64')
+ deleteList.push('lib/binding/node-v111-darwin-x64')
+ deleteList.push('lib/binding/node-v108-darwin-x64')
+ deleteList.push('lib/binding/node-v93-darwin-x64')
+
+ if (dict[arch] === 'arm64') {
+ await rename('lib/binding/openssl@' + openssl + '/node-v115-linux-arm64', 'lib/binding/node-v115-linux-arm64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v111-linux-arm64', 'lib/binding/node-v111-linux-arm64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v108-linux-arm64', 'lib/binding/node-v108-linux-arm64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v93-linux-arm64', 'lib/binding/node-v93-linux-arm64')
+ } else {
+ await rename('lib/binding/openssl@' + openssl + '/node-v115-linux-x64', 'lib/binding/node-v115-linux-x64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v111-linux-x64', 'lib/binding/node-v111-linux-x64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v108-linux-x64', 'lib/binding/node-v108-linux-x64')
+ await rename('lib/binding/openssl@' + openssl + '/node-v93-linux-x64', 'lib/binding/node-v93-linux-x64')
+ }
+ await rm('lib/binding/openssl@3', { recursive: true, force: true })
+ await rm('lib/binding/openssl@1', { recursive: true, force: true })
+ await rm(deleteList[0], { recursive: true, force: true })
+ await rm(deleteList[1], { recursive: true, force: true })
+ await rm(deleteList[2], { recursive: true, force: true })
+ await rm(deleteList[3], { recursive: true, force: true })
+ await rm(deleteList[4], { recursive: true, force: true })
+ await rm(deleteList[5], { recursive: true, force: true })
+ await rm(deleteList[6], { recursive: true, force: true })
+ await rm(deleteList[7], { recursive: true, force: true })
+ } else {
+ if (dict[arch] === 'arm64') {
+ deleteList.push('lib/binding/node-v115-darwin-x64')
+ deleteList.push('lib/binding/node-v111-darwin-x64')
+ deleteList.push('lib/binding/node-v108-darwin-x64')
+ deleteList.push('lib/binding/node-v93-darwin-x64')
+ } else {
+ deleteList.push('lib/binding/node-v115-darwin-arm64')
+ deleteList.push('lib/binding/node-v111-darwin-arm64')
+ deleteList.push('lib/binding/node-v108-darwin-arm64')
+ deleteList.push('lib/binding/node-v93-darwin-arm64')
+ }
+ await rm('lib/binding/openssl@3', { recursive: true, force: true })
+ await rm('lib/binding/openssl@1', { recursive: true, force: true })
+ await rm(deleteList[0], { recursive: true, force: true })
+ await rm(deleteList[1], { recursive: true, force: true })
+ await rm(deleteList[2], { recursive: true, force: true })
+ await rm(deleteList[3], { recursive: true, force: true })
+ }
+})()
diff --git a/src/include/client.h b/src/include/client.h
index de89af4a..6fdb82e9 100644
--- a/src/include/client.h
+++ b/src/include/client.h
@@ -83,6 +83,8 @@ class AerospikeClient : public Nan::ObjectWrap {
static NAN_METHOD(BatchApply);
static NAN_METHOD(BatchRemove);
static NAN_METHOD(BatchSelect);
+ static NAN_METHOD(ContextFromBase64);
+ static NAN_METHOD(ContextToBase64);
static NAN_METHOD(Close);
static NAN_METHOD(Connect);
static NAN_METHOD(ExistsAsync);
diff --git a/src/include/operations.h b/src/include/operations.h
index 1d41eafa..4bccec84 100644
--- a/src/include/operations.h
+++ b/src/include/operations.h
@@ -44,6 +44,7 @@ int get_optional_cdt_context(as_cdt_ctx *context, bool *has_context,
as_cdt_ctx* get_optional_cdt_context_heap(int * rc,
v8::Local obj, const char *prop,
const LogInfo *log);
+int get_v8_cdt_context(as_cdt_ctx *context, v8::Local items);
v8::Local scalar_opcode_values();
v8::Local list_opcode_values();
diff --git a/src/include/query.h b/src/include/query.h
index d715bce8..f097a4fa 100644
--- a/src/include/query.h
+++ b/src/include/query.h
@@ -34,8 +34,9 @@ struct query_udata {
void setup_query(as_query *query, v8::Local ns,
v8::Local set, v8::Local maybe_options,
- LogInfo *log);
-void setup_options(as_query *query, v8::Local options, LogInfo *log);
+ as_cdt_ctx* context, bool* with_context, LogInfo *log);
+void setup_options(as_query *query, v8::Local options, as_cdt_ctx* context, bool* with_context, LogInfo *log);
void setup_query_pages(as_query ** query, v8::Local ns, v8::Local set,
- v8::Localmaybe_options, uint8_t* bytes, uint32_t bytes_size, LogInfo *log);
+ v8::Localmaybe_options, uint8_t* bytes, uint32_t bytes_size,
+ as_cdt_ctx* context, bool* with_context, LogInfo *log);
void free_query(as_query *query, as_policy_query *policy);
diff --git a/src/main/async.cc b/src/main/async.cc
index 41419e33..1c449256 100644
--- a/src/main/async.cc
+++ b/src/main/async.cc
@@ -195,7 +195,7 @@ bool async_scan_pages_listener(as_error *err, as_record *record, void *udata,
query_bytes_to_jsobject(bytes, bytes_size, log),
Nan::Null()};
- cmd->Callback(4, {argv});
+ cmd->Callback(4, argv);
as_scan_destroy(scan);
free(bytes);
delete cmd;
@@ -253,7 +253,7 @@ bool async_query_pages_listener(as_error *err, as_record *record, void *udata,
query_bytes_to_jsobject(bytes, bytes_size, log),
Nan::Null()};
- cmd->Callback(4, {argv});
+ cmd->Callback(4, argv);
free_query(query, NULL);
free(bytes);
delete cmd;
diff --git a/src/main/cdt_ctx.cc b/src/main/cdt_ctx.cc
index 23811f61..3d4657af 100644
--- a/src/main/cdt_ctx.cc
+++ b/src/main/cdt_ctx.cc
@@ -26,6 +26,83 @@ extern "C" {
using namespace v8;
+NAN_METHOD(AerospikeClient::ContextToBase64)
+{
+ TYPE_CHECK_REQ(info[0], IsObject, "Context must be an object");
+
+ as_cdt_ctx context;
+ bool has_context = false;
+
+ if (info[0]->IsObject()) {
+ get_optional_cdt_context(&context, &has_context, info[0].As(), "context", NULL);
+ }
+
+ if(has_context){
+ uint32_t capacity = as_cdt_ctx_base64_capacity(&context);
+ char* serializedContext = new char[capacity];
+ as_cdt_ctx_to_base64(&context, serializedContext, capacity);;
+ as_cdt_ctx_destroy(&context);
+ info.GetReturnValue().Set(Nan::New(serializedContext).ToLocalChecked());
+ delete [] serializedContext;
+ }
+ else{
+ Nan::ThrowError("Context is invalid, cannot serialize");
+ }
+
+}
+
+NAN_METHOD(AerospikeClient::ContextFromBase64)
+{
+ TYPE_CHECK_REQ(info[0], IsObject, "Serialized context must be an object");
+
+ char* serializedContext = NULL;
+ if (info[0]->IsObject()) {
+ if(get_string_property(&serializedContext, info[0].As(), "context", NULL) != AS_NODE_PARAM_OK){
+ Nan::ThrowError("Serialized context is invalid");
+ return;
+ }
+ }
+
+ as_cdt_ctx context;
+ as_cdt_ctx_from_base64(&context, serializedContext);
+ Local v8_items = Nan::New(context.list.size);
+ get_v8_cdt_context(&context, v8_items);
+ cf_free(serializedContext);
+ as_cdt_ctx_destroy(&context);
+ info.GetReturnValue().Set(v8_items);
+
+}
+
+int get_v8_cdt_context(as_cdt_ctx *context, Local items)
+{
+ Nan::HandleScope scope;
+ for(uint32_t i = 0; i < context->list.size; i++){
+
+ as_cdt_ctx_item* item = (as_cdt_ctx_item*) as_vector_get(&context->list, i);
+ Local v8Item = Nan::New(2);
+
+ if((item->type & 0xF) > 0x1){
+ Nan::Set(v8Item, 0, Nan::New(item->type));
+ Nan::Set(v8Item, 1, val_to_jsvalue(item->val.pval, NULL));
+
+ Nan::Set(items, i, v8Item);
+ }
+ else{
+ Nan::Set(v8Item, 0, Nan::New(item->type));
+ //First 31 bits mask
+ int32_t ival = item->val.ival & 0x7FFFFFFF;
+ //Signed bit mask
+ if(item->val.ival & 0x8000000000000000){
+ ival = ival | 0x80000000;
+ }
+ Nan::Set(v8Item, 1, Nan::New(ival));
+
+ Nan::Set(items, i, v8Item);
+ }
+ }
+ return AS_NODE_PARAM_OK;
+}
+
int get_optional_cdt_context(as_cdt_ctx *context, bool *has_context,
Local obj, const char *prop,
const LogInfo *log)
diff --git a/src/main/client.cc b/src/main/client.cc
index b382e805..3dee670b 100644
--- a/src/main/client.cc
+++ b/src/main/client.cc
@@ -289,6 +289,8 @@ void AerospikeClient::Init()
Nan::SetPrototypeMethod(tpl, "batchApply", BatchApply);
Nan::SetPrototypeMethod(tpl, "batchRemove", BatchRemove);
Nan::SetPrototypeMethod(tpl, "batchSelect", BatchSelect);
+ Nan::SetPrototypeMethod(tpl, "contextFromBase64", ContextFromBase64);
+ Nan::SetPrototypeMethod(tpl, "contextToBase64", ContextToBase64);
Nan::SetPrototypeMethod(tpl, "close", Close);
Nan::SetPrototypeMethod(tpl, "connect", Connect);
Nan::SetPrototypeMethod(tpl, "existsAsync", ExistsAsync);
diff --git a/src/main/commands/index_create.cc b/src/main/commands/index_create.cc
index cfa5dfd4..9f0d0987 100644
--- a/src/main/commands/index_create.cc
+++ b/src/main/commands/index_create.cc
@@ -20,6 +20,7 @@
#include "conversions.h"
#include "policy.h"
#include "log.h"
+#include "operations.h"
extern "C" {
#include
@@ -42,6 +43,8 @@ class IndexCreateCommand : public AerospikeCommand {
cf_free(policy);
if (index != NULL)
free(index);
+ if (with_context)
+ as_cdt_ctx_destroy(&context);
}
as_index_task task;
@@ -52,6 +55,8 @@ class IndexCreateCommand : public AerospikeCommand {
char *index = NULL;
as_index_type itype;
as_index_datatype dtype;
+ as_cdt_ctx context;
+ bool with_context;
};
static void *prepare(const Nan::FunctionCallbackInfo &info)
@@ -60,7 +65,7 @@ static void *prepare(const Nan::FunctionCallbackInfo &info)
AerospikeClient *client =
Nan::ObjectWrap::Unwrap(info.This());
IndexCreateCommand *cmd =
- new IndexCreateCommand(client, info[7].As());
+ new IndexCreateCommand(client, info[8].As());
LogInfo *log = client->log;
if (as_strlcpy(cmd->ns, *Nan::Utf8String(info[0].As()),
@@ -90,8 +95,16 @@ static void *prepare(const Nan::FunctionCallbackInfo &info)
cmd->dtype = (as_index_datatype)Nan::To(info[5]).FromJust();
if (info[6]->IsObject()) {
+ if (get_optional_cdt_context(&cmd->context, &cmd->with_context, info[6].As(), "context", log) !=
+ AS_NODE_PARAM_OK) {
+ return CmdSetError(cmd, AEROSPIKE_ERR_PARAM,
+ "Context parameter is invalid");
+ }
+ }
+
+ if (info[7]->IsObject()) {
cmd->policy = (as_policy_info *)cf_malloc(sizeof(as_policy_info));
- if (infopolicy_from_jsobject(cmd->policy, info[6].As(), log) !=
+ if (infopolicy_from_jsobject(cmd->policy, info[7].As(), log) !=
AS_NODE_PARAM_OK) {
return CmdSetError(cmd, AEROSPIKE_ERR_PARAM,
"Policy parameter is invalid");
@@ -115,9 +128,9 @@ static void execute(uv_work_t *req)
"index=%s, type=%d, datatype=%d",
cmd->ns, cmd->set, cmd->bin, cmd->index, cmd->itype,
cmd->dtype);
- aerospike_index_create_complex(cmd->as, &cmd->err, &cmd->task, cmd->policy,
+ aerospike_index_create_ctx(cmd->as, &cmd->err, &cmd->task, cmd->policy,
cmd->ns, cmd->set, cmd->bin, cmd->index,
- cmd->itype, cmd->dtype);
+ cmd->itype, cmd->dtype, cmd->with_context ? &cmd->context : NULL);
}
static void respond(uv_work_t *req, int status)
@@ -144,8 +157,9 @@ NAN_METHOD(AerospikeClient::IndexCreate)
TYPE_CHECK_REQ(info[3], IsString, "Index name must be a string");
TYPE_CHECK_OPT(info[4], IsNumber, "Index type must be an integer");
TYPE_CHECK_REQ(info[5], IsNumber, "Index datatype must be an integer");
- TYPE_CHECK_OPT(info[6], IsObject, "Policy must be an object");
- TYPE_CHECK_REQ(info[7], IsFunction, "Callback must be a function");
+ TYPE_CHECK_OPT(info[6], IsObject, "Context must be an object");
+ TYPE_CHECK_OPT(info[7], IsObject, "Policy must be an object");
+ TYPE_CHECK_REQ(info[8], IsFunction, "Callback must be a function");
async_invoke(info, prepare, execute, respond);
}
diff --git a/src/main/commands/query_apply.cc b/src/main/commands/query_apply.cc
index f54b28d7..3268e257 100644
--- a/src/main/commands/query_apply.cc
+++ b/src/main/commands/query_apply.cc
@@ -46,11 +46,16 @@ class QueryApplyCommand : public AerospikeCommand {
cf_free(policy);
if (val != NULL)
cf_free(val);
+ if(with_context){
+ as_cdt_ctx_destroy(&context);
+ }
}
as_policy_query *policy = NULL;
as_query query;
as_val *val = NULL;
+ as_cdt_ctx context;
+ bool with_context = false;
};
static bool query_foreach_callback(const as_val *val, void *udata)
@@ -71,7 +76,7 @@ static void *prepare(const Nan::FunctionCallbackInfo &info)
new QueryApplyCommand(client, info[4].As());
LogInfo *log = client->log;
- setup_query(&cmd->query, info[0], info[1], info[2], log);
+ setup_query(&cmd->query, info[0], info[1], info[2], &cmd->context, &cmd->with_context, log);
if (info[3]->IsObject()) {
cmd->policy = (as_policy_query *)cf_malloc(sizeof(as_policy_query));
diff --git a/src/main/commands/query_async.cc b/src/main/commands/query_async.cc
index 60b6d6f0..2599baec 100644
--- a/src/main/commands/query_async.cc
+++ b/src/main/commands/query_async.cc
@@ -53,8 +53,10 @@ NAN_METHOD(AerospikeClient::QueryAsync)
as_status status;
as_partition_filter pf;
bool pf_defined = false;
+ as_cdt_ctx context;
+ bool with_context = false;
- setup_query(&query, info[0], info[1], info[2], log);
+ setup_query(&query, info[0], info[1], info[2], &context, &with_context, log);
if (info[3]->IsObject()) {
if (querypolicy_from_jsobject(&policy, info[3].As(), log) !=
@@ -95,4 +97,8 @@ NAN_METHOD(AerospikeClient::QueryAsync)
Cleanup:
delete cmd;
free_query(&query, p_policy);
+ if(with_context){
+ as_cdt_ctx_destroy(&context);
+ }
+
}
diff --git a/src/main/commands/query_background.cc b/src/main/commands/query_background.cc
index f8e31f45..54f096b6 100644
--- a/src/main/commands/query_background.cc
+++ b/src/main/commands/query_background.cc
@@ -44,11 +44,16 @@ class QueryBackgroundCommand : public AerospikeCommand {
free_query(&query, NULL);
if (policy != NULL)
cf_free(policy);
+ if(with_context){
+ as_cdt_ctx_destroy(&context);
+ }
}
as_policy_write *policy = NULL;
uint64_t query_id = 0;
as_query query;
+ as_cdt_ctx context;
+ bool with_context = false;
};
static void *prepare(const Nan::FunctionCallbackInfo &info)
@@ -59,7 +64,8 @@ static void *prepare(const Nan::FunctionCallbackInfo &info)
new QueryBackgroundCommand(client, info[5].As());
LogInfo *log = client->log;
- setup_query(&cmd->query, info[0], info[1], info[2], log);
+
+ setup_query(&cmd->query, info[0], info[1], info[2], &cmd->context, &cmd->with_context, log);
if (info[3]->IsObject()) {
cmd->policy = (as_policy_write *)cf_malloc(sizeof(as_policy_write));
diff --git a/src/main/commands/query_foreach.cc b/src/main/commands/query_foreach.cc
index df68f53c..5e440f1d 100644
--- a/src/main/commands/query_foreach.cc
+++ b/src/main/commands/query_foreach.cc
@@ -54,6 +54,9 @@ class QueryForeachCommand : public AerospikeCommand {
as_queue_mt_destroy(results);
results = NULL;
}
+ if(with_context){
+ as_cdt_ctx_destroy(&context);
+ }
}
as_policy_query *policy = NULL;
@@ -62,6 +65,8 @@ class QueryForeachCommand : public AerospikeCommand {
uint32_t max_q_size;
uint32_t signal_interval = 0;
uv_async_t async_handle;
+ as_cdt_ctx context;
+ bool with_context = false;
};
// Push the record from the server to a queue.
@@ -151,7 +156,7 @@ static void *prepare(const Nan::FunctionCallbackInfo &info)
new QueryForeachCommand(client, info[4].As());
LogInfo *log = client->log;
- setup_query(&cmd->query, info[0], info[1], info[2], log);
+ setup_query(&cmd->query, info[0], info[1], info[2], &cmd->context, &cmd->with_context, log);
if (info[3]->IsObject()) {
cmd->policy = (as_policy_query *)cf_malloc(sizeof(as_policy_query));
diff --git a/src/main/commands/query_pages.cc b/src/main/commands/query_pages.cc
index f0755513..3d4335b2 100644
--- a/src/main/commands/query_pages.cc
+++ b/src/main/commands/query_pages.cc
@@ -21,6 +21,7 @@
#include "policy.h"
#include "log.h"
#include "query.h"
+#include "operations.h"
extern "C" {
#include
@@ -40,13 +41,14 @@ NAN_METHOD(AerospikeClient::QueryPages)
TYPE_CHECK_OPT(info[2], IsObject, "Options must be an object");
TYPE_CHECK_OPT(info[3], IsObject, "Policy must be an object");
TYPE_CHECK_OPT(info[4], IsObject, "saved_query must be an object");
- TYPE_CHECK_OPT(info[5], IsNumber, "max_records must be an object");
- TYPE_CHECK_REQ(info[6], IsFunction, "Callback must be a function");
+ TYPE_CHECK_OPT(info[5], IsNumber, "max_records must be a number");
+ TYPE_CHECK_OPT(info[6], IsObject, "context must be an object");
+ TYPE_CHECK_REQ(info[7], IsFunction, "Callback must be a function");
AerospikeClient *client =
Nan::ObjectWrap::Unwrap(info.This());
AsyncCommand *cmd =
- new AsyncCommand("Query", client, info[6].As());
+ new AsyncCommand("Query", client, info[7].As());
LogInfo *log = client->log;
as_policy_query* p_policy = NULL;
@@ -54,23 +56,36 @@ NAN_METHOD(AerospikeClient::QueryPages)
as_partition_filter pf;
bool pf_defined = false;
as_status status;
+ as_cdt_ctx context;
+ bool with_context = false;
struct query_udata* qu = (query_udata*) cf_malloc(sizeof(struct query_udata));
qu->cmd = cmd;
qu->count = 0;
+ if (info[6]->IsObject()) {
+ if (get_optional_cdt_context(&context, &with_context, info[6].As(), "context", log) !=
+ AS_NODE_PARAM_OK) {
+ as_v8_error(log, "Parsing context arguments for query index filter failed");
+ Nan::ThrowTypeError("Error in filter context");
+ }
+ }
+
if (info[4]->IsObject()) {
uint32_t bytes_size = 0;
load_bytes_size(info[4].As(), &bytes_size, log);
uint8_t* bytes = new uint8_t[bytes_size];
load_bytes(info[4].As(), bytes, bytes_size, log);
- setup_query_pages(&qu->query, info[0], info[1], Nan::Null(), bytes, bytes_size, log);
- delete bytes;
+ setup_query_pages(&qu->query, info[0], info[1], Nan::Null(), bytes, bytes_size, &context, &with_context, log);
+ delete [] bytes;
}
else{
- setup_query_pages(&qu->query, info[0], info[1], info[2], NULL, 0, log);
+ setup_query_pages(&qu->query, info[0], info[1], info[2], NULL, 0, &context, &with_context, log);
}
+ if(with_context) {
+ qu->query->where.entries[0].ctx = &context;
+ }
if (info[3]->IsObject()) {
if (querypolicy_from_jsobject(&policy, info[3].As(), log) !=
@@ -120,5 +135,8 @@ NAN_METHOD(AerospikeClient::QueryPages)
if (p_policy && policy.base.filter_exp) {
as_exp_destroy(policy.base.filter_exp);
}
+ if(with_context) {
+ as_cdt_ctx_destroy(&context);
+ }
}
\ No newline at end of file
diff --git a/src/main/commands/scan_pages.cc b/src/main/commands/scan_pages.cc
index faf85161..503aa423 100644
--- a/src/main/commands/scan_pages.cc
+++ b/src/main/commands/scan_pages.cc
@@ -68,7 +68,7 @@ NAN_METHOD(AerospikeClient::ScanPages)
uint8_t* bytes = new uint8_t[bytes_size];
load_bytes(info[5].As(), bytes, bytes_size, log);
setup_scan_pages(&su->scan, info[0], info[1], Nan::Null(), bytes, bytes_size, log);
- delete bytes;
+ delete [] bytes;
}
else{
setup_scan_pages(&su->scan, info[0], info[1], info[2], NULL, 0, log);
diff --git a/src/main/policy.cc b/src/main/policy.cc
index 4bb80082..93477256 100644
--- a/src/main/policy.cc
+++ b/src/main/policy.cc
@@ -546,6 +546,11 @@ int querypolicy_from_jsobject(as_policy_query *policy, Local obj,
AS_NODE_PARAM_OK) {
return rc;
}
+ if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
+ obj, "replica", log)) !=
+ AS_NODE_PARAM_OK) {
+ return rc;
+ }
if ((rc = get_optional_uint32_property((uint32_t *)&policy->info_timeout,
NULL, obj, "infoTimeout", log)) !=
AS_NODE_PARAM_OK) {
@@ -600,6 +605,11 @@ int scanpolicy_from_jsobject(as_policy_scan *policy, Local obj,
AS_NODE_PARAM_OK) {
return rc;
}
+ if ((rc = get_optional_uint32_property((uint32_t *)&policy->replica, NULL,
+ obj, "replica", log)) !=
+ AS_NODE_PARAM_OK) {
+ return rc;
+ }
as_v8_detail(log, "Parsing scan policy: success");
return AS_NODE_PARAM_OK;
}
diff --git a/src/main/query.cc b/src/main/query.cc
index 64af4442..779d14fd 100644
--- a/src/main/query.cc
+++ b/src/main/query.cc
@@ -31,7 +31,7 @@ extern "C" {
using namespace v8;
void setup_query(as_query *query, Local ns, Local set,
- Local maybe_options, LogInfo *log)
+ Local maybe_options, as_cdt_ctx* context, bool* with_context, LogInfo *log)
{
as_namespace as_ns = {'\0'};
as_set as_set = {'\0'};
@@ -50,18 +50,18 @@ void setup_query(as_query *query, Local ns, Local set,
// TODO: Return param error
}
}
-
as_query_init(query, as_ns, as_set);
if (!maybe_options->IsObject()) {
return;
}
- setup_options(query, maybe_options.As(), log);
+
+ setup_options(query, maybe_options.As(), context, with_context, log);
}
-void setup_options(as_query *query, Local options, LogInfo *log)
+void setup_options(as_query *query, Local options, as_cdt_ctx* context, bool* with_context, LogInfo *log)
{
Local filters_val =
@@ -76,6 +76,14 @@ void setup_options(as_query *query, Local options, LogInfo *log)
for (int i = 0; i < size; i++) {
Local filter =
Nan::Get(filters, i).ToLocalChecked().As();
+ if(!(*with_context)){
+ if (get_optional_cdt_context(context, with_context, filter, "context", log) !=
+ AS_NODE_PARAM_OK) {
+ as_v8_error(log, "Parsing context arguments for query index filter failed");
+ Nan::ThrowTypeError("Error in filter context");
+ }
+ }
+
Local bin =
Nan::Get(filter, Nan::New("bin").ToLocalChecked())
.ToLocalChecked();
@@ -115,7 +123,7 @@ void setup_options(as_query *query, Local options, LogInfo *log)
if (v8min->IsNumber() && v8max->IsNumber()) {
const int64_t min = Nan::To(v8min).FromJust();
const int64_t max = Nan::To(v8max).FromJust();
- as_query_where(query, bin_name, predicate, type,
+ as_query_where_with_ctx(query, bin_name, *with_context ? context : NULL, predicate, type,
datatype, min, max);
as_v8_debug(log,
"Integer range predicate from %llu to %llu",
@@ -140,7 +148,7 @@ void setup_options(as_query *query, Local options, LogInfo *log)
"The region value passed is not a GeoJSON string");
}
const char *bin_val = strdup(*Nan::Utf8String(value));
- as_query_where(query, bin_name, predicate, type, datatype,
+ as_query_where_with_ctx(query, bin_name, *with_context ? context : NULL, predicate, type, datatype,
bin_val);
as_v8_debug(log, "Geo range predicate %s", bin_val);
}
@@ -153,7 +161,7 @@ void setup_options(as_query *query, Local options, LogInfo *log)
.ToLocalChecked();
if (value->IsNumber()) {
const int64_t val = Nan::To(value).FromJust();
- as_query_where(query, bin_name, predicate, type,
+ as_query_where_with_ctx(query, bin_name, *with_context ? context : NULL, predicate, type,
datatype, val);
as_v8_debug(log, "Integer equality predicate %d", val);
}
@@ -175,7 +183,7 @@ void setup_options(as_query *query, Local options, LogInfo *log)
"predicate - value is not a string");
}
const char *bin_val = strdup(*Nan::Utf8String(value));
- as_query_where(query, bin_name, predicate, type, datatype,
+ as_query_where_with_ctx(query, bin_name, *with_context ? context : NULL, predicate, type, datatype,
bin_val);
as_v8_debug(log, "String equality predicate %s", bin_val);
}
@@ -253,7 +261,8 @@ void setup_options(as_query *query, Local options, LogInfo *log)
}
void setup_query_pages(as_query** query, Local ns, Local set,
- Local maybe_options, uint8_t* bytes, uint32_t bytes_size, LogInfo *log)
+ Local maybe_options, uint8_t* bytes, uint32_t bytes_size,
+ as_cdt_ctx* context, bool* with_context, LogInfo *log)
{
as_namespace as_ns = {'\0'};
as_set as_set = {'\0'};
@@ -285,7 +294,7 @@ void setup_query_pages(as_query** query, Local ns, Local set,
return;
}
- setup_options(*query, maybe_options.As(), log);
+ setup_options(*query, maybe_options.As(), context, with_context, log);
}
diff --git a/test/cdt_context.js b/test/cdt_context.js
index 3bed967a..278e56dc 100644
--- a/test/cdt_context.js
+++ b/test/cdt_context.js
@@ -16,7 +16,7 @@
'use strict'
-/* eslint-env mocha */
+/* global expect, describe, it */
const Aerospike = require('../lib/aerospike')
const helper = require('./test_helper')
@@ -49,6 +49,12 @@ describe('Aerospike.cdt.Context', function () {
.then(assertResultEql({ nested: 5 }))
.then(cleanup)
})
+ it('Throws an error when index is too large', function () {
+ expect(() => new Context().addListIndex(2147483648)).to.throw(Error)
+ })
+ it('Throws an error when index is too small', function () {
+ expect(() => new Context().addListIndex(-2147483649)).to.throw(Error)
+ })
})
describe('Context.addListIndexCreate', function () {
@@ -127,6 +133,12 @@ describe('Aerospike.cdt.Context', function () {
.then(assertError(status.ERR_OP_NOT_APPLICABLE))
.then(cleanup)
})
+ it('Throws an error when index is too large', function () {
+ expect(() => new Context().addListIndexCreate(2147483648)).to.throw(Error)
+ })
+ it('Throws an error when index is too small', function () {
+ expect(() => new Context().addListIndexCreate(-2147483649)).to.throw(Error)
+ })
})
describe('Context.addListRank', function () {
@@ -139,6 +151,12 @@ describe('Aerospike.cdt.Context', function () {
.then(assertResultEql({ nested: 3 }))
.then(cleanup)
})
+ it('Throws an error when rank is too large', function () {
+ expect(() => new Context().addListRank(2147483648)).to.throw(Error)
+ })
+ it('Throws an error when rank is too small', function () {
+ expect(() => new Context().addListRank(-2147483649)).to.throw(Error)
+ })
})
describe('Context.addListValue', function () {
@@ -163,6 +181,12 @@ describe('Aerospike.cdt.Context', function () {
.then(assertResultEql({ nested: 3 }))
.then(cleanup)
})
+ it('Throws an error when index is too large', function () {
+ expect(() => new Context().addMapIndex(2147483648)).to.throw(Error)
+ })
+ it('Throws an error when index is too small', function () {
+ expect(() => new Context().addMapIndex(-2147483649)).to.throw(Error)
+ })
})
describe('Context.addMapRank', function () {
@@ -175,6 +199,12 @@ describe('Aerospike.cdt.Context', function () {
.then(assertResultEql({ nested: 3 }))
.then(cleanup)
})
+ it('Throws an error when rank is too large', function () {
+ expect(() => new Context().addMapRank(2147483648)).to.throw(Error)
+ })
+ it('Throws an error when rank is too small', function () {
+ expect(() => new Context().addMapRank(-2147483649)).to.throw(Error)
+ })
})
describe('Context.addMapKey', function () {
diff --git a/test/client.js b/test/client.js
index 3ecb9577..5cf7d11e 100644
--- a/test/client.js
+++ b/test/client.js
@@ -20,6 +20,7 @@
const Aerospike = require('../lib/aerospike')
const Client = Aerospike.Client
+const Context = Aerospike.cdt.Context
const helper = require('./test_helper')
const keygen = helper.keygen
@@ -152,6 +153,74 @@ describe('Client', function () {
})
})
+ describe('Client#contextToBase64', function () {
+ const client = helper.client
+ const context = new Context().addMapKey('nested')
+ it('Serializes a CDT context', function () {
+ expect(typeof client.contextToBase64(context)).to.equal('string')
+ })
+ it('Throws an error if no context is given', function () {
+ expect(() => { client.contextToBase64() }).to.throw(Error)
+ })
+ it('Throws an error if a non-object is given', function () {
+ expect(() => { client.contextToBase64('test') }).to.throw(Error)
+ })
+ })
+
+ describe('Client#contextFromBase64', function () {
+ const client = helper.client
+ const addListIndex = new Context().addListIndex(5)
+ const addListIndexCreate = new Context().addListIndexCreate(45, Aerospike.lists.order.KEY_ORDERED, true)
+ const addListRank = new Context().addListRank(15)
+ const addListValueString = new Context().addListValue('apple')
+ const addListValueInt = new Context().addListValue(4500)
+ const addMapIndex = new Context().addMapIndex(10)
+ const addMapRank = new Context().addMapRank(11)
+ const addMapKey = new Context().addMapKey('nested')
+ const addMapKeyCreate = new Context().addMapKeyCreate('nested', Aerospike.maps.order.ORDERED)
+ const addMapValueString = new Context().addMapValue('nested')
+ const addMapValueInt = new Context().addMapValue(1000)
+ it('Deserializes a cdt context with addListIndex', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addListIndex))).to.eql(addListIndex)
+ })
+ it('Deserializes a cdt context with addListIndexCreate', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addListIndexCreate))).to.eql(addListIndexCreate)
+ })
+ it('Deserializes a cdt context with addListRank', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addListRank))).to.eql(addListRank)
+ })
+ it('Deserializes a cdt context with addListValueString', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addListValueString))).to.eql(addListValueString)
+ })
+ it('Deserializes a cdt context with addListValueInt', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addListValueInt))).to.eql(addListValueInt)
+ })
+ it('Deserializes a cdt context with addMapIndex', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapIndex))).to.eql(addMapIndex)
+ })
+ it('Deserializes a cdt context with addMapRank', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapRank))).to.eql(addMapRank)
+ })
+ it('Deserializes a cdt context with addMapKey', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapKey))).to.eql(addMapKey)
+ })
+ it('Deserializes a cdt context with addMapKeyCreate', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapKeyCreate))).to.eql(addMapKeyCreate)
+ })
+ it('Deserializes a cdt context with addMapValueString', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapValueString))).to.eql(addMapValueString)
+ })
+ it('Deserializes a cdt context with addMapValueInt', function () {
+ expect(client.contextFromBase64(client.contextToBase64(addMapValueInt))).to.eql(addMapValueInt)
+ })
+ it('Throws an error if no value is given', function () {
+ expect(() => { client.contextFromBase64() }).to.throw(Error)
+ })
+ it('Throws an error if an non-string value is given', function () {
+ expect(() => { client.contextFromBase64(45) }).to.throw(Error)
+ })
+ })
+
context.skip('cluster name', function () {
it('should fail to connect to the cluster if the cluster name does not match', function (done) {
const config = Object.assign({}, helper.config)
diff --git a/test/index.js b/test/index.js
index f98019e0..9fbf5b74 100644
--- a/test/index.js
+++ b/test/index.js
@@ -23,6 +23,7 @@ const Aerospike = require('../lib/aerospike')
const Job = require('../lib/job')
const IndexJob = require('../lib/index_job')
const helper = require('./test_helper')
+const Context = Aerospike.cdt.Context
context('secondary indexes', function () {
const client = helper.client
@@ -80,6 +81,37 @@ context('secondary indexes', function () {
.then(() => verifyIndexExists(helper.namespace, testIndex.name))
})
+ it('should create an index with CDT Context', function () {
+ const options = {
+ ns: helper.namespace,
+ set: helper.set,
+ bin: testIndex.bin,
+ index: testIndex.name,
+ type: Aerospike.indexType.LIST,
+ datatype: Aerospike.indexDataType.NUMERIC,
+ context: new Context().addListIndex(0)
+ }
+
+ return client.createIndex(options)
+ .then(() => verifyIndexExists(helper.namespace, testIndex.name))
+ })
+
+ it('should not create an index with CDT Context \'addListIndexCreate\'', function () {
+ const options = {
+ ns: helper.namespace,
+ set: helper.set,
+ bin: testIndex.bin,
+ index: testIndex.name,
+ type: Aerospike.indexType.LIST,
+ datatype: Aerospike.indexDataType.NUMERIC,
+ context: new Context().addListIndexCreate(0, 0, false)
+ }
+
+ return client.createIndex(options)
+ .then(() => expect(1).to.equal(2))
+ .catch(() => { expect('pass').to.equal('pass') })
+ })
+
it('should create an integer index with info policy', function () {
const options = {
ns: helper.namespace,
@@ -162,15 +194,35 @@ context('secondary indexes', function () {
})
})
- describe('Client#indexRemove()', function () {
- beforeEach(() => helper.index.create(testIndex.name, helper.set, testIndex.bin,
- Aerospike.indexDataType.STRING, Aerospike.indexType.DEFAULT))
+ describe('Client#indexRemove()', async function () {
+ beforeEach(() => {
+ helper.index.create(testIndex.name, helper.set, testIndex.bin,
+ Aerospike.indexDataType.STRING, Aerospike.indexType.DEFAULT)
+ })
- it('should drop an index', function (done) {
- client.indexRemove(helper.namespace, testIndex.name, function (err) {
- expect(err).to.be.null()
- done()
- })
+ it('should drop an index', async function () {
+ // Wait for index creation to complete
+ this.timeout(10000)
+ await new Promise(resolve => setTimeout(resolve, 5000))
+
+ // Do query on the secondary index to ensure proper creation.
+ let query = client.query(helper.namespace, helper.set)
+ query.where(Aerospike.filter.equal(testIndex.bin, 'value'))
+ await query.results()
+
+ await client.indexRemove(helper.namespace, testIndex.name)
+
+ // Do query on the secondary index to ensure proper deletion
+ query = client.query(helper.namespace, helper.set)
+ query.where(Aerospike.filter.equal(testIndex.bin, 'value'))
+ try {
+ await query.results()
+ // Fail test if this code is reached
+ expect('fail').to.equal('now')
+ } catch (error) {
+ expect(error.code).to.equal(201)
+ expect('pass').to.equal('pass')
+ }
})
it('should return a Promise if called without callback function', function () {
diff --git a/test/query.js b/test/query.js
index 717f346a..8e51c20a 100644
--- a/test/query.js
+++ b/test/query.js
@@ -24,6 +24,7 @@ const Query = require('../lib/query')
const Job = require('../lib/job')
const helper = require('./test_helper')
const exp = Aerospike.exp
+const Context = Aerospike.cdt.Context
const AerospikeError = Aerospike.AerospikeError
const GeoJSON = Aerospike.GeoJSON
@@ -79,7 +80,37 @@ describe('Queries', function () {
{ name: 'filter', value: 1 },
{ name: 'filter', value: 2 },
{ name: 'filter', value: 3 },
- { name: 'filter', value: 4 }
+ { name: 'filter', value: 4 },
+
+ { name: 'nested int list match', li: { nested: [1, 5, 9] } },
+ { name: 'nested int list non-match', li: { nested: [500, 501, 502] } },
+ { name: 'nested int map match', mi: { nested: { a: 1, b: 5, c: 9 } } },
+ { name: 'nested int map non-match', mi: { nested: { a: 500, b: 501, c: 502 } } },
+ { name: 'nested string list match', ls: { nested: ['banana', 'blueberry'] } },
+ { name: 'nested string list non-match', ls: { nested: ['tomato', 'cuccumber'] } },
+ { name: 'nested string map match', ms: { nested: { a: 'banana', b: 'blueberry' } } },
+ { name: 'nested string map non-match', ms: { nested: { a: 'tomato', b: 'cuccumber' } } },
+ { name: 'nested string mapkeys match', mks: { nested: { banana: 1, blueberry: 2 } } },
+ { name: 'nested string mapkeys non-match', mks: { nested: { tomato: 3, cuccumber: 4 } } },
+ { name: 'nested point match', g: { nested: GeoJSON.Point(103.913, 1.308) } },
+ { name: 'nested point non-match', g: { nested: GeoJSON.Point(-122.101, 37.421) } },
+ { name: 'nested point list match', lg: { nested: [GeoJSON.Point(103.913, 1.308), GeoJSON.Point(105.913, 3.308)] } },
+ { name: 'nested point list non-match', lg: { nested: [GeoJSON.Point(-122.101, 37.421), GeoJSON.Point(-120.101, 39.421)] } },
+ { name: 'nested point map match', mg: { nested: { a: GeoJSON.Point(103.913, 1.308), b: GeoJSON.Point(105.913, 3.308) } } },
+ { name: 'nested point map non-match', mg: { nested: { a: GeoJSON.Point(-122.101, 37.421), b: GeoJSON.Point(-120.101, 39.421) } } },
+ { name: 'nested region match', g: { nested: GeoJSON.Polygon([102.913, 0.308], [102.913, 2.308], [104.913, 2.308], [104.913, 0.308], [102.913, 0.308]) } },
+ { name: 'nested region non-match', g: { nested: GeoJSON.Polygon([-121.101, 36.421], [-121.101, 38.421], [-123.101, 38.421], [-123.101, 36.421], [-121.101, 36.421]) } },
+ { name: 'nested region list match', lg: { nested: [GeoJSON.Polygon([102.913, 0.308], [102.913, 2.308], [104.913, 2.308], [104.913, 0.308], [102.913, 0.308])] } },
+ { name: 'nested region list non-match', lg: { nested: [GeoJSON.Polygon([-121.101, 36.421], [-121.101, 38.421], [-123.101, 38.421], [-123.101, 36.421], [-121.101, 36.421])] } },
+ { name: 'nested region map match', mg: { nested: { a: GeoJSON.Polygon([102.913, 0.308], [102.913, 2.308], [104.913, 2.308], [104.913, 0.308], [102.913, 0.308]) } } },
+ { name: 'nested region map non-match', mg: { nested: [GeoJSON.Polygon([-121.101, 36.421], [-121.101, 38.421], [-123.101, 38.421], [-123.101, 36.421], [-121.101, 36.421])] } },
+ { name: 'nested aggregate', nested: { value: 10 } },
+ { name: 'nested aggregate', nested: { value: 20 } },
+ { name: 'nested aggregate', nested: { value: 30 } },
+ { name: 'nested aggregate', nested: { doubleNested: { value: 10 } } },
+ { name: 'nested aggregate', nested: { doubleNested: { value: 20 } } },
+ { name: 'nested aggregate', nested: { doubleNested: { value: 30 } } }
+
]
const numberOfSamples = samples.length
const indexes = [
@@ -93,7 +124,18 @@ describe('Queries', function () {
['qidxStrMapKeys', 'mks', STRING, MAPKEYS],
['qidxGeo', 'g', GEO2DSPHERE],
['qidxGeoList', 'lg', GEO2DSPHERE, LIST],
- ['qidxGeoMap', 'mg', GEO2DSPHERE, MAPVALUES]
+ ['qidxGeoMap', 'mg', GEO2DSPHERE, MAPVALUES],
+ // CDT context indexes
+ ['qidxNameNested', 'name', STRING, MAPKEYS, new Context().addMapKey('nested')],
+ ['qidxIntListNested', 'li', NUMERIC, LIST, new Context().addMapKey('nested')],
+ ['qidxIntMapNested', 'mi', NUMERIC, MAPVALUES, new Context().addMapKey('nested')],
+ ['qidxStrListNested', 'ls', STRING, LIST, new Context().addMapKey('nested')],
+ ['qidxStrMapNested', 'ms', STRING, MAPVALUES, new Context().addMapKey('nested')],
+ ['qidxStrMapKeysNested', 'mks', STRING, MAPKEYS, new Context().addMapKey('nested')],
+ ['qidxGeoListNested', 'lg', GEO2DSPHERE, LIST, new Context().addMapKey('nested')],
+ ['qidxGeoMapNested', 'mg', GEO2DSPHERE, MAPVALUES, new Context().addMapKey('nested')],
+ ['qidxAggregateMapNested', 'nested', STRING, MAPKEYS],
+ ['qidxAggregateMapDoubleNested', 'nested', STRING, MAPKEYS, new Context().addMapKey('doubleNested')]
]
let keys = []
@@ -122,7 +164,7 @@ describe('Queries', function () {
putgen.put(numberOfSamples, generators)
.then((records) => { keys = records.map((rec) => rec.key) })
.then(() => Promise.all(indexes.map(idx =>
- helper.index.create(idx[0], testSet, idx[1], idx[2], idx[3])))),
+ helper.index.create(idx[0], testSet, idx[1], idx[2], idx[3], idx[4])))),
helper.udf.register('udf.lua')
])
})
@@ -200,6 +242,22 @@ describe('Queries', function () {
})
})
+ it('should apply a stream UDF to the nested context', function (done) {
+ const args = {
+ filters: [filter.contains('name', 'value', MAPKEYS, new Context().addMapKey('nested'))]
+ }
+ const query = client.query(helper.namespace, testSet, args)
+ query.setUdf('udf', 'even')
+ const stream = query.foreach()
+ const results = []
+ stream.on('error', error => { throw error })
+ stream.on('data', record => results.push(record.bins))
+ stream.on('end', () => {
+ expect(results.sort()).to.eql([])
+ done()
+ })
+ })
+
describe('query.paginate()', function () {
it('paginates with the correct amount of keys and pages', async function () {
let recordsReceived = 0
@@ -292,6 +350,32 @@ describe('Queries', function () {
}
})
+ it('Paginates correctly using query.results() on an index with a cdt context', async function () {
+ let recordTotal = 0
+ let recordsReceived = 0
+ let pageTotal = 0
+ const lastPage = 1
+ const maxRecs = 5
+ const query = client.query(helper.namespace, testSet, { paginate: true, maxRecords: maxRecs, filters: [filter.contains('nested', 'value', MAPKEYS, new Context().addMapKey('doubleNested'))] })
+ let results = []
+ while (1) {
+ console.log(results)
+ results = await query.results()
+ console.log(results)
+ recordsReceived += results.length
+ results = []
+ pageTotal += 1
+ recordTotal += recordsReceived
+ if (recordsReceived !== maxRecs) {
+ expect(query.hasNextPage()).to.equal(false)
+ expect(pageTotal).to.equal(lastPage)
+ expect(recordTotal).to.equal(3)
+ break
+ }
+ recordsReceived = 0
+ }
+ })
+
it('Throw error when query.UDF is set and query.paginate is true', async function () {
const maxRecs = 2
const query = client.query(helper.namespace, testSet, { paginate: true, maxRecords: maxRecs, filters: [filter.equal('name', 'filter')] })
@@ -481,10 +565,20 @@ describe('Queries', function () {
verifyQueryResults(args, 'int list match', done)
})
+ it('should match integers in a list within a range in a nested context', function (done) {
+ const args = { filters: [filter.range('li', 3, 7, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested int list match', done)
+ })
+
it('should match integers in a map within a range', function (done) {
const args = { filters: [filter.range('mi', 3, 7, MAPVALUES)] }
verifyQueryResults(args, 'int map match', done)
})
+
+ it('should match integers in a map within a range in a nested context', function (done) {
+ const args = { filters: [filter.range('mi', 3, 7, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested int map match', done)
+ })
})
describe('filter.contains()', function () {
@@ -493,26 +587,51 @@ describe('Queries', function () {
verifyQueryResults(args, 'int list match', done)
})
+ it('should match lists containing an integer in a nested context', function (done) {
+ const args = { filters: [filter.contains('li', 5, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested int list match', done)
+ })
+
it('should match maps containing an integer value', function (done) {
const args = { filters: [filter.contains('mi', 5, MAPVALUES)] }
verifyQueryResults(args, 'int map match', done)
})
+ it('should match maps containing an integer value in a nested context', function (done) {
+ const args = { filters: [filter.contains('mi', 5, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested int map match', done)
+ })
+
it('should match lists containing a string', function (done) {
const args = { filters: [filter.contains('ls', 'banana', LIST)] }
verifyQueryResults(args, 'string list match', done)
})
+ it('should match lists containing a string in a nested context', function (done) {
+ const args = { filters: [filter.contains('ls', 'banana', LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested string list match', done)
+ })
+
it('should match maps containing a string value', function (done) {
const args = { filters: [filter.contains('ms', 'banana', MAPVALUES)] }
verifyQueryResults(args, 'string map match', done)
})
+ it('should match maps containing a string value in a nested context', function (done) {
+ const args = { filters: [filter.contains('ms', 'banana', MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested string map match', done)
+ })
+
it('should match maps containing a string key', function (done) {
const args = { filters: [filter.contains('mks', 'banana', MAPKEYS)] }
verifyQueryResults(args, 'string mapkeys match', done)
})
+ it('should match maps containing a string key in a nested context', function (done) {
+ const args = { filters: [filter.contains('mks', 'banana', MAPKEYS, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested string mapkeys match', done)
+ })
+
it('throws a type error if the comparison value is of invalid type', function () {
const fn = () => filter.contains('list', { foo: 'bar' }, LIST)
expect(fn).to.throw(TypeError)
@@ -532,12 +651,24 @@ describe('Queries', function () {
verifyQueryResults(args, 'point list match', done)
})
+ it('should match locations in a list within a GeoJSON region in a nested context', function (done) {
+ const region = new GeoJSON({ type: 'Polygon', coordinates: [[[103, 1.3], [104, 1.3], [104, 1.4], [103, 1.4], [103, 1.3]]] })
+ const args = { filters: [filter.geoWithinGeoJSONRegion('lg', region, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested point list match', done)
+ })
+
it('should match locations in a map within a GeoJSON region', function (done) {
const region = new GeoJSON({ type: 'Polygon', coordinates: [[[103, 1.3], [104, 1.3], [104, 1.4], [103, 1.4], [103, 1.3]]] })
const args = { filters: [filter.geoWithinGeoJSONRegion('mg', region, MAPVALUES)] }
verifyQueryResults(args, 'point map match', done)
})
+ it('should match locations in a map within a GeoJSON region in a nested context', function (done) {
+ const region = new GeoJSON({ type: 'Polygon', coordinates: [[[103, 1.3], [104, 1.3], [104, 1.4], [103, 1.4], [103, 1.3]]] })
+ const args = { filters: [filter.geoWithinGeoJSONRegion('mg', region, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested point map match', done)
+ })
+
it('accepts a plain object as GeoJSON', function (done) {
const region = { type: 'Polygon', coordinates: [[[103, 1.3], [104, 1.3], [104, 1.4], [103, 1.4], [103, 1.3]]] }
const args = { filters: [filter.geoWithinGeoJSONRegion('g', region)] }
@@ -556,10 +687,20 @@ describe('Queries', function () {
verifyQueryResults(args, 'point list match', done)
})
+ it('should match locations in a list within a radius from another location in a nested context', function (done) {
+ const args = { filters: [filter.geoWithinRadius('lg', 103.9135, 1.3085, 15000, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested point list match', done)
+ })
+
it('should match locations in a map within a radius from another location', function (done) {
const args = { filters: [filter.geoWithinRadius('mg', 103.9135, 1.3085, 15000, MAPVALUES)] }
verifyQueryResults(args, 'point map match', done)
})
+
+ it('should match locations in a map within a radius from another location in a nested context', function (done) {
+ const args = { filters: [filter.geoWithinRadius('mg', 103.9135, 1.3085, 15000, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested point map match', done)
+ })
})
describe('filter.geoContainsGeoJSONPoint()', function () {
@@ -575,12 +716,24 @@ describe('Queries', function () {
verifyQueryResults(args, 'region list match', done)
})
+ it('should match regions in a list that contain a GeoJSON point in a nested context', function (done) {
+ const point = new GeoJSON({ type: 'Point', coordinates: [103.913, 1.308] })
+ const args = { filters: [filter.geoContainsGeoJSONPoint('lg', point, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested region list match', done)
+ })
+
it('should match regions in a map that contain a GeoJSON point', function (done) {
const point = new GeoJSON({ type: 'Point', coordinates: [103.913, 1.308] })
const args = { filters: [filter.geoContainsGeoJSONPoint('mg', point, MAPVALUES)] }
verifyQueryResults(args, 'region map match', done)
})
+ it('should match regions in a map that contain a GeoJSON point in a nested context', function (done) {
+ const point = new GeoJSON({ type: 'Point', coordinates: [103.913, 1.308] })
+ const args = { filters: [filter.geoContainsGeoJSONPoint('mg', point, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested region map match', done)
+ })
+
it('accepts a plain object as GeoJSON', function (done) {
const point = { type: 'Point', coordinates: [103.913, 1.308] }
const args = { filters: [filter.geoContainsGeoJSONPoint('g', point)] }
@@ -599,10 +752,20 @@ describe('Queries', function () {
verifyQueryResults(args, 'region list match', done)
})
+ it('should match regions in a list that contain a lng/lat coordinate pair in a nested context', function (done) {
+ const args = { filters: [filter.geoContainsPoint('lg', 103.913, 1.308, LIST, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested region list match', done)
+ })
+
it('should match regions in a map that contain a lng/lat coordinate pair', function (done) {
const args = { filters: [filter.geoContainsPoint('mg', 103.913, 1.308, MAPVALUES)] }
verifyQueryResults(args, 'region map match', done)
})
+
+ it('should match regions in a map that contain a lng/lat coordinate pair in a nested context', function (done) {
+ const args = { filters: [filter.geoContainsPoint('mg', 103.913, 1.308, MAPVALUES, new Context().addMapKey('nested'))] }
+ verifyQueryResults(args, 'nested region map match', done)
+ })
})
})
})
@@ -651,6 +814,30 @@ describe('Queries', function () {
})
})
+ it('should apply a user defined function and aggregate the results from a map', function (done) {
+ const args = {
+ filters: [filter.contains('nested', 'value', MAPKEYS)]
+ }
+ const query = client.query(helper.namespace, testSet, args)
+ query.apply('udf', 'count', function (error, result) {
+ if (error) throw error
+ expect(result).to.equal(3)
+ done()
+ })
+ })
+
+ it('should apply a user defined function and aggregate the results from a nested map', function (done) {
+ const args = {
+ filters: [filter.contains('nested', 'value', MAPKEYS, new Context().addMapKey('doubleNested'))]
+ }
+ const query = client.query(helper.namespace, testSet, args)
+ query.apply('udf', 'count', function (error, result) {
+ if (error) throw error
+ expect(result).to.equal(3)
+ done()
+ })
+ })
+
it('should apply a user defined function with arguments and aggregate the results', function (done) {
const args = {
filters: [filter.equal('name', 'aggregate')]
@@ -698,6 +885,16 @@ describe('Queries', function () {
expect(job).to.be.instanceof(Job)
})
})
+ it('returns a Promise that resolves to a Job with a filter containing a CDT context', function () {
+ const args = {
+ filters: [filter.contains('nested', 'value', MAPKEYS, new Context().addMapKey('doubleNested'))]
+ }
+ const query = client.query(helper.namespace, testSet, args)
+ return query.background('udf', 'noop')
+ .then(job => {
+ expect(job).to.be.instanceof(Job)
+ })
+ })
})
describe('query.operate()', function () {
diff --git a/test/test_helper.js b/test/test_helper.js
index c865d480..9682b92d 100644
--- a/test/test_helper.js
+++ b/test/test_helper.js
@@ -72,14 +72,15 @@ function IndexHelper (client) {
this.client = client
}
-IndexHelper.prototype.create = function (indexName, setName, binName, dataType, indexType) {
+IndexHelper.prototype.create = function (indexName, setName, binName, dataType, indexType, context) {
const index = {
ns: options.namespace,
set: setName,
bin: binName,
index: indexName,
type: indexType || Aerospike.indexType.DEFAULT,
- datatype: dataType
+ datatype: dataType,
+ context
}
return this.client.createIndex(index)
.then(job => job.wait(10))