IGNITE-9465: Node.js client: improved complex object flags processing

This closes #4713


Project: http://git-wip-us.apache.org/repos/asf/ignite/repo
Commit: http://git-wip-us.apache.org/repos/asf/ignite/commit/4b002427
Tree: http://git-wip-us.apache.org/repos/asf/ignite/tree/4b002427
Diff: http://git-wip-us.apache.org/repos/asf/ignite/diff/4b002427

Branch: refs/heads/ignite-7251
Commit: 4b002427874d1881a7ef467aaf0cce6071fc8e98
Parents: 2ab9493
Author: ekaterina-nbl <ekaterina.vergiz...@nobitlost.com>
Authored: Mon Sep 17 15:26:50 2018 +0300
Committer: Igor Sapego <isap...@apache.org>
Committed: Mon Sep 17 15:26:50 2018 +0300

----------------------------------------------------------------------
 modules/platforms/nodejs/README.md              | 613 +------------------
 modules/platforms/nodejs/examples/README.md     | 128 ----
 modules/platforms/nodejs/lib/BinaryObject.js    | 129 ++--
 .../platforms/nodejs/lib/internal/BinaryType.js |  50 +-
 .../nodejs/lib/internal/BinaryTypeStorage.js    |   5 +
 .../nodejs/lib/internal/BinaryUtils.js          |   2 +-
 .../nodejs/lib/internal/MessageBuffer.js        |  31 +-
 modules/platforms/nodejs/spec/README.md         |  42 --
 .../nodejs/spec/cache/ComplexObject.spec.js     |  76 +++
 9 files changed, 231 insertions(+), 845 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/README.md
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/README.md 
b/modules/platforms/nodejs/README.md
index 63a6725..4792caa 100644
--- a/modules/platforms/nodejs/README.md
+++ b/modules/platforms/nodejs/README.md
@@ -1,615 +1,32 @@
 # NodeJS Client for Apache Ignite #
 
-This client allows your application to work with the [Apache Ignite 
platform](https://ignite.apache.org/) via the [Binary Client 
Protocol](https://apacheignite.readme.io/docs/binary-client-protocol).
-
-The client includes:
-- [API 
specification](https://rawgit.com/nobitlost/ignite/master/modules/platforms/nodejs/api_spec/index.html)
-- [implementation](./lib)
-- [examples](./examples)
-- [tests](./spec)
-- docs
-  - the main readme (this file)
-  - [readme for examples](./examples/README.md)
-  - [readme for tests](./spec/README.md)
-
 ## Installation ##
 
 [Node.js](https://nodejs.org/en/) version 8 or higher is required. Either 
download the Node.js [pre-built binary](https://nodejs.org/en/download/) for 
the target platform, or install Node.js via [package 
manager](https://nodejs.org/en/download/package-manager).
 
-Once `node` and `npm` are installed, execute the following commands:
+Once `node` and `npm` are installed, you can use one of the following 
installation options.
 
-(temporary, while the NPM module is not released on 
[npmjs](https://www.npmjs.com))
+### Installation via npm ###
 
-1. Clone or download Ignite repository https://github.com/nobitlost/ignite.git 
to `local_ignite_path`
-2. Go to `local_ignite_path/modules/platforms/nodejs` folder
-3. Execute `npm link` command
-4. Execute `npm link apache-ignite-client` command (needed only for examples 
and tests)
+Execute the following command to install the Node.js Thin Client package:
 
-```bash
-cd local_ignite_path/modules/platforms/nodejs
-npm link
-npm link apache-ignite-client
 ```
-
-## Supported Features ##
-
-The client supports all operations and types from the [Binary Client Protocol 
v.2.4](https://apacheignite.readme.io/v2.4/docs/binary-client-protocol) except 
the following not-applicable features:
-- OP_REGISTER_BINARY_TYPE_NAME and OP_GET_BINARY_TYPE_NAME operations are not 
supported.
-- Filter object for OP_QUERY_SCAN operation is not supported. OP_QUERY_SCAN 
operation itself is supported.
-- It is not possible to register a new Ignite Enum type. Reading and writing 
items of the existing Ignite Enum types are supported.
-
-The following additional features are supported:
-- Authentication using username/password.
-- SSL/TLS connection.
-- "Failover re-connection algorithm".
-
-## API Specification ##
-
-Full specification of the client's public API is available 
[here](https://rawgit.com/nobitlost/ignite/master/modules/platforms/nodejs/api_spec/index.html)
-
-It is auto-generated from the [jsdoc](http://usejsdoc.org/) comments in source 
files and located in the [api_spec](./api_spec) folder.
-
-Promises async/await mechanism is used by the API and the client's 
implementation.
-
-## Data Types ##
-
-The client supports two cases of mapping between Ignite types defined by the 
Binary Client Protocol and JavaScript types:
-- default mapping,
-- explicit mapping.
-
-A mapping occurs every time an application writes or reads a field to/from an 
Ignite cache via the client's API. A field here is any data in a cache - key or 
value of a cache entry or a map, element of an array or set, field of a complex 
object, etc.
-
-Using the client's API methods, an application can explicitly specify an 
Ignite type for a field. The client uses this information during the field 
read/write operations. It returns the corresponding JavaScript type in results 
of read operations. It checks the corresponding JavaScript type in inputs of 
write operations.
-
-If an application does not explicitly specify an Ignite type for a field, the 
client uses default mapping during the field read/write operations.
-
-Default mapping between Ignite and JavaScript types is described 
[here](https://rawgit.com/nobitlost/ignite/master/modules/platforms/nodejs/api_spec/ObjectType.html).
-
-### Complex Object Type Support ###
-
-The client provides two ways to operate with the Ignite Complex Object type - 
in the deserialized form and in the binary form.
-
-An application can specify an Ignite type of a field by an instance of the 
*ComplexObjectType* class which references an instance of a JavaScript Object. 
In this case, when the application reads a value of the field, the client 
deserializes the received Ignite Complex Object and returns it to the client as 
an instance of the corresponding JavaScript Object. When the application writes 
a value of the field, the client expects an instance of the corresponding 
JavaScript Object and serializes it to the Ignite Complex Object.
-
-If an application does not specify an Ignite type of a field and reads a value 
of the field, the client returns the received Ignite Complex Object as an 
instance of the *BinaryObject* class - a binary form of the Ignite Complex 
Object. The *BinaryObject* allows to manipulate with it's content - read and 
write values of the object's fields, add and remove the fields, etc. Also, an 
application can create an instance of the *BinaryObject* class from a 
JavaScript Object. An application can write the *BinaryObject* as a value of a 
field in a cache, if that field has no explicitly specified Ignite type.
-
-The client takes care of obtaining or registering information about Ignite 
Complex Object type, including schema, from/at Ignite cluster. It is done 
automatically by the client, when required for reading or writing of the Ignite 
Complex Object from/to a cache.
-
-## Usage ##
-
-The below sections exaplains the basic steps to work with Apache Ignite using 
NodeJS client.
-
-### Instantiate Ignite Client ###
-
-A usage of the client starts from the creation of an *IgniteClient* class 
instance. The constructor has one, optional, parameter - *onStateChanged* 
callback which will be called every time the client moves to a new connection 
state (see below).
-
-It is possible to create as many *IgniteClient* instances as needed. All of 
them will work fully independently.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-
-const igniteClient = new IgniteClient(onStateChanged);
-
-function onStateChanged(state, reason) {
-    if (state === IgniteClient.STATE.CONNECTED) {
-        console.log('Client is started');
-    }
-    else if (state === IgniteClient.STATE.DISCONNECTED) {
-        console.log('Client is stopped');
-        if (reason) {
-            console.log(reason);
-        }
-    }
-}
+npm install -g apache-ignite-client
 ```
 
-### Create Ignite Client Configuration ###
-
-The next step is to define a configuration for the client's connection - 
create an *IgniteClientConfiguration* class instance.
-
-A mandatory part of the configuration, which is specified in the constructor, 
is a list of endpoints of the Ignite nodes. At least one endpoint must be 
specified. A client connects to one node only - a random endpoint from the 
provided list. Other nodes, if provided, are used by the client for the 
"failover re-connection algorithm": the client tries to re-connect to the next 
random endpoint from the list if the current connection has lost.
-
-Optional parts of the configuration can be specified using additional set 
methods. They include:
-- username and password for authentication,
-- SSL/TLS connection enabling,
-- NodeJS connection options.
-
-By default, the client establishes a non-secure connection with default 
connection options defined by NodeJS and does not use authentication.
-
-Example: default Ignite Client Configuration
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-
-const igniteClientConfiguration = new 
IgniteClientConfiguration('127.0.0.1:10800');
-```
-
-Example: Ignite Client Configuration with username/password authentication and 
additional connection options
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-
-const igniteClientConfiguration = new 
IgniteClientConfiguration('127.0.0.1:10800').
-    setUserName('ignite').
-    setPassword('ignite').
-    setConnectionOptions(false, { 'timeout' : 0 });
-```
-
-### Connect Ignite Client ###
-
-The next step is to connect the client to an Ignite node. The configuration 
for the client's connection, which includes endpoint(s) to connect to, is 
specified in the connect method.
-
-The client has three connection states - *CONNECTING*, *CONNECTED*, 
*DISCONNECTED*. A state is reported via *onStateChanged* callback, if that was 
provided in the client's constructor.
-
-Any operations with Ignite caches are possible in the *CONNECTED* state only.
-
-If the client unexpectedly lost the connection, it automatically moves to the 
*CONNECTING* state and tries to re-connect using the "failover re-connection 
algorithm". If not possible to connect to all endpoints from the provided list, 
the client moves to the *DISCONNECTED* state.
-
-At any moment, an application can call the disconnect method and forcibly 
moves the client to the *DISCONNECTED* state.
-
-When the client becomes disconnected, an application can call the connect 
method again - with the same or different configuration (eg. with different 
list of endpoints).
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-
-async function connectClient() {
-    const igniteClient = new IgniteClient(onStateChanged);
-    try {
-        const igniteClientConfiguration = new 
IgniteClientConfiguration('127.0.0.1:10800');
-        // connect to Ignite node
-        await igniteClient.connect(igniteClientConfiguration);
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-function onStateChanged(state, reason) {
-    if (state === IgniteClient.STATE.CONNECTED) {
-        console.log('Client is started');
-    }
-    else if (state === IgniteClient.STATE.DISCONNECTED) {
-        console.log('Client is stopped');
-        if (reason) {
-            console.log(reason);
-        }
-    }
-}
-
-connectClient();
-```
-
-### Obtain Cache Instance ###
-
-The next step is to obtain a Cache instance - an instance of the *CacheClient* 
class. One Cache instance gives access to one Ignite cache.
-
-The Ignite client provides several methods to manipulate with Ignite caches 
and obtain a Cache instance - get a cache by it's name, create a cache with the 
specified name and optional cache configuration, get or create a cache, 
destroys a cache, etc.
-
-It is possible to obtain as many *CacheClient* instances as needed - for the 
same or different Ignite caches - and work with all of them "in parallel".
-
-Example: get or create cache by name and destroy the cache
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-
-async function getOrCreateCacheByName() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        // get or create cache by name
-        const cache = await igniteClient.getOrCreateCache('myCache');
-
-        // perform cache key-value operations
-        // ...
-
-        // destroy cache
-        await igniteClient.destroyCache('myCache');
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-getOrCreateCacheByName();
-```
-
-Example: create cache by name and configuration
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const CacheConfiguration = IgniteClient.CacheConfiguration;
-
-async function createCacheByConfiguration() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        // create cache by name and configuration
-        const cache = await igniteClient.createCache(
-            'myCache',
-            new CacheConfiguration().setSqlSchema('PUBLIC'));
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-createCacheByConfiguration();
-```
-
-Example: get existing cache by name
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
+### Installation from Sources ###
 
-async function getExistingCache() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        // get existing cache by name
-        const cache = igniteClient.getCache('myCache');
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
+If you want to install the Thin Client library from Ignite sources, please 
follow the steps:
 
-getExistingCache();
-```
-
-### Configure Cache Instance ###
-
-The next step is optional.
-
-It is possible to specify concrete Ignite types for the key and/or the value 
of the cache. If the key and/or value is a non-primitive type (eg. a map, a 
collection, a complex object, etc.) it is possible to specify concrete Ignite 
types for fields of that objects as well.
-
-If Ignite type is not explicitly specified for some field, the client tries to 
make automatic default mapping between JavaScript types and Ignite object types.
-
-More details about types and mappings are clarified in the [Data 
Types](#data-types) section.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const ObjectType = IgniteClient.ObjectType;
-const MapObjectType = IgniteClient.MapObjectType;
-
-async function setCacheKeyValueTypes() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        const cache = await igniteClient.getOrCreateCache('myCache');
-        // set cache key/value types
-        cache.setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
-            setValueType(new MapObjectType(
-                MapObjectType.MAP_SUBTYPE.LINKED_HASH_MAP,
-                ObjectType.PRIMITIVE_TYPE.SHORT,
-                ObjectType.PRIMITIVE_TYPE.BYTE_ARRAY));
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-setCacheKeyValueTypes();
-```
-
-Now, everything is ready to manipulate with the data in the cache.
-
-### Key-Value Queries ###
-
-The *CacheClient* class provides methods to manipulate with the key and the 
value of the cache using Key-Value Queries operations - put, get, put all, get 
all, replace, clear, etc.
-
-Example: different cache Key-Value operations with primitive types
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const ObjectType = IgniteClient.ObjectType;
-const CacheEntry = IgniteClient.CacheEntry;
-
-async function performCacheKeyValueOperations() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        const cache = (await igniteClient.getOrCreateCache('myCache')).
-            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);
-        // put and get value
-        await cache.put(1, 'abc');
-        const value = await cache.get(1);
-
-        // put and get multiple values using putAll()/getAll() methods
-        await cache.putAll([new CacheEntry(2, 'value2'), new CacheEntry(3, 
'value3')]);
-        const values = await cache.getAll([1, 2, 3]);
-
-        // removes all entries from the cache
-        await cache.clear();
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-performCacheKeyValueOperations();
-```
-
-Example: cache put/get Complex Objects and Binary Objects
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const ObjectType = IgniteClient.ObjectType;
-const CacheEntry = IgniteClient.CacheEntry;
-const ComplexObjectType = IgniteClient.ComplexObjectType;
-
-class Person {
-    constructor(id = null, name = null, salary = null) {
-        this.id = id;
-        this.name = name;
-        this.salary = salary;
-    }
-}
-
-async function putGetComplexAndBinaryObjects() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        const cache = await igniteClient.getOrCreateCache('myPersonCache');
-        // Complex Object type for JavaScript Person class instances
-        const personComplexObjectType = new ComplexObjectType(new Person(0, 
'', 0)).
-            setFieldType('id', ObjectType.PRIMITIVE_TYPE.INTEGER); 
-        // set cache key and value types
-        cache.setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
-            setValueType(personComplexObjectType);
-        // put Complex Objects to the cache
-        await cache.put(1, new Person(1, 'John Doe', 1000));
-        await cache.put(2, new Person(2, 'Jane Roe', 2000));
-        // get Complex Object, returned value is an instance of Person class
-        const person = await cache.get(1);
-        console.log(person);
-
-        // new CacheClient instance of the same cache to operate with 
BinaryObjects
-        const binaryCache = igniteClient.getCache('myPersonCache').
-            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);
-        // get Complex Object from the cache in a binary form, returned value 
is an instance of BinaryObject class
-        let binaryPerson = await binaryCache.get(2);
-        console.log('Binary form of Person:');       
-        for (let fieldName of binaryPerson.getFieldNames()) {
-            let fieldValue = await binaryPerson.getField(fieldName);
-            console.log(fieldName + ' : ' + fieldValue);
-        }
-        // modify Binary Object and put it to the cache
-        binaryPerson.setField('id', 3, ObjectType.PRIMITIVE_TYPE.INTEGER).
-            setField('name', 'Mary Major');
-        await binaryCache.put(3, binaryPerson);
-
-        // get Binary Object from the cache and convert it to JavaScript Object
-        binaryPerson = await binaryCache.get(3);
-        console.log(await binaryPerson.toObject(personComplexObjectType));
-
-        await igniteClient.destroyCache('myPersonCache');
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-putGetComplexAndBinaryObjects();
-```
-
-### SQL, SQL Fields and Scan Queries ###
-
-The *CacheClient* class provides the query method that accepts an instance of 
a concrete query definition class and returns an instance of a concrete cursor 
class which can be used to obtain the results of the query.
-
-Every cursor class allows
-- either to iterate over the results of the query by obtaining one element of 
the results after another,
-- or to get all elements of the results in a one array at once.
-
-#### SQL Query ####
-
-First, define the query by creating and configuring an instance of the 
*SqlQuery* class.
-
-Then, pass the *SqlQuery* instance in to the query method of the Cache 
instance and obtain an instance of the *Cursor* class.
-
-Finally, use the *Cursor* instance to iterate over or get all cache entries 
returned by the query.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const CacheConfiguration = IgniteClient.CacheConfiguration;
-const QueryEntity = IgniteClient.QueryEntity;
-const QueryField = IgniteClient.QueryField;
-const ObjectType = IgniteClient.ObjectType;
-const ComplexObjectType = IgniteClient.ComplexObjectType;
-const CacheEntry = IgniteClient.CacheEntry;
-const SqlQuery = IgniteClient.SqlQuery;
-
-async function performSqlQuery() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        // cache configuration required for sql query execution
-        const cacheConfiguration = new CacheConfiguration().
-            setQueryEntities(
-                new QueryEntity().
-                    setValueTypeName('Person').
-                    setFields([
-                        new QueryField('name', 'java.lang.String'),
-                        new QueryField('salary', 'java.lang.Double')
-                    ]));
-        const cache = (await 
igniteClient.getOrCreateCache('sqlQueryPersonCache', cacheConfiguration)).
-            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER).
-            setValueType(new ComplexObjectType({ 'name' : '', 'salary' : 0 }, 
'Person'));
-
-        // put multiple values using putAll()
-        await cache.putAll([
-            new CacheEntry(1, { 'name' : 'John Doe', 'salary' : 1000 }),
-            new CacheEntry(2, { 'name' : 'Jane Roe', 'salary' : 2000 }),
-            new CacheEntry(2, { 'name' : 'Mary Major', 'salary' : 1500 })]);
-
-        // create and configure sql query
-        const sqlQuery = new SqlQuery('Person', 'salary > ? and salary <= ?').
-            setArgs(900, 1600);
-        // obtain sql query cursor
-        const cursor = await cache.query(sqlQuery);
-        // getAll cache entries returned by the sql query
-        for (let cacheEntry of await cursor.getAll()) {
-            console.log(cacheEntry.getValue());
-        }
-
-        await igniteClient.destroyCache('sqlQueryPersonCache');
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-performSqlQuery();
-```
-
-#### Scan Query ####
-
-First, define the query by creating and configuring an instance of the 
*ScanQuery* class.
-
-Then, pass the *ScanQuery* instance in to the query method of the Cache 
instance and obtain an instance of the *Cursor* class.
-
-Finally, use the *Cursor* instance to iterate over or get all cache entries 
returned by the query.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const ObjectType = IgniteClient.ObjectType;
-const CacheEntry = IgniteClient.CacheEntry;
-const ScanQuery = IgniteClient.ScanQuery;
-
-async function performScanQuery() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        const cache = (await igniteClient.getOrCreateCache('myCache')).
-            setKeyType(ObjectType.PRIMITIVE_TYPE.INTEGER);
-
-        // put multiple values using putAll()
-        await cache.putAll([
-            new CacheEntry(1, 'value1'),
-            new CacheEntry(2, 'value2'),
-            new CacheEntry(3, 'value3')]);
-
-        // create and configure scan query
-        const scanQuery = new ScanQuery().
-            setPageSize(1);
-        // obtain scan query cursor
-        const cursor = await cache.query(scanQuery);
-        // getAll cache entries returned by the scan query
-        for (let cacheEntry of await cursor.getAll()) {
-            console.log(cacheEntry.getValue());
-        }
-
-        await igniteClient.destroyCache('myCache');
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
-
-performScanQuery();
-```
-
-#### SQL Fields Query ####
-
-First, define the query by creating and configuring an instance of the 
*SqlFieldsQuery* class.
-
-Then, pass the *SqlFieldsQuery* instance in to the query method of the Cache 
instance and obtain an instance of the *SqlFieldsCursor* class.
-
-Finally, use the *SqlFieldsCursor* instance to iterate over or get all 
elements returned by the query.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-const IgniteClientConfiguration = IgniteClient.IgniteClientConfiguration;
-const CacheConfiguration = IgniteClient.CacheConfiguration;
-const ObjectType = IgniteClient.ObjectType;
-const CacheEntry = IgniteClient.CacheEntry;
-const SqlFieldsQuery = IgniteClient.SqlFieldsQuery;
-
-async function performSqlFieldsQuery() {
-    const igniteClient = new IgniteClient();
-    try {
-        await igniteClient.connect(new 
IgniteClientConfiguration('127.0.0.1:10800'));
-        const cache = await igniteClient.getOrCreateCache('myPersonCache', new 
CacheConfiguration().
-            setSqlSchema('PUBLIC'));
-
-        // create table using SqlFieldsQuery
-        (await cache.query(new SqlFieldsQuery(
-           'CREATE TABLE Person (id INTEGER PRIMARY KEY, firstName VARCHAR, 
lastName VARCHAR, salary DOUBLE)'))).getAll();
-
-        // insert data into the table
-        const insertQuery = new SqlFieldsQuery('INSERT INTO Person (id, 
firstName, lastName, salary) values (?, ?, ?, ?)').
-            setArgTypes(ObjectType.PRIMITIVE_TYPE.INTEGER);
-        (await cache.query(insertQuery.setArgs(1, 'John', 'Doe', 
1000))).getAll();
-        (await cache.query(insertQuery.setArgs(2, 'Jane', 'Roe', 
2000))).getAll();
-
-        // obtain sql fields cursor
-        const sqlFieldsCursor = await cache.query(
-            new SqlFieldsQuery("SELECT concat(firstName, ' ', lastName), 
salary from Person").
-                setPageSize(1));
-
-        // iterate over elements returned by the query
-        do {
-            console.log(await sqlFieldsCursor.getValue());
-        } while (sqlFieldsCursor.hasMore());
-
-        // drop the table
-        (await cache.query(new SqlFieldsQuery("DROP TABLE Person"))).getAll();
-    }
-    catch (err) {
-        console.log(err.message);
-    }
-    finally {
-        igniteClient.disconnect();
-    }
-}
+1. Download Ignite sources to `local_ignite_path`
+2. Go to `local_ignite_path/modules/platforms/nodejs` folder
+3. Execute `npm link` command
+4. Execute `npm link apache-ignite-client` command (needed only for examples)
 
-performSqlFieldsQuery();
+```bash
+cd local_ignite_path/modules/platforms/nodejs
+npm link
+npm link apache-ignite-client #linking examples (optional)
 ```
 
-### Enable Debug ###
-
-To switch on/off the client's debug output (including errors logging), call 
*setDebug()* method of the *IgniteClient* instance. Debug output is disabled by 
default.
-
-```javascript
-const IgniteClient = require('apache-ignite-client');
-
-const igniteClient = new IgniteClient();
-igniteClient.setDebug(true);
-```
+For more information, see [Apache Ignite Node.JS Thin Client 
documentation](https://apacheignite.readme.io/docs/nodejs-thin-client).

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/examples/README.md
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/examples/README.md 
b/modules/platforms/nodejs/examples/README.md
deleted file mode 100644
index 94b1433..0000000
--- a/modules/platforms/nodejs/examples/README.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# Examples #
-
-NodeJS Client for Apache Ignite contains fully workable examples to 
demonstrate the main behavior of the client.
-
-## Description ##
-
-### Sql Example ###
-
-Source: [SqlExample.js](./SqlExample.js)
-
-This example shows primary APIs to use with Ignite as with an SQL database:
-- connects to a node
-- creates a cache, if it doesn't exist
-- creates tables (CREATE TABLE)
-- creates indices (CREATE INDEX)
-- writes data of primitive types into the tables (INSERT INTO table)
-- reads data from the tables (SELECT ...)
-- deletes tables (DROP TABLE)
-- destroys the cache
-
-### Cache Put Get Example ###
-
-Source: [CachePutGetExample.js](./CachePutGetExample.js)
-
-This example demonstrates basic Cache, Key-Value Queries and Scan Query 
operations:
-- connects to a node
-- creates a cache, if it doesn't exist
-  - specifies key type as Integer
-- executes different cache operations with Complex Objects and Binary Objects
-  - put several objects in parallel
-  - putAll
-  - get
-  - getAll
-  - ScanQuery
-- destroys the cache
-
-### Sql Query Entries Example ###
-
-Source: [SqlQueryEntriesExample.js](./SqlQueryEntriesExample.js)
-
-This example demonstrates basic Cache, Key-Value Queries and SQL Query 
operations:
-- connects to a node
-- creates a cache from CacheConfiguration, if it doesn't exist
-- writes data of primitive and Complex Object types into the cache using 
Key-Value put operation
-- reads data from the cache using SQL Query
-- destroys the cache
-
-### Auth Tls Example ###
-
-Source: [AuthTlsExample.js](./AuthTlsExample.js)
-
-This example requires [additional setup](#additional-setup-for-authtlsexample).
-
-This example demonstrates how to establish a secure connection to an Ignite 
node and use username/password authentication, as well as basic Key-Value 
Queries operations for primitive types:
-- connects to a node using TLS and providing username/password
-- creates a cache, if it doesn't exist
-  - specifies key and value type of the cache
-- put data of primitive types into the cache
-- get data from the cache
-- destroys the cache
-
-
-### Failover Example ###
-
-Source: [FailoverExample.js](./FailoverExample.js)
-
-This example requires [additional 
setup](#additional-setup-for-failoverexample).
-
-This example demonstrates the failover behavior of the client
-- configures the client to connect to a set of nodes
-- connects to a node
-- if connection is broken, the client automatically tries to reconnect to 
another node
-- if no specified nodes are available, stops the client
-
-
-## Installation ##
-
-(temporary, while the NPM module is not released on 
[npmjs](https://www.npmjs.com))
-
-Examples are installed along with the client.
-Follow the [instructions in the main readme](../README.md#installation).
-
-## Setup and Running ##
-
-1. Run Apache Ignite server - locally or remotely.
-
-2. If needed, modify `ENDPOINT` constant in an example source file - Ignite 
node endpoint. The default value is `127.0.0.1:10800`.
-
-3. Run an example by calling `node <example_file_name>.js`. Eg. `node 
CachePutGetExample.js`
-
-## Additional Setup for AuthTlsExample ##
-
-1. Obtain certificates required for TLS:
-  - either use pre-generated certificates provided in the 
[examples/certs](./certs) folder. Password for the files: `123456`. Note, these 
certificates work for an Ignite server installed locally only.
-  - or obtain other existing certificates applicable for a concrete Ignite 
server.
-  - or generate new certificates applicable for a concrete Ignite server.
-
-  - The following files are needed:
-    - keystore.jks, truststore.jks - for the server side
-    - client.key, client.crt, ca.crt - for the client side
-
-2. Place client.key, client.crt and ca.crt files somewhere locally, eg. into 
the [examples/certs](./certs) folder.
-
-3. If needed, modify `TLS_KEY_FILE_NAME`, `TLS_CERT_FILE_NAME` and 
`TLS_CA_FILE_NAME` constants in the example source file. The default values 
point to the files in the [examples/certs](./certs) folder.
-
-4. Setup Apache Ignite server to accept TLS - see appropriate Ignite 
documentation. Provide the obtained keystore.jks and truststore.jks 
certificates during the setup.
-
-5. Switch on and setup authentication in Apache Ignite server - see 
appropriate Ignite documentation.
-
-6. If needed, modify `USER_NAME` and `PASSWORD` constants in the example 
source file. The default values are the default Ignite username/password.
-
-7. Executes [Setup and Running](#setup-and-running) steps.
-
-## Additional Setup for FailoverExample ##
-
-1. Run three Ignite nodes. See appropriate Ignite documentation for more 
details.
-
-2. If needed, modify `ENDPOINT1`, `ENDPOINT2`, `ENDPOINT2` constants in an 
example source file - Ignite node endpoints.
-Default values are `localhost:10800`, `localhost:10801`, `localhost:10802` 
respectively.
-
-2. Run an example by calling `node FailoverExample.js`. 
-
-3. Shut down the node the client connected to (you can find it out from the 
client logs in the console).
-
-4. From the logs, you will see that the client automatically reconnects to 
another node which is available.
-
-5. Shut down all the nodes. You will see the client being stopped after 
failing to connect to each of the nodes.
-

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/lib/BinaryObject.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/BinaryObject.js 
b/modules/platforms/nodejs/lib/BinaryObject.js
index fb139da..9be60da 100644
--- a/modules/platforms/nodejs/lib/BinaryObject.js
+++ b/modules/platforms/nodejs/lib/BinaryObject.js
@@ -77,7 +77,9 @@ class BinaryObject {
         this._typeBuilder = BinaryTypeBuilder.fromTypeName(typeName);
         this._modified = false;
         this._schemaOffset = null;
+        this._hasSchema = false;
         this._compactFooter = false;
+        this._hasRawData = false;
     }
 
     /**
@@ -301,14 +303,22 @@ class BinaryObject {
             await this._typeBuilder.finalize(communicator);
             this._startPos = buffer.position;
             buffer.position = this._startPos + HEADER_LENGTH;
-            // write fields
-            for (let field of this._fields.values()) {
-                await field._writeValue(communicator, buffer, 
this._typeBuilder.getField(field.id).typeCode);
+            this._hasSchema = (this._fields.size > 0);
+            if (this._hasSchema) {
+                let field;
+                // write fields
+                for (field of this._fields.values()) {
+                    await field._writeValue(communicator, buffer, 
this._typeBuilder.getField(field.id).typeCode);
+                }
+                this._schemaOffset = buffer.position - this._startPos;
+                this._offsetType = field.getOffsetType(this._startPos);
+                // write schema
+                for (let field of this._fields.values()) {
+                    field._writeOffset(buffer, this._startPos, 
this._offsetType);
+                }
             }
-            this._schemaOffset = buffer.position - this._startPos;
-            // write schema
-            for (let field of this._fields.values()) {
-                field._writeOffset(buffer, this._startPos);
+            else {
+                this._schemaOffset = 0;
             }
             this._length = buffer.position - this._startPos;
             this._buffer = buffer;
@@ -333,7 +343,17 @@ class BinaryObject {
         // version
         this._buffer.writeByte(VERSION);
         // flags
-        this._buffer.writeShort(FLAG_USER_TYPE | FLAG_HAS_SCHEMA | 
FLAG_COMPACT_FOOTER);
+        let flags = FLAG_USER_TYPE;
+        if (this._hasSchema) {
+            flags = flags | FLAG_HAS_SCHEMA | FLAG_COMPACT_FOOTER;
+        }
+        if (this._offsetType === BinaryUtils.TYPE_CODE.BYTE) {
+            flags = flags | FLAG_OFFSET_ONE_BYTE;
+        }
+        else if (this._offsetType === BinaryUtils.TYPE_CODE.SHORT) {
+            flags = flags | FLAG_OFFSET_TWO_BYTES;
+        }
+        this._buffer.writeShort(flags);
         // type id
         this._buffer.writeInteger(this._typeBuilder.getTypeId());
         // hash code
@@ -342,7 +362,7 @@ class BinaryObject {
         // length
         this._buffer.writeInteger(this._length);
         // schema id
-        this._buffer.writeInteger(this._typeBuilder.getSchemaId());
+        this._buffer.writeInteger(this._hasSchema ? 
this._typeBuilder.getSchemaId() : 0);
         // schema offset
         this._buffer.writeInteger(this._schemaOffset);
     }
@@ -352,37 +372,43 @@ class BinaryObject {
      */
     async _read(communicator) {
         await this._readHeader(communicator);
-        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);
+        if (this._hasSchema) {
+            this._buffer.position = this._startPos + this._schemaOffset;
+            const fieldOffsets = new Array();
+            const fieldIds = this._typeBuilder._schema.fieldIds;
+            let index = 0;
+            let fieldId;
+            let schemaEndOffset = this._startPos + this._length;
+            if (this._hasRawData) {
+                schemaEndOffset -= 
BinaryUtils.getSize(BinaryUtils.TYPE_CODE.INTEGER);
             }
-            else {
-                if (index >= fieldIds.length) {
-                    throw Errors.IgniteClientError.serializationError(
-                        false, 'wrong number of fields in schema');
+            while (this._buffer.position < schemaEndOffset) {
+                if (!this._compactFooter) {
+                    fieldId = this._buffer.readInteger();
+                    this._typeBuilder._schema.addField(fieldId);
                 }
-                fieldId = fieldIds[index];
-                index++;
+                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, false)]);
+            }
+            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(
+                    communicator,this._buffer, this._startPos + offset, 
nextOffset - offset, fieldId);
+                this._fields.set(field.id, field);
             }
-            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(
-                communicator,this._buffer, this._startPos + offset, nextOffset 
- offset, fieldId);
-            this._fields.set(field.id, field);
         }
         this._buffer.position = this._startPos + this._length;
     }
@@ -410,23 +436,15 @@ class BinaryObject {
         const schemaId = this._buffer.readInteger();
         // schema offset
         this._schemaOffset = this._buffer.readInteger();
-        const hasSchema = BinaryObject._isFlagSet(flags, FLAG_HAS_SCHEMA);
+        this._hasSchema = BinaryObject._isFlagSet(flags, FLAG_HAS_SCHEMA);
         this._compactFooter = BinaryObject._isFlagSet(flags, 
FLAG_COMPACT_FOOTER);
+        this._hasRawData = BinaryObject._isFlagSet(flags, FLAG_HAS_RAW_DATA);
         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(flags, 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(communicator, 
typeId, schemaId, hasSchema);
+        this._typeBuilder = await BinaryTypeBuilder.fromTypeId(communicator, 
typeId, this._compactFooter ? schemaId : null);
     }
 }
 
@@ -465,6 +483,17 @@ class BinaryObjectField {
         return this._value;
     }
 
+    getOffsetType(headerStartPos) {
+        let offset = this._offset - headerStartPos;
+        if (offset < 0x100) {
+            return BinaryUtils.TYPE_CODE.BYTE;
+        }
+        else if (offset < 0x10000) {
+            return BinaryUtils.TYPE_CODE.SHORT;
+        }
+        return BinaryUtils.TYPE_CODE.INTEGER;        
+    }
+
     static _fromBuffer(communicator, buffer, offset, length, id) {
         const result = new BinaryObjectField(null);
         result._id = id;
@@ -493,8 +522,8 @@ class BinaryObjectField {
         this._offset = offset;
     }
 
-    _writeOffset(buffer, headerStartPos) {
-        buffer.writeInteger(this._offset - headerStartPos);
+    _writeOffset(buffer, headerStartPos, offsetType) {
+        buffer.writeNumber(this._offset - headerStartPos, offsetType, false);
     }
 }
 

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/lib/internal/BinaryType.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/internal/BinaryType.js 
b/modules/platforms/nodejs/lib/internal/BinaryType.js
index 98aa6a3..a111ed0 100644
--- a/modules/platforms/nodejs/lib/internal/BinaryType.js
+++ b/modules/platforms/nodejs/lib/internal/BinaryType.js
@@ -18,6 +18,7 @@
 'use strict';
 
 const Util = require('util');
+const Long = require('long');
 const ComplexObjectType = require('../ObjectType').ComplexObjectType;
 const BinaryTypeStorage = require('./BinaryTypeStorage');
 const BinaryUtils = require('./BinaryUtils');
@@ -104,6 +105,15 @@ class BinaryType {
         return result;
     }
 
+    isValid() {
+        for (let field of this._fields.values()) {
+            if (!field.isValid()) {
+                return false;
+            }
+        }
+        return this._name !== null;
+    }
+
     static _calculateId(name) {
         return BinaryUtils.hashCodeLowerCase(name);
     }
@@ -245,19 +255,16 @@ class BinarySchema {
     }
 
     static _updateSchemaId(schemaId, fieldId) {
-        schemaId = schemaId ^ (fieldId & 0xFF);
-        schemaId = schemaId * FNV1_PRIME;
-        schemaId |= 0;
-        schemaId = schemaId ^ ((fieldId >> 8) & 0xFF);
-        schemaId = schemaId * FNV1_PRIME;
-        schemaId |= 0;
-        schemaId = schemaId ^ ((fieldId >> 16) & 0xFF);
-        schemaId = schemaId * FNV1_PRIME;
-        schemaId |= 0;
-        schemaId = schemaId ^ ((fieldId >> 24) & 0xFF);
-        schemaId = schemaId * FNV1_PRIME;
-        schemaId |= 0;
+        schemaId = BinarySchema._updateSchemaIdPart(schemaId, fieldId & 0xFF);
+        schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 8) & 
0xFF);
+        schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 16) 
& 0xFF);
+        schemaId = BinarySchema._updateSchemaIdPart(schemaId, (fieldId >> 24) 
& 0xFF);
+        return schemaId;
+    }
 
+    static _updateSchemaIdPart(schemaId, fieldIdPart) {
+        schemaId = schemaId ^ fieldIdPart;
+        schemaId = Long.fromValue(schemaId).multiply(FNV1_PRIME).getLowBits();
         return schemaId;
     }
 
@@ -304,6 +311,10 @@ class BinaryField {
         return this._typeCode;
     }
 
+    isValid() {
+        return this._name !== null;
+    }
+
     static _calculateId(name) {
         return BinaryUtils.hashCodeLowerCase(name);
     }
@@ -335,12 +346,12 @@ class BinaryTypeBuilder {
         return result;
     }
 
-    static async fromTypeId(communicator, typeId, schemaId, hasSchema) {
+    static async fromTypeId(communicator, typeId, schemaId) {
         let result = new BinaryTypeBuilder();
-        if (hasSchema) {
-            let type = await communicator.typeStorage.getType(typeId, 
schemaId);
-            if (type) {
-                result._type = type;
+        let type = await communicator.typeStorage.getType(typeId, schemaId);
+        if (type) {
+            result._type = type;
+            if (schemaId !== null) {
                 result._schema = type.getSchema(schemaId);
                 if (!result._schema) {
                     throw Errors.IgniteClientError.serializationError(
@@ -348,8 +359,11 @@ class BinaryTypeBuilder {
                             schemaId, type.name));
                 }
                 result._fromStorage = true;
-                return result;
             }
+            else {
+                result._schema = new BinarySchema();
+            }
+            return result;
         }
         result._init(null);
         result._type._id = typeId;

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/lib/internal/BinaryTypeStorage.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/internal/BinaryTypeStorage.js 
b/modules/platforms/nodejs/lib/internal/BinaryTypeStorage.js
index 144710a..2248eb5 100644
--- a/modules/platforms/nodejs/lib/internal/BinaryTypeStorage.js
+++ b/modules/platforms/nodejs/lib/internal/BinaryTypeStorage.js
@@ -19,6 +19,7 @@
 
 const Errors = require('../Errors');
 const BinaryUtils = require('./BinaryUtils');
+const Util = require('util');
 
 class BinaryTypeStorage {
 
@@ -96,6 +97,10 @@ class BinaryTypeStorage {
     }
 
     async _putBinaryType(binaryType) {
+        if (!binaryType.isValid()) {
+            throw Errors.IgniteClientError.serializationError(
+                true, Util.format('type "%d" can not be registered', 
binaryType.id));
+        }
         await this._communicator.send(
             BinaryUtils.OPERATION.PUT_BINARY_TYPE,
             async (payload) => {

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/lib/internal/BinaryUtils.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/internal/BinaryUtils.js 
b/modules/platforms/nodejs/lib/internal/BinaryUtils.js
index ab2d40c..2619df7 100644
--- a/modules/platforms/nodejs/lib/internal/BinaryUtils.js
+++ b/modules/platforms/nodejs/lib/internal/BinaryUtils.js
@@ -306,7 +306,7 @@ class BinaryUtils {
         const BinaryObject = require('../BinaryObject');
         const objectType = typeof object;
         if (object === null) {
-            return BinaryUtils.TYPE_CODE.NULL;
+            throw 
Errors.IgniteClientError.unsupportedTypeError(BinaryUtils.TYPE_CODE.NULL);
         }
         else if (objectType === 'number') {
             return BinaryUtils.TYPE_CODE.DOUBLE;

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/lib/internal/MessageBuffer.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/lib/internal/MessageBuffer.js 
b/modules/platforms/nodejs/lib/internal/MessageBuffer.js
index dc3e928..b3be7e9 100644
--- a/modules/platforms/nodejs/lib/internal/MessageBuffer.js
+++ b/modules/platforms/nodejs/lib/internal/MessageBuffer.js
@@ -105,19 +105,34 @@ class MessageBuffer {
         this.writeNumber(value, BinaryUtils.TYPE_CODE.DOUBLE);
     }
 
-    writeNumber(value, type) {
+    writeNumber(value, type, signed = true) {
         const size = BinaryUtils.getSize(type);
         this._ensureCapacity(size);
         try {
             switch (type) {
                 case BinaryUtils.TYPE_CODE.BYTE:
-                    this._buffer.writeInt8(value, this._position);
+                    if (signed) {
+                        this._buffer.writeInt8(value, this._position);
+                    }
+                    else {
+                        this._buffer.writeUInt8(value, this._position);   
+                    }
                     break;
                 case BinaryUtils.TYPE_CODE.SHORT:
-                    this._buffer.writeInt16LE(value, this._position);
+                    if (signed) {
+                        this._buffer.writeInt16LE(value, this._position);
+                    }
+                    else {
+                        this._buffer.writeUInt16LE(value, this._position);   
+                    }
                     break;
                 case BinaryUtils.TYPE_CODE.INTEGER:
-                    this._buffer.writeInt32LE(value, this._position);
+                    if (signed) {
+                        this._buffer.writeInt32LE(value, this._position);
+                    }
+                    else {
+                        this._buffer.writeUInt32LE(value, this._position);   
+                    }
                     break;
                 case BinaryUtils.TYPE_CODE.FLOAT:
                     this._buffer.writeFloatLE(value, this._position);
@@ -184,19 +199,19 @@ class MessageBuffer {
         return this.readNumber(BinaryUtils.TYPE_CODE.DOUBLE);
     }
 
-    readNumber(type) {
+    readNumber(type, signed = true) {
         const size = BinaryUtils.getSize(type);
         this._ensureSize(size);
         let value;
         switch (type) {
             case BinaryUtils.TYPE_CODE.BYTE:
-                value = this._buffer.readInt8(this._position);
+                value = signed ? this._buffer.readInt8(this._position) : 
this._buffer.readUInt8(this._position);
                 break;
             case BinaryUtils.TYPE_CODE.SHORT:
-                value = this._buffer.readInt16LE(this._position);
+                value = signed ? this._buffer.readInt16LE(this._position) : 
this._buffer.readUInt16LE(this._position);
                 break;
             case BinaryUtils.TYPE_CODE.INTEGER:
-                value = this._buffer.readInt32LE(this._position);
+                value = signed ? this._buffer.readInt32LE(this._position) : 
this._buffer.readUInt32LE(this._position);
                 break;
             case BinaryUtils.TYPE_CODE.FLOAT:
                 value = this._buffer.readFloatLE(this._position);

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/spec/README.md
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/README.md 
b/modules/platforms/nodejs/spec/README.md
deleted file mode 100644
index 4b947c4..0000000
--- a/modules/platforms/nodejs/spec/README.md
+++ /dev/null
@@ -1,42 +0,0 @@
-# Tests #
-
-NodeJS Client for Apache Ignite contains 
[Jasmine](https://www.npmjs.com/package/jasmine) tests to check the behavior of 
the client. the tests include:
-- functional tests which cover all API methods of the client
-- examples executors which run all examples except AuthTlsExample
-- AuthTlsExample executor
-
-## Tests Installation ##
-
-(temporary, while the NPM module is not released on 
[npmjs](https://www.npmjs.com))
-
-Tests are installed along with the client.
-Follow the [instructions in the main readme](../README.md#installation).
-
-## Tests Running ##
-
-1. Run Apache Ignite server locally or remotely with default configuration.
-2. Set the environment variable:
-    - **APACHE_IGNITE_CLIENT_ENDPOINTS** - comma separated list of Ignite node 
endpoints.
-    - **APACHE_IGNITE_CLIENT_DEBUG** - (optional) if *true*, tests will 
display additional output (default: *false*).
-3. Alternatively, instead of the environment variables setting, you can 
directly specify the values of the corresponding variables in 
[local_ignite_path/modules/platforms/nodejspec/config.js](./config.js) file.
-4. Run the tests:
-
-### Run Functional Tests ###
-
-Call `npm test` command from `local_ignite_path/modules/platforms/nodejs` 
folder.
-
-### Run Examples Executors ###
-
-Call `npm run test:examples` command from 
`local_ignite_path/modules/platforms/nodejs` folder.
-
-### Run AuthTlsExample Executor ###
-
-It requires running Apache Ignite server with non-default configuration 
(authentication and TLS switched on).
-
-If the server runs locally:
-- setup the server to accept TLS. During the setup use `keystore.jks` and 
`truststore.jks` certificates from 
`local_ignite_path/modules/platforms/nodejs/examples/certs/` folder. Password 
for the files: `123456`
-- switch on the authentication on the server. Use the default 
username/password.
-
-If the server runs remotely, and/or other certificates are required, and/or 
non-default username/password is required - see this 
[instruction](../examples/README.md#additional-setup-for-authtlsexample).
-
-Call `npm run test:auth_example` command from 
`local_ignite_path/modules/platforms/nodejs` folder.

http://git-wip-us.apache.org/repos/asf/ignite/blob/4b002427/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
----------------------------------------------------------------------
diff --git a/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js 
b/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
index 2119ab5..9cc8115 100644
--- a/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
+++ b/modules/platforms/nodejs/spec/cache/ComplexObject.spec.js
@@ -28,6 +28,9 @@ const ComplexObjectType = IgniteClient.ComplexObjectType;
 const BinaryObject = IgniteClient.BinaryObject;
 
 const CACHE_NAME = '__test_cache';
+const ONE_BYTE_MAX_OFFSET = 0x100 - 1;
+const TWO_BYTES_MAX_OFFSET = 0x10000 - 1;
+const COMPLEX_OBJECT_HEADER_LENGTH = 24;
 
 class Class1 {
     constructor() {
@@ -323,6 +326,79 @@ describe('complex object test suite >', () => {
             catch(error => done.fail(error));
     });
 
+    it('put get complex objects with one byte offset', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const key = new Date();
+                const valueType = new ComplexObjectType(new Class2(), 
'Class2WithStrings');
+                const value = new Class2();
+                value.field_2_1 = 'x';
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+                const oneByteMaxLen = getMaxFieldLength(true);
+                value.field_2_1 = 'x'.repeat(oneByteMaxLen);
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get complex objects with two bytes offset', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const key = new Date();
+                const valueType = new ComplexObjectType(new Class2(), 
'Class2WithStrings');
+                const value = new Class2();
+                const oneByteMaxLen = getMaxFieldLength(true);
+                value.field_2_1 = 'x'.repeat(oneByteMaxLen + 1);
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+                const twoBytesMaxLen = getMaxFieldLength(false);
+                value.field_2_1 = 'x'.repeat(twoBytesMaxLen);
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get complex objects with four bytes offset', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const key = new Date();
+                const valueType = new ComplexObjectType(new Class2(), 
'Class2WithStrings');
+                const value = new Class2();
+                const twoBytesMaxLen = getMaxFieldLength(false);
+                value.field_2_1 = 'x'.repeat(twoBytesMaxLen + 1);
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+                value.field_2_1 = 'x'.repeat(twoBytesMaxLen * 2);
+                value.field_2_2 = 'y';
+                await putGetComplexObjects(key, value, null, valueType, value);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    it('put get complex objects without schema', (done) => {
+        Promise.resolve().
+            then(async () => {
+                const key = new Date();
+                const valueType = new ComplexObjectType({});
+                const value = {};
+                await putGetComplexObjects(key, value, null, valueType, value);
+            }).
+            then(done).
+            catch(error => done.fail(error));
+    });
+
+    function getMaxFieldLength(oneByte) {
+        const maxOffset = oneByte ? ONE_BYTE_MAX_OFFSET : TWO_BYTES_MAX_OFFSET;
+        // max offset - field type code - field type (string) length - complex 
object header length
+        return maxOffset - 1 - 4 - COMPLEX_OBJECT_HEADER_LENGTH;
+    }
+
     async function testSuiteCleanup(done) {
         await TestingHelper.destroyCache(CACHE_NAME, done);
     }

Reply via email to