UNPKG

dl-nosqldb

Version:

DL nosqldb library

585 lines (509 loc) 19 kB
'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;