dl-nosqldb
Version:
DL nosqldb library
585 lines (509 loc) • 19 kB
JavaScript
'use strict';
/*jslint es6 */
const Long = require('long');
const opal = require('dl-opal-client');
const Errors = require('./Errors.js');
const Promisify = require('es6-promisify');
class Nosqldb {
constructor(datasource) {
this._datasource = datasource;
this._rpcUrl = this._getRpcUrl(datasource);
this._metadata = null;
this._timerId = setInterval(this._refreshMetadata.bind(this), 60 * 1000);
}
_getRpcUrl(datasource) {
const keyspace = datasource.split('.')[0];
return `${keyspace}.nosqldb.onetapi.pl`;
}
_nosqldbRpc(method, params, cb) {
const request = {
interface: this._rpcUrl,
method: method,
params: params,
options: {
timeout: Nosqldb.REQUEST_TIMEOUT
}
};
opal.call(request, (err, data) => {
if (err) {
return cb(err);
}
return cb(null, data);
});
}
_getMetadata(refresh, cb) {
if (this._metadata && !refresh) {
return cb(null, this._metadata);
}
this._nosqldbRpc('get_metadata', [], (err, res) => {
if (err) {
console.error('Error while getting metadata: ', err);
return cb(err);
}
this._metadata = res;
if (cb) {
return cb(null, this._metadata);
}
});
}
_refreshMetadata() {
this._getMetadata(true);
}
_encodeValue(value, typestring) {
if (value === null) {
return value;
}
const m = typestring.match(/(\w+)<([\w, ]+)>/);
if (typestring === 'timestamp') {
return value.getTime();
}
else if (typestring === 'bigint' || typestring === 'counter' || typestring === 'decimal') {
return value.toString();
}
else if (typestring === 'blob') {
return value.toString('hex');
}
else if (m) {
const collection = m[1];
const t = m[2];
if (collection === 'set' || collection === 'list') {
return value.map((v) => {
return this._encodeValue(v, t);
});
}
else if (collection === 'map') {
const tSplit = t.split(', ');
const tK = tSplit[0];
const tV = tSplit[1];
const encodedMap = [];
for (let k in value) {
if (value.hasOwnProperty(k)) {
encodedMap.push([this._encodeValue(k, tK), this._encodeValue(value[k], tV)]);
}
}
return encodedMap;
}
}
return value;
}
_decodeValue(value, typestring) {
if (value === null) {
return value;
}
const m = typestring.match(/(\w+)<([\w, ]+)>/);
if (typestring === 'timestamp') {
return new Date(value);
}
else if (typestring === 'bigint' || typestring === 'counter') {
return Long.fromString(value, false);
}
else if (typestring === 'decimal') {
return parseFloat(value);
}
else if (typestring === 'blob') {
return new Buffer(value, 'hex');
}
else if (m) {
const collection = m[1];
const t = m[2];
if (collection === 'set' || collection === 'list') {
return value.map((v) => {
return this._decodeValue(v, t);
});
}
else if (collection === 'map') {
const tSplit = t.split(', ');
const tK = tSplit[0];
const tV = tSplit[1];
const decodedMap = {};
value.forEach((item) => {
decodedMap[this._decodeValue(item[0], tK)] = this._decodeValue(item[1], tV);
});
return decodedMap;
}
}
return value;
}
_encodeKey(table, key) {
if (!(key instanceof Array)) {
key = [key];
}
const tableMetadata = this._metadata.tables[table];
let encodedKey = [];
for (let i = 0; i < key.length; i++) {
const typestring = tableMetadata.primary_key[i].typestring;
encodedKey[i] = this._encodeValue(key[i], typestring);
}
return encodedKey;
}
_encodeColumns(table, columns) {
const tableMetadata = this._metadata.tables[table];
let encodedColumns = {};
for (let name in columns) {
if (columns.hasOwnProperty(name)) {
const typestring = tableMetadata.columns[name].typestring;
encodedColumns[name] = this._encodeValue(columns[name], typestring);
}
}
return encodedColumns;
}
_encodeAssignments(table, assignments) {
const tableMetadata = this._metadata.tables[table];
let encodedAssignments = {};
for (let name in assignments) {
if (assignments.hasOwnProperty(name)) {
const typestring = tableMetadata.columns[name].typestring;
let operator = assignments[name][0];
let value = assignments[name][1];
const m = typestring.match(/(\w+)<([\w, ]+)>/);
if ((operator instanceof Array) && operator.length === 1 && m) {
const collection = m[1];
const t = m[2];
if (collection === 'list') {
value = this._encodeValue(value, t);
}
else if (collection === 'map') {
const tSplit = t.split(', ');
const tK = tSplit[0];
const tV = tSplit[1];
operator = [this._encodeValue(operator[0], tK)];
value = this._encodeValue(value, tV);
}
}
else {
value = this._encodeValue(value, typestring);
}
encodedAssignments[name] = [operator, value];
}
}
return encodedAssignments;
}
_handleError(err) {
switch (err.code) {
case Errors.INVALID_REQUEST:
return new Errors.InvalidRequest(err.message);
case Errors.REQUEST_ERROR:
return new Errors.RequestError(err.message);
case Errors.CONSISTENCY_ERROR:
return new Errors.ConsistencyError(err.message);
case Errors.CLUSTER_ERROR:
return new Errors.ClusterError(err.message);
case Errors.CONNECTION_ERROR:
return new Errors.ConnectionError(err.message);
default:
return err;
}
}
_decodeRow(table, row) {
const tableMetadata = this._metadata.tables[table];
for (let name in row) {
if (row.hasOwnProperty(name)) {
const typestring = tableMetadata.columns[name].typestring;
row[name] = this._decodeValue(row[name], typestring);
}
}
return row;
}
_handleResult(table, res) {
if (!res) {
return res;
}
let that = this;
if (res instanceof Array) {
return res.map(function decodeRow(row) {
return that._decodeRow(table, row);
});
}
return this._decodeRow(table, res);
}
insert(table, key, columns, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._insertMethod(table, key, columns, options, cb);
}
const promisify = Promisify(this._insertMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(table, key, columns, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_insertMethod(table, key, columns, options, cb) {
const ttl = options.ttl || null;
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
key = this._encodeKey(table, key);
columns = this._encodeColumns(table, columns);
this._nosqldbRpc('insert', [table, key, columns, ttl, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null);
});
});
}
multiInsert(items, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._multiInsertMethod(items, options, cb);
}
const promisify = Promisify(this._multiInsertMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(items, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_multiInsertMethod(items, options = {}, cb) {
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
let encodedItems = items.map((item) => {
return {
table: item.table,
key: this._encodeKey(item.table, item.key),
columns: this._encodeColumns(item.table, item.columns),
ttl: item.ttl
};
});
this._nosqldbRpc('multi_insert', [encodedItems, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null);
});
});
}
update(table, key, assignments, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._updateMethod(table, key, assignments, options, cb);
}
const promisify = Promisify(this._updateMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(table, key, assignments, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_updateMethod(table, key, assignments, options = {}, cb) {
const ttl = options.ttl || null;
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
key = this._encodeKey(table, key);
assignments = this._encodeAssignments(table, assignments);
this._nosqldbRpc('update', [table, key, assignments, ttl, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null);
});
});
}
get(table, key, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._getMethod(table, key, options, cb);
}
const promisify = Promisify(this._getMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(table, key, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_getMethod(table, key, options = {}, cb) {
const columns = options.columns || null;
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
key = this._encodeKey(table, key);
this._nosqldbRpc('get', [table, key, columns, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null, data);
});
});
}
multiGet(items, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._multiGetMethod(items, options, cb);
}
const promisify = Promisify(this._multiGetMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(items, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_multiGetMethod(items, options = {}, cb) {
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
let encodedItems = items.map((item) => {
return {
table: item.table,
key: this._encodeKey(item.table, item.key),
columns: item.columns
};
});
this._nosqldbRpc('multi_get', [encodedItems, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null, data);
});
});
}
getRange(table, keyPrefix, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._getRangeMethod(table, keyPrefix, options, cb);
}
const promisify = Promisify(this._getRangeMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(table, keyPrefix, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_getRangeMethod(table, keyPrefix, options = {}, cb) {
const rangeLt = options.rangeLt || null;
const rangeLte = options.rangeLte || null;
const rangeGt = options.rangeGt || null;
const rangeGte = options.rangeGte || null;
const columns = options.columns || null;
const orderBy = options.orderBy || null;
const limit = options.limit || null;
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
keyPrefix = this._encodeKey(table, keyPrefix);
this._nosqldbRpc('get_range', [table, keyPrefix, rangeLt, rangeLte, rangeGt, rangeGte, columns, orderBy, limit, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null, data);
});
});
}
remove(table, key, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._removeMethod(table, key, options, cb);
}
const promisify = Promisify(this._removeMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(table, key, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_removeMethod(table, key, options = {}, cb) {
const columns = options.columns || null;
const consistency = options.consistency || 'default';
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
key = this._encodeKey(table, key);
this._nosqldbRpc('remove', [table, key, columns, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null);
});
});
}
batch(batch, options = {}, cb = null) {
if (typeof cb === 'function') {
return this._batchMethod(batch, options, cb);
}
const promisify = Promisify(this._batchMethod.bind(this));
return new Promise((resolve, reject) => {
promisify(batch, options)
.then((data) => {
resolve(data);
})
.catch((err) => {
reject(err);
});
});
}
_batchMethod(batch, options = {}, cb) {
const consistency = options.consistency || 'default';
let operations = [];
this._getMetadata(false, (err, data) => {
if (err) {
return cb(err);
}
batch.forEach((operation) => {
const name = operation.name;
const params = operation.params;
if (name === 'insert') {
operations.push(['insert', [
params.table,
this._encodeKey(params.table, params.key),
this._encodeColumns(params.table, params.columns),
params.ttl
]]);
}
else if (name === 'remove') {
operations.push(['remove', [
params.table,
this._encodeKey(params.table, params.key),
params.columns
]]);
}
});
this._nosqldbRpc('batch', [operations, consistency], (err, data) => {
if (err) {
return cb(this._handleError(err));
}
return cb(null);
});
});
}
destroy() {
clearInterval(this._timerId);
}
}
exports.Nosqldb = Nosqldb;
Nosqldb.REQUEST_TIMEOUT = 1000;