You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@ignite.apache.org by ag...@apache.org on 2018/06/30 13:24:40 UTC
[18/50] [abbrv] ignite git commit: IGNITE-7777: Implemented Ignite
NodeJs thin client
http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/index.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/index.js b/modules/platforms/nodejs/index.js
new file mode 100644
index 0000000..cce86ab
--- /dev/null
+++ b/modules/platforms/nodejs/index.js
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+module.exports = require('./lib/IgniteClient');
+module.exports.ObjectType = require('./lib/ObjectType').ObjectType;
+module.exports.MapObjectType = require('./lib/ObjectType').MapObjectType;
+module.exports.CollectionObjectType = require('./lib/ObjectType').CollectionObjectType;
+module.exports.ComplexObjectType = require('./lib/ObjectType').ComplexObjectType;
+module.exports.ObjectArrayType = require('./lib/ObjectType').ObjectArrayType;
+module.exports.BinaryObject = require('./lib/BinaryObject');
+module.exports.Timestamp = require('./lib/Timestamp');
+module.exports.EnumItem = require('./lib/EnumItem');
+module.exports.Decimal = require('decimal.js');
+module.exports.Errors = require('./lib/Errors');
+module.exports.IgniteClientConfiguration = require('./lib/IgniteClientConfiguration');
+module.exports.CacheClient = require('./lib/CacheClient');
+module.exports.CacheEntry = require('./lib/CacheClient').CacheEntry;
+module.exports.CacheConfiguration = require('./lib/CacheConfiguration');
+module.exports.QueryEntity = require('./lib/CacheConfiguration').QueryEntity;
+module.exports.QueryField = require('./lib/CacheConfiguration').QueryField;
+module.exports.QueryIndex = require('./lib/CacheConfiguration').QueryIndex;
+module.exports.CacheKeyConfiguration = require('./lib/CacheConfiguration').CacheKeyConfiguration;
+module.exports.SqlQuery = require('./lib/Query').SqlQuery;
+module.exports.SqlFieldsQuery = require('./lib/Query').SqlFieldsQuery;
+module.exports.ScanQuery = require('./lib/Query').ScanQuery;
+module.exports.Cursor = require('./lib/Cursor').Cursor;
+module.exports.SqlFieldsCursor = require('./lib/Cursor').SqlFieldsCursor;
http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/lib/BinaryObject.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/BinaryObject.js b/modules/platforms/nodejs/lib/BinaryObject.js
new file mode 100644
index 0000000..2cc6be6
--- /dev/null
+++ b/modules/platforms/nodejs/lib/BinaryObject.js
@@ -0,0 +1,498 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const Util = require('util');
+const ObjectType = require('./ObjectType').ObjectType;
+const ComplexObjectType = require('./ObjectType').ComplexObjectType;
+const Errors = require('./Errors');
+const BinaryUtils = require('./internal/BinaryUtils');
+const BinaryType = require('./internal/BinaryType');
+const BinaryField = require('./internal/BinaryType').BinaryField;
+const BinaryTypeBuilder = require('./internal/BinaryType').BinaryTypeBuilder;
+const BinaryWriter = require('./internal/BinaryWriter');
+const ArgumentChecker = require('./internal/ArgumentChecker');
+const Logger = require('./internal/Logger');
+
+const HEADER_LENGTH = 24;
+const VERSION = 1;
+
+// user type
+const FLAG_USER_TYPE = 0x0001;
+// schema exists
+const FLAG_HAS_SCHEMA = 0x0002;
+// object contains raw data
+const FLAG_HAS_RAW_DATA = 0x0004;
+// offsets take 1 byte
+const FLAG_OFFSET_ONE_BYTE = 0x0008;
+// offsets take 2 bytes
+const FLAG_OFFSET_TWO_BYTES = 0x0010;
+// compact footer, no field IDs
+const FLAG_COMPACT_FOOTER = 0x0020;
+
+/**
+ * Class representing a complex Ignite object in the binary form.
+ *
+ * It corresponds to COMPOSITE_TYPE.COMPLEX_OBJECT {@link ObjectType.COMPOSITE_TYPE},
+ * has mandatory type Id, which corresponds to a name of the complex type,
+ * and includes optional fields.
+ *
+ * An instance of the BinaryObject can be obtained/created by the following ways:
+ * - returned by the client when a complex object is received from Ignite cache
+ * and is not deserialized to another JavaScript object.
+ * - created using the public constructor. Fields may be added to such an instance using setField() method.
+ * - created from a JavaScript object using static fromObject() method.
+ */
+class BinaryObject {
+
+ /**
+ * Creates an instance of the BinaryObject without any fields.
+ *
+ * Fields may be added later using setField() method.
+ *
+ * @param {string} typeName - name of the complex type to generate the type Id.
+ *
+ * @return {BinaryObject} - new BinaryObject instance.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ constructor(typeName) {
+ ArgumentChecker.notEmpty(typeName, 'typeName');
+ this._buffer = null;
+ this._fields = new Map();
+ this._typeBuilder = BinaryTypeBuilder.fromTypeName(typeName);
+ this._modified = false;
+ this._schemaOffset = null;
+ this._compactFooter = false;
+ }
+
+ /**
+ * Creates an instance of the BinaryObject from the specified instance of JavaScript Object.
+ *
+ * All fields of the JavaScript Object instance with their values are added to the BinaryObject.
+ * Fields may be added or removed later using setField() and removeField() methods.
+ *
+ * If complexObjectType parameter is specified, then the type Id is taken from it.
+ * Otherwise, the type Id is generated from the name of the JavaScript Object.
+ *
+ * @async
+ *
+ * @param {object} jsObject - instance of JavaScript Object
+ * which adds and initializes the fields of the BinaryObject instance.
+ * @param {ComplexObjectType} [complexObjectType] - instance of complex type definition
+ * which specifies non-standard mapping of the fields of the BinaryObject instance
+ * to/from the Ignite types.
+ *
+ * @return {BinaryObject} - new BinaryObject instance.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ static async fromObject(jsObject, complexObjectType = null) {
+ ArgumentChecker.notEmpty(jsObject, 'jsObject');
+ ArgumentChecker.hasType(complexObjectType, 'complexObjectType', false, ComplexObjectType);
+ const typeBuilder = BinaryTypeBuilder.fromObject(jsObject, complexObjectType);
+ const result = new BinaryObject(typeBuilder.getTypeName());
+ result._typeBuilder = typeBuilder;
+ let fieldName;
+ for (let field of result._typeBuilder.getFields()) {
+ fieldName = field.name;
+ if (jsObject && jsObject[fieldName] !== undefined) {
+ result.setField(
+ fieldName,
+ jsObject[fieldName],
+ complexObjectType ? complexObjectType._getFieldType(fieldName) : null);
+ }
+ else {
+ throw Errors.IgniteClientError.serializationError(
+ true, Util.format('field "%s" is undefined', fieldName));
+ }
+ }
+ return result;
+ }
+
+ /**
+ * Sets the new value of the specified field.
+ * Adds the specified field, if it did not exist before.
+ *
+ * Optionally, specifies a type of the field.
+ * If the type is not specified then during operations the Ignite client
+ * will try to make automatic mapping between JavaScript types and Ignite object types -
+ * according to the mapping table defined in the description of the {@link ObjectType} class.
+ *
+ * @param {string} fieldName - name of the field.
+ * @param {*} fieldValue - new value of the field.
+ * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} [fieldType] - type of the field:
+ * - either a type code of primitive (simple) type
+ * - or an instance of class representing non-primitive (composite) type
+ * - or null (or not specified) that means the type is not specified.
+ *
+ * @return {BinaryObject} - the same instance of BinaryObject
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ setField(fieldName, fieldValue, fieldType = null) {
+ ArgumentChecker.notEmpty(fieldName, 'fieldName');
+ this._modified = true;
+ const field = new BinaryObjectField(fieldName, fieldValue, fieldType);
+ this._fields.set(field.id, field);
+ this._typeBuilder.setField(fieldName, field.typeCode);
+ return this;
+ }
+
+ /**
+ * Removes the specified field.
+ * Does nothing if the field does not exist.
+ *
+ * @param {string} fieldName - name of the field.
+ *
+ * @return {BinaryObject} - the same instance of BinaryObject
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ removeField(fieldName) {
+ ArgumentChecker.notEmpty(fieldName, 'fieldName');
+ this._modified = true;
+ this._fields.delete(BinaryField._calculateId(fieldName));
+ this._typeBuilder.removeField(fieldName);
+ return this;
+ }
+
+ /**
+ * Checks if the specified field exists in this BinaryObject instance.
+ *
+ * @param {string} fieldName - name of the field.
+ *
+ * @return {boolean} - true if exists, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ hasField(fieldName) {
+ ArgumentChecker.notEmpty(fieldName, 'fieldName');
+ return this._fields.has(BinaryField._calculateId(fieldName));
+ }
+
+ /**
+ * Returns a value of the specified field.
+ *
+ * Optionally, specifies a type of the field.
+ * If the type is not specified then the Ignite client
+ * will try to make automatic mapping between JavaScript types and Ignite object types -
+ * according to the mapping table defined in the description of the {@link ObjectType} class.
+ *
+ * @async
+ *
+ * @param {string} fieldName - name of the field.
+ * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} [fieldType] - type of the field:
+ * - either a type code of primitive (simple) type
+ * - or an instance of class representing non-primitive (composite) type
+ * - or null (or not specified) that means the type is not specified.
+ *
+ * @return {*} - value of the field or JavaScript undefined if the field does not exist.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getField(fieldName, fieldType = null) {
+ ArgumentChecker.notEmpty(fieldName, 'fieldName');
+ const field = this._fields.get(BinaryField._calculateId(fieldName));
+ return field ? await field.getValue(fieldType) : undefined;
+ }
+
+ /**
+ * Deserializes this BinaryObject instance into an instance of the specified complex object type.
+ *
+ * @async
+ *
+ * @param {ComplexObjectType} complexObjectType - instance of class representing complex object type.
+ *
+ * @return {object} - instance of the JavaScript object
+ * which corresponds to the specified complex object type.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async toObject(complexObjectType) {
+ ArgumentChecker.notNull(complexObjectType, 'complexObjectType');
+ ArgumentChecker.hasType(complexObjectType, 'complexObjectType', false, ComplexObjectType);
+ const result = new (complexObjectType._objectConstructor);
+ let binaryField;
+ let fieldName;
+ for (let field of this._fields.values()) {
+ binaryField = this._typeBuilder.getField(field.id);
+ if (!binaryField) {
+ throw Errors.IgniteClientError.serializationError(
+ false, Util.format('field with id "%s" can not be deserialized', field.id));
+ }
+ fieldName = binaryField.name;
+ result[fieldName] = await field.getValue(complexObjectType._getFieldType(fieldName));
+ }
+ return result;
+ }
+
+ /**
+ * Returns type name of this BinaryObject instance.
+ *
+ * @return {string} - type name.
+ */
+ getTypeName() {
+ return this._typeBuilder.getTypeName();
+ }
+
+ /**
+ * Returns names of all fields of this BinaryObject instance.
+ *
+ * @return {Array<string>} - names of all fields.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ getFieldNames() {
+ return this._typeBuilder._schema.fieldIds.map(fieldId => {
+ const field = this._typeBuilder.getField(fieldId);
+ if (field) {
+ return field.name;
+ }
+ else {
+ throw Errors.IgniteClientError.internalError(
+ Util.format('Field "%s" is absent in binary type fields', fieldId));
+ }
+ });
+ }
+
+ /** Private methods */
+
+ /**
+ * @ignore
+ */
+ static _isFlagSet(flags, flag) {
+ return (flags & flag) === flag;
+ }
+
+ /**
+ * @ignore
+ */
+ static async _fromBuffer(buffer) {
+ const result = new BinaryObject(new ComplexObjectType({})._typeName);
+ result._buffer = buffer;
+ result._startPos = buffer.position;
+ await result._read();
+ return result;
+ }
+
+ /**
+ * @ignore
+ */
+ async _write(buffer) {
+ if (this._buffer && !this._modified) {
+ buffer.writeBuffer(this._buffer.buffer, this._startPos, this._startPos + this._length);
+ }
+ else {
+ await this._typeBuilder.finalize();
+ this._startPos = buffer.position;
+ buffer.position = this._startPos + HEADER_LENGTH;
+ // write fields
+ for (let field of this._fields.values()) {
+ await field._writeValue(buffer, this._typeBuilder.getField(field.id).typeCode);
+ }
+ this._schemaOffset = buffer.position - this._startPos;
+ // write schema
+ for (let field of this._fields.values()) {
+ field._writeOffset(buffer, this._startPos);
+ }
+ this._length = buffer.position - this._startPos;
+ this._buffer = buffer;
+ // write header
+ this._writeHeader();
+ this._buffer.position = this._startPos + this._length;
+ this._modified = false;
+ }
+
+ if (Logger.debug) {
+ Logger.logDebug('BinaryObject._write [' + [...this._buffer.getSlice(this._startPos, this._startPos + this._length)] + ']');
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ _writeHeader() {
+ this._buffer.position = this._startPos;
+ // type code
+ this._buffer.writeByte(BinaryUtils.TYPE_CODE.COMPLEX_OBJECT);
+ // version
+ this._buffer.writeByte(VERSION);
+ // flags
+ this._buffer.writeShort(FLAG_USER_TYPE | FLAG_HAS_SCHEMA | FLAG_COMPACT_FOOTER);
+ // type id
+ this._buffer.writeInteger(this._typeBuilder.getTypeId());
+ // hash code
+ this._buffer.writeInteger(BinaryUtils.contentHashCode(
+ this._buffer, this._startPos + HEADER_LENGTH, this._schemaOffset - 1));
+ // length
+ this._buffer.writeInteger(this._length);
+ // schema id
+ this._buffer.writeInteger(this._typeBuilder.getSchemaId());
+ // schema offset
+ this._buffer.writeInteger(this._schemaOffset);
+ }
+
+ /**
+ * @ignore
+ */
+ async _read() {
+ await this._readHeader();
+ this._buffer.position = this._startPos + this._schemaOffset;
+ const fieldOffsets = new Array();
+ const fieldIds = this._typeBuilder._schema.fieldIds;
+ let index = 0;
+ let fieldId;
+ while (this._buffer.position < this._startPos + this._length) {
+ if (!this._compactFooter) {
+ fieldId = this._buffer.readInteger();
+ this._typeBuilder._schema.addField(fieldId);
+ }
+ else {
+ if (index >= fieldIds.length) {
+ throw Errors.IgniteClientError.serializationError(
+ false, 'wrong number of fields in schema');
+ }
+ fieldId = fieldIds[index];
+ index++;
+ }
+ fieldOffsets.push([fieldId, this._buffer.readNumber(this._offsetType)]);
+ }
+ fieldOffsets.sort((val1, val2) => val1[1] - val2[1]);
+ let offset;
+ let nextOffset;
+ let field;
+ for (let i = 0; i < fieldOffsets.length; i++) {
+ fieldId = fieldOffsets[i][0];
+ offset = fieldOffsets[i][1];
+ nextOffset = i + 1 < fieldOffsets.length ? fieldOffsets[i + 1][1] : this._schemaOffset;
+ field = BinaryObjectField._fromBuffer(
+ this._buffer, this._startPos + offset, nextOffset - offset, fieldId);
+ this._fields.set(field.id, field);
+ }
+ this._buffer.position = this._startPos + this._length;
+ }
+
+ /**
+ * @ignore
+ */
+ async _readHeader() {
+ // type code
+ this._buffer.readByte();
+ // version
+ const version = this._buffer.readByte();
+ if (version !== VERSION) {
+ throw Errors.IgniteClientError.internalError();
+ }
+ // flags
+ const flags = this._buffer.readShort();
+ // type id
+ const typeId = this._buffer.readInteger();
+ // hash code
+ this._buffer.readInteger();
+ // length
+ this._length = this._buffer.readInteger();
+ // schema id
+ const schemaId = this._buffer.readInteger();
+ // schema offset
+ this._schemaOffset = this._buffer.readInteger();
+ const hasSchema = BinaryObject._isFlagSet(flags, FLAG_HAS_SCHEMA);
+ this._compactFooter = BinaryObject._isFlagSet(flags, FLAG_COMPACT_FOOTER);
+ this._offsetType = BinaryObject._isFlagSet(flags, FLAG_OFFSET_ONE_BYTE) ?
+ BinaryUtils.TYPE_CODE.BYTE :
+ BinaryObject._isFlagSet(flags, FLAG_OFFSET_TWO_BYTES) ?
+ BinaryUtils.TYPE_CODE.SHORT :
+ BinaryUtils.TYPE_CODE.INTEGER;
+
+ if (BinaryObject._isFlagSet(FLAG_HAS_RAW_DATA)) {
+ throw Errors.IgniteClientError.serializationError(
+ false, 'complex objects with raw data are not supported');
+ }
+ if (this._compactFooter && !hasSchema) {
+ throw Errors.IgniteClientError.serializationError(
+ false, 'schema is absent for object with compact footer');
+ }
+ this._typeBuilder = await BinaryTypeBuilder.fromTypeId(typeId, schemaId, hasSchema);
+ }
+}
+
+/**
+ * @ignore
+ */
+class BinaryObjectField {
+ constructor(name, value = undefined, type = null) {
+ this._name = name;
+ this._id = BinaryField._calculateId(name);
+ this._value = value;
+ this._type = type;
+ if (!type && value !== undefined && value !== null) {
+ this._type = BinaryUtils.calcObjectType(value);
+ }
+ this._typeCode = null;
+ if (this._type) {
+ this._typeCode = BinaryUtils.getTypeCode(this._type);
+ }
+ }
+
+ get id() {
+ return this._id;
+ }
+
+ get typeCode() {
+ return this._typeCode;
+ }
+
+ async getValue(type = null) {
+ if (this._value === undefined || this._buffer && this._type !== type) {
+ this._buffer.position = this._offset;
+ const BinaryReader = require('./internal/BinaryReader');
+ this._value = await BinaryReader.readObject(this._buffer, type);
+ this._type = type;
+ }
+ return this._value;
+ }
+
+ static _fromBuffer(buffer, offset, length, id) {
+ const result = new BinaryObjectField(null);
+ result._id = id;
+ result._buffer = buffer;
+ result._offset = offset;
+ result._length = length;
+ return result;
+ }
+
+ async _writeValue(buffer, expectedTypeCode) {
+ const offset = buffer.position;
+ if (this._buffer) {
+ buffer.writeBuffer(this._buffer.buffer, this._offset, this._offset + this._length);
+ }
+ else {
+ BinaryUtils.checkCompatibility(this._value, expectedTypeCode);
+ await BinaryWriter.writeObject(buffer, this._value, this._type);
+ }
+ this._buffer = buffer;
+ this._length = buffer.position - offset;
+ this._offset = offset;
+ }
+
+ _writeOffset(buffer, headerStartPos) {
+ buffer.writeInteger(this._offset - headerStartPos);
+ }
+}
+
+module.exports = BinaryObject;
http://git-wip-us.apache.org/repos/asf/ignite/blob/c56d16fb/modules/platforms/nodejs/lib/CacheClient.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/CacheClient.js b/modules/platforms/nodejs/lib/CacheClient.js
new file mode 100644
index 0000000..b76471f
--- /dev/null
+++ b/modules/platforms/nodejs/lib/CacheClient.js
@@ -0,0 +1,761 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+const BinaryUtils = require('./internal/BinaryUtils');
+const BinaryReader = require('./internal/BinaryReader');
+const BinaryWriter = require('./internal/BinaryWriter');
+const ArgumentChecker = require('./internal/ArgumentChecker');
+const SqlQuery = require('./Query').SqlQuery;
+const SqlFieldsQuery = require('./Query').SqlFieldsQuery;
+const ScanQuery = require('./Query').ScanQuery;
+
+/**
+ * Peek modes
+ * @typedef CacheClient.PEEK_MODE
+ * @enum
+ * @readonly
+ * @property ALL 0
+ * @property NEAR 1
+ * @property PRIMARY 2
+ * @property BACKUP 3
+ */
+const PEEK_MODE = Object.freeze({
+ ALL : 0,
+ NEAR : 1,
+ PRIMARY : 2,
+ BACKUP : 3
+});
+
+/**
+ * Class representing and providing access to Ignite cache.
+ *
+ * The class has no public constructor. An instance of this class should be obtained
+ * via the methods of {@link IgniteClient} objects.
+ * One instance of this class provides access to one Ignite cache which is specified
+ * during the instance obtaining and cannot be changed after that.
+ *
+ * There are three groups of methods in the cache client:
+ * - methods to configure the cache client
+ * - methods to operate with the cache using Key-Value Queries
+ * - methods to operate with the cache using SQL and Scan Queries
+ *
+ * @hideconstructor
+ */
+class CacheClient {
+
+ static get PEEK_MODE() {
+ return PEEK_MODE;
+ }
+
+ /* Methods to configure the cache client */
+
+ /**
+ * Specifies a type of the cache key.
+ *
+ * The cache client assumes that keys in all further operations with the cache
+ * will have the specified type.
+ * Eg. the cache client will convert keys provided as input parameters of the methods
+ * to the specified object type before sending them to a server.
+ *
+ * After the cache client creation a type of the cache key is not specified (null).
+ *
+ * If the type is not specified then during operations the cache client
+ * will do automatic mapping between some of the JavaScript types and object types -
+ * according to the mapping table defined in the description of the {@link ObjectType} class.
+ *
+ * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} type - type of the keys in the cache:
+ * - either a type code of primitive (simple) type
+ * - or an instance of class representing non-primitive (composite) type
+ * - or null (means the type is not specified).
+ *
+ * @return {CacheClient} - the same instance of the cache client.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ setKeyType(type) {
+ BinaryUtils.checkObjectType(type, 'type');
+ this._keyType = type;
+ return this;
+ }
+
+ /**
+ * Specifies a type of the cache value.
+ *
+ * The cache client assumes that values in all further operations with the cache
+ * will have the specified type.
+ * Eg. the cache client will convert values provided as input parameters of the methods
+ * to the specified object type before sending them to a server.
+ *
+ * After the cache client creation a type of the cache value is not specified (null).
+ *
+ * If the type is not specified then during operations the cache client
+ * will do automatic mapping between some of the JavaScript types and object types -
+ * according to the mapping table defined in the description of the {@link ObjectType} class.
+ *
+ * @param {ObjectType.PRIMITIVE_TYPE | CompositeType} type - type of the values in the cache:
+ * - either a type code of primitive (simple) type
+ * - or an instance of class representing non-primitive (composite) type
+ * - or null (means the type is not specified).
+ *
+ * @return {CacheClient} - the same instance of the cache client.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ setValueType(type) {
+ BinaryUtils.checkObjectType(type, 'type');
+ this._valueType = type;
+ return this;
+ }
+
+ /* Methods to operate with the cache using Key-Value Queries */
+
+ /**
+ * Retrieves a value associated with the specified key from the cache.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ *
+ * @return {Promise<*>} - value associated with the specified key, or null if it does not exist.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async get(key) {
+ return await this._writeKeyReadValueOp(BinaryUtils.OPERATION.CACHE_GET, key);
+ }
+
+ /**
+ * Retrieves entries associated with the specified keys from the cache.
+ *
+ * @async
+ *
+ * @param {Array<*>} keys - keys.
+ *
+ * @return {Promise<Array<CacheEntry>>} - the retrieved entries (key-value pairs).
+ * Entries with the keys which do not exist in the cache are not included into the array.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getAll(keys) {
+ ArgumentChecker.notEmpty(keys, 'keys');
+ ArgumentChecker.hasType(keys, 'keys', false, Array);
+ let result = null;
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_GET_ALL,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await this._writeKeys(payload, keys);
+ },
+ async (payload) => {
+ const resultCount = payload.readInteger();
+ result = new Array(resultCount);
+ for (let i = 0; i < resultCount; i++) {
+ result[i] = new CacheEntry(
+ await BinaryReader.readObject(payload, this._getKeyType()),
+ await BinaryReader.readObject(payload, this._getValueType()));
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Associates the specified value with the specified key in the cache.
+ *
+ * Overwrites the previous value if the key exists in the cache,
+ * otherwise creates new entry (key-value pair).
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async put(key, value) {
+ await this._writeKeyValueOp(BinaryUtils.OPERATION.CACHE_PUT, key, value);
+ }
+
+ /**
+ * Associates the specified values with the specified keys in the cache.
+ *
+ * Overwrites the previous value if a key exists in the cache,
+ * otherwise creates new entry (key-value pair).
+ *
+ * @async
+ *
+ * @param {Array<CacheEntry>} entries - entries (key-value pairs) to be put into the cache.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async putAll(entries) {
+ ArgumentChecker.notEmpty(entries, 'entries');
+ ArgumentChecker.hasType(entries, 'entries', true, CacheEntry);
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_PUT_ALL,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ payload.writeInteger(entries.length);
+ for (let entry of entries) {
+ await this._writeKeyValue(payload, entry.getKey(), entry.getValue());
+ }
+ });
+ }
+
+ /**
+ * Checks if the specified key exists in the cache.
+ *
+ * @async
+ *
+ * @param {*} key - key to check.
+ *
+ * @return {Promise<boolean>} - true if the key exists, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async containsKey(key) {
+ return await this._writeKeyReadBooleanOp(BinaryUtils.OPERATION.CACHE_CONTAINS_KEY, key);
+ }
+
+ /**
+ * Checks if all the specified keys exist in the cache.
+ *
+ * @async
+ *
+ * @param {Array<*>} keys - keys to check.
+ *
+ * @return {Promise<boolean>} - true if all the keys exist,
+ * false if at least one of the keys does not exist in the cache.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async containsKeys(keys) {
+ return await this._writeKeysReadBooleanOp(BinaryUtils.OPERATION.CACHE_CONTAINS_KEYS, keys);
+ }
+
+ /**
+ * Associates the specified value with the specified key in the cache
+ * and returns the previous associated value, if any.
+ *
+ * Overwrites the previous value if the key exists in the cache,
+ * otherwise creates new entry (key-value pair).
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @return {Promise<*>} - the previous value associated with the specified key, or null if it did not exist.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getAndPut(key, value) {
+ return await this._writeKeyValueReadValueOp(BinaryUtils.OPERATION.CACHE_GET_AND_PUT, key, value);
+ }
+
+ /**
+ * Associates the specified value with the specified key in the cache
+ * and returns the previous associated value, if the key exists in the cache.
+ * Otherwise does nothing and returns null.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @return {Promise<*>} - the previous value associated with the specified key, or null if it did not exist.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getAndReplace(key, value) {
+ return await this._writeKeyValueReadValueOp(BinaryUtils.OPERATION.CACHE_GET_AND_REPLACE, key, value);
+ }
+
+ /**
+ * Removes the cache entry with the specified key
+ * and returns the last associated value, if any.
+ *
+ * @async
+ *
+ * @param {*} key - key of the entry to be removed.
+ *
+ * @return {Promise<*>} - the last value associated with the specified key, or null if it did not exist.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getAndRemove(key) {
+ return await this._writeKeyReadValueOp(BinaryUtils.OPERATION.CACHE_GET_AND_REMOVE, key);
+ }
+
+ /**
+ * Creates new entry (key-value pair) if the specified key does not exist in the cache.
+ * Otherwise does nothing.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @return {Promise<boolean>} - true if the operation has been done, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async putIfAbsent(key, value) {
+ return await this._writeKeyValueReadBooleanOp(BinaryUtils.OPERATION.CACHE_PUT_IF_ABSENT, key, value);
+ }
+
+ /**
+ * Creates new entry (key-value pair) if the specified key does not exist in the cache.
+ * Otherwise returns the current value associated with the existing key.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @return {Promise<*>} - the current value associated with the key if it already exists in the cache,
+ * null if the new entry is created.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getAndPutIfAbsent(key, value) {
+ return await this._writeKeyValueReadValueOp(BinaryUtils.OPERATION.CACHE_GET_AND_PUT_IF_ABSENT, key, value);
+ }
+
+ /**
+ * Associates the specified value with the specified key, if the key exists in the cache.
+ * Otherwise does nothing.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be associated with the specified key.
+ *
+ * @return {Promise<boolean>} - true if the operation has been done, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async replace(key, value) {
+ return await this._writeKeyValueReadBooleanOp(BinaryUtils.OPERATION.CACHE_REPLACE, key, value);
+ }
+
+ /**
+ * Associates the new value with the specified key, if the key exists in the cache
+ * and the current value equals to the provided one.
+ * Otherwise does nothing.
+ *
+ * @async
+ *
+ * @param {*} key - key.
+ * @param {*} value - value to be compared with the current value associated with the specified key.
+ * @param {*} newValue - new value to be associated with the specified key.
+ *
+ * @return {Promise<boolean>} - true if the operation has been done, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async replaceIfEquals(key, value, newValue) {
+ ArgumentChecker.notNull(key, 'key');
+ ArgumentChecker.notNull(value, 'value');
+ ArgumentChecker.notNull(newValue, 'newValue');
+ let result;
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_REPLACE_IF_EQUALS,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await this._writeKeyValue(payload, key, value);
+ await BinaryWriter.writeObject(payload, newValue, this._getValueType());
+ },
+ async (payload) => {
+ result = payload.readBoolean();
+ });
+ return result;
+ }
+
+ /**
+ * Removes all entries from the cache, without notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async clear() {
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_CLEAR,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ });
+ }
+
+ /**
+ * Removes entry with the specified key from the cache, without notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @param {*} key - key to be removed.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async clearKey(key) {
+ await this._writeKeyOp(BinaryUtils.OPERATION.CACHE_CLEAR_KEY, key);
+ }
+
+ /**
+ * Removes entries with the specified keys from the cache, without notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @param {Array<*>} keys - keys to be removed.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async clearKeys(keys) {
+ await this._writeKeysOp(BinaryUtils.OPERATION.CACHE_CLEAR_KEYS, keys);
+ }
+
+ /**
+ * Removes entry with the specified key from the cache, notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @param {*} key - key to be removed.
+ *
+ * @return {Promise<boolean>} - true if the operation has been done, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async removeKey(key) {
+ return await this._writeKeyReadBooleanOp(BinaryUtils.OPERATION.CACHE_REMOVE_KEY, key);
+ }
+
+ /**
+ * Removes entry with the specified key from the cache, if the current value equals to the provided one.
+ * Notifies listeners and cache writers.
+ *
+ * @async
+ *
+ * @param {*} key - key to be removed.
+ * @param {*} value - value to be compared with the current value associated with the specified key.
+ *
+ * @return {Promise<boolean>} - true if the operation has been done, false otherwise.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async removeIfEquals(key, value) {
+ return await this._writeKeyValueReadBooleanOp(BinaryUtils.OPERATION.CACHE_REMOVE_IF_EQUALS, key, value);
+ }
+
+ /**
+ * Removes entries with the specified keys from the cache, notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @param {Array<*>} keys - keys to be removed.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async removeKeys(keys) {
+ await this._writeKeysOp(BinaryUtils.OPERATION.CACHE_REMOVE_KEYS, keys);
+ }
+
+ /**
+ * Removes all entries from the cache, notifying listeners and cache writers.
+ *
+ * @async
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async removeAll() {
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_REMOVE_ALL,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ });
+ }
+
+ /**
+ * Returns the number of the entries in the cache.
+ *
+ * @async
+ *
+ * @param {...CacheClient.PEEK_MODE} [peekModes] - peek modes.
+ *
+ * @return {Promise<number>} - the number of the entries in the cache.
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async getSize(...peekModes) {
+ ArgumentChecker.hasValueFrom(peekModes, 'peekModes', true, CacheClient.PEEK_MODE);
+ let result;
+ await this._socket.send(
+ BinaryUtils.OPERATION.CACHE_GET_SIZE,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ payload.writeInteger(peekModes.length);
+ for (let mode of peekModes) {
+ payload.writeByte(mode);
+ }
+ },
+ async (payload) => {
+ result = payload.readLong().toNumber();
+ });
+ return result;
+ }
+
+ /* Methods to operate with the cache using SQL and Scan Queries */
+
+ /**
+ * Starts an SQL or Scan query operation.
+ *
+ * @async
+ *
+ * @param {SqlQuery | SqlFieldsQuery | ScanQuery} query - query to be executed.
+ *
+ * @return {Promise<Cursor>} - cursor to obtain the results of the query operation:
+ * - {@link SqlFieldsCursor} in case of {@link SqlFieldsQuery} query
+ * - {@link Cursor} in case of other types of query
+ *
+ * @throws {IgniteClientError} if error.
+ */
+ async query(query) {
+ ArgumentChecker.notNull(query, 'query');
+ ArgumentChecker.hasType(query, 'query', false, SqlQuery, SqlFieldsQuery, ScanQuery);
+
+ let value = null;
+ await this._socket.send(
+ query._operation,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await query._write(payload);
+ },
+ async (payload) => {
+ value = await query._getCursor(this._socket, payload, this._keyType, this._valueType);
+ });
+ return value;
+ }
+
+ /** Private methods */
+
+ /**
+ * @ignore
+ */
+ constructor(name, config, socket) {
+ this._name = name;
+ this._cacheId = CacheClient._calculateId(this._name);
+ this._config = config;
+ this._keyType = null;
+ this._valueType = null;
+ this._socket = socket;
+ }
+
+ /**
+ * @ignore
+ */
+ static _calculateId(name) {
+ return BinaryUtils.hashCode(name);
+ }
+
+ /**
+ * @ignore
+ */
+ _writeCacheInfo(payload) {
+ payload.writeInteger(this._cacheId);
+ payload.writeByte(0);
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyValue(payload, key, value) {
+ await BinaryWriter.writeObject(payload, key, this._getKeyType());
+ await BinaryWriter.writeObject(payload, value, this._getValueType());
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeys(payload, keys) {
+ payload.writeInteger(keys.length);
+ for (let key of keys) {
+ await BinaryWriter.writeObject(payload, key, this._getKeyType());
+ }
+ }
+
+ /**
+ * @ignore
+ */
+ _getKeyType() {
+ return this._keyType;
+ }
+
+ /**
+ * @ignore
+ */
+ _getValueType() {
+ return this._valueType;
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyValueOp(operation, key, value, payloadReader = null) {
+ ArgumentChecker.notNull(key, 'key');
+ ArgumentChecker.notNull(value, 'value');
+ await this._socket.send(
+ operation,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await this._writeKeyValue(payload, key, value);
+ },
+ payloadReader);
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyValueReadValueOp(operation, key, value) {
+ let result = null;
+ await this._writeKeyValueOp(
+ operation, key, value,
+ async (payload) => {
+ result = await BinaryReader.readObject(payload, this._getValueType());
+ });
+ return result;
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyValueReadBooleanOp(operation, key, value) {
+ let result = false;
+ await this._writeKeyValueOp(
+ operation, key, value,
+ async (payload) => {
+ result = payload.readBoolean();
+ });
+ return result;
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyOp(operation, key, payloadReader = null) {
+ ArgumentChecker.notNull(key, 'key');
+ await this._socket.send(
+ operation,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await BinaryWriter.writeObject(payload, key, this._getKeyType());
+ },
+ payloadReader);
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyReadValueOp(operation, key) {
+ let value = null;
+ await this._writeKeyOp(
+ operation, key,
+ async (payload) => {
+ value = await BinaryReader.readObject(payload, this._getValueType());
+ });
+ return value;
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeyReadBooleanOp(operation, key) {
+ let result = false;
+ await this._writeKeyOp(
+ operation, key,
+ async (payload) => {
+ result = payload.readBoolean();
+ });
+ return result;
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeysOp(operation, keys, payloadReader = null) {
+ ArgumentChecker.notEmpty(keys, 'keys');
+ ArgumentChecker.hasType(keys, 'keys', false, Array);
+ await this._socket.send(
+ operation,
+ async (payload) => {
+ this._writeCacheInfo(payload);
+ await this._writeKeys(payload, keys);
+ },
+ payloadReader);
+ }
+
+ /**
+ * @ignore
+ */
+ async _writeKeysReadBooleanOp(operation, keys) {
+ let result = false;
+ await this._writeKeysOp(
+ operation, keys,
+ async (payload) => {
+ result = payload.readBoolean();
+ });
+ return result;
+ }
+}
+
+/**
+ * A cache entry (key-value pair).
+ */
+class CacheEntry {
+
+ /**
+ * Public constructor.
+ *
+ * @param {*} key - key corresponding to this entry.
+ * @param {*} value - value associated with the key.
+ *
+ * @return {CacheEntry} - new CacheEntry instance
+ */
+ constructor(key, value) {
+ this._key = key;
+ this._value = value;
+ }
+
+ /**
+ * Returns the key corresponding to this entry.
+ *
+ * @return {*} - the key corresponding to this entry.
+ */
+ getKey() {
+ return this._key;
+ }
+
+ /**
+ * Returns the value corresponding to this entry.
+ *
+ * @return {*} - the value corresponding to this entry.
+ */
+ getValue() {
+ return this._value;
+ }
+}
+
+module.exports = CacheClient;
+module.exports.CacheEntry = CacheEntry;