未验证 提交 defb4739 编写于 作者: H hzcheng 提交者: GitHub

Merge pull request #170 from StoneT2000/master

Added node.js connector and example usage of node.js
......@@ -6,13 +6,13 @@ TDengine is an open-sourced big data platform under [GNU AGPL v3.0](http://www.g
- **10x Faster on Insert/Query Speeds**: Through the innovative design on storage, on a single-core machine, over 20K requests can be processed, millions of data points can be ingested, and over 10 million data points can be retrieved in a second. It is 10 times faster than other databases.
- **1/5 Hardware/Cloud Service Costs**: Compared with typical big data solutions, less than 1/5 of computing resources are required. Via column-based storage and tuned compression algorithms for different data types, less than 1/10 of storage space is needed.
- **1/5 Hardware/Cloud Service Costs**: Compared with typical big data solutions, less than 1/5 of computing resources are required. Via column-based storage and tuned compression algorithms for different data types, less than 1/10 of storage space is needed.
- **Full Stack for Time-Series Data**: By integrating a database with message queuing, caching, and stream computing features together, it is no longer necessary to integrate Kafka/Redis/HBase/Spark or other software. It makes the system architecture much simpler and more robust..
- **Powerful Data Analysis**: Whether it is 10 years or one minute ago, data can be queried just by specifying the time range. Data can be aggregated over time, multiple time streams or both. Ad Hoc queries or analyses can be executed via TDengine shell, Python, R or Matlab.
- **Powerful Data Analysis**: Whether it is 10 years or one minute ago, data can be queried just by specifying the time range. Data can be aggregated over time, multiple time streams or both. Ad Hoc queries or analyses can be executed via TDengine shell, Python, R or Matlab.
- **Seamless Integration with Other Tools**: Telegraf, Grafana, Matlab, R, and other tools can be integrated with TDengine without a line of code. MQTT, OPC, Hadoop, Spark, and many others will be integrated soon.
- **Seamless Integration with Other Tools**: Telegraf, Grafana, Matlab, R, and other tools can be integrated with TDengine without a line of code. MQTT, OPC, Hadoop, Spark, and many others will be integrated soon.
- **Zero Management, No Learning Curve**: It takes only seconds to download, install, and run it successfully; there are no other dependencies. Automatic partitioning on tables or DBs. Standard SQL is used, with C/C++, Python, JDBC, Go and RESTful connectors.
......@@ -30,7 +30,7 @@ mkdir build && cd build
cmake .. && cmake --build .
```
# Running
# Running
<!-- TDengine uses _/etc/taos/taos.cfg_ as the default configuration file. This behavior can be changed with _-c_ option. For a quick start, we will make directories structured as:
```
test/
......@@ -55,7 +55,7 @@ In another terminal, use the TDengine shell to connect the server:
./build/bin/taos -c test/cfg
```
# Installing
# Installing
After building successfully, TDengine can be installed by:
```cmd
make install
......@@ -95,13 +95,14 @@ TDengine provides abundant developing tools for users to develop on TDengine. Fo
- [Python](https://www.taosdata.com/en/documentation/connector/#Python-Connector)
- [Go](https://www.taosdata.com/en/documentation/connector/#Go-Connector)
- [RESTful API](https://www.taosdata.com/en/documentation/connector/#RESTful-Connector)
- [Node.js](https://www.taosdata.com/en/documentation/connector/#Node.js-Connector)
# TDengine Roadmap
- Support event-driven stream computing
- Support event-driven stream computing
- Support user defined functions
- Support MQTT connection
- Support OPC connection
- Support Hadoop, Spark connections
- Support Hadoop, Spark connections
- Support Tableau and other BI tools
# Contribute to TDengine
......
const ref = require('ref');
const ffi = require('ffi');
const ArrayType = require('ref-array');
const Struct = require('ref-struct');
const fieldTypes = require('./constants');
const errors = require ('./error')
module.exports = CTaosInterface;
function convertMillisecondsToDatetime(time) {
return new Date(time);
}
function convertMicrosecondsToDatetime(time) {
return new Date(time * 0.001);
}
function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
timestampConverter = convertMillisecondsToDatetime;
if (micro == true) {
timestampConverter = convertMicrosecondsToDatetime;
}
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let queue = [];
let time = 0;
for (let i = currOffset; i < currOffset + nbytes; i++) {
queue.push(data[i]);
if (data[i] == 0) {
break;
}
}
for (let i = queue.length - 1; i >= 0; i--) {
time += queue[i] * Math.pow(16, i * 2);
}
currOffset += nbytes;
res.push(timestampConverter(time));
}
return res;
}
function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = new Array(data.length);
for (let i = 0; i < data.length; i++) {
if (data[i] == 0) {
res[i] = false;
}
else {
res[i] = true;
}
}
return res;
}
function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(data.readIntLE(currOffset,1));
currOffset += nbytes;
}
return res;
}
function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(data.readIntLE(currOffset,2));
currOffset += nbytes;
}
return res;
}
function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(data.readInt32LE(currOffset));
currOffset += nbytes;
}
return res;
}
function readBigInt64LE(buffer, offset = 0) {
const first = buffer[offset];
const last = buffer[offset + 7];
if (first === undefined || last === undefined)
boundsError(offset, buffer.length - 8);
const val = buffer[offset + 4] + buffer[offset + 5] * 2 ** 8 + buffer[offset + 6] * 2 ** 16 + (last << 24); // Overflow
return ((BigInt(val) << 32n) + BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24));
}
function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(BigInt(data.readInt64LE(currOffset)));
currOffset += nbytes;
}
return res;
}
function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(parseFloat(data.readFloatLE(currOffset).toFixed(7)));
currOffset += nbytes;
}
return res;
}
function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
res.push(parseFloat(data.readDoubleLE(currOffset).toFixed(16)));
currOffset += nbytes;
}
return res;
}
function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
while (currOffset < data.length) {
let dataEntry = data.slice(currOffset, currOffset + nbytes); //one entry in a row under a column;
res.push(ref.readCString(dataEntry));
currOffset += nbytes;
}
return res;
}
function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset);
let res = [];
let currOffset = 0;
//every 4;
while (currOffset < data.length) {
let dataEntry = data.slice(currOffset, currOffset + nbytes); //one entry in a row under a column;
res.push(dataEntry.toString("utf16le").replace(/\u0000/g, ""));
currOffset += nbytes;
}
return res;
}
//Object with all the relevant converters from pblock data to javascript data
let convertFunctions = {
[fieldTypes.C_BOOL] : convertBool,
[fieldTypes.C_TINYINT] : convertTinyint,
[fieldTypes.C_SMALLINT] : convertSmallint,
[fieldTypes.C_INT] : convertInt,
[fieldTypes.C_BIGINT] : convertBigint,
[fieldTypes.C_FLOAT] : convertFloat,
[fieldTypes.C_DOUBLE] : convertDouble,
[fieldTypes.C_BINARY] : convertBinary,
[fieldTypes.C_TIMESTAMP] : convertTimestamp,
[fieldTypes.C_NCHAR] : convertNchar
}
// Define TaosField structure
var char_arr = ArrayType(ref.types.char);
var TaosField = Struct({
'name': char_arr,
});
TaosField.fields.name.type.size = 64;
TaosField.defineProperty('bytes', ref.types.short);
TaosField.defineProperty('type', ref.types.char);
//The C interface with the Taos TSDB
function CTaosInterface (config = null, pass = false) {
ref.types.char_ptr = ref.refType(ref.types.char);
ref.types.void_ptr = ref.refType(ref.types.void);
/*Declare a bunch of functions first*/
this.libtaos = ffi.Library('libtaos', {
'taos_options': [ ref.types.int, [ ref.types.int , ref.types.void_ptr ] ],
'taos_init': [ ref.types.void, [ ] ],
//TAOS *taos_connect(char *ip, char *user, char *pass, char *db, int port)
'taos_connect': [ ref.types.void_ptr, [ ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.char_ptr, ref.types.int ] ],
//void taos_close(TAOS *taos)
'taos_close': [ ref.types.void, [ ref.types.void_ptr ] ],
//TAOS_RES *taos_use_result(TAOS *taos);
'taos_use_result': [ ref.types.void_ptr, [ ref.types.void_ptr ] ],
//int taos_query(TAOS *taos, char *sqlstr)
'taos_query': [ ref.types.int, [ ref.types.void_ptr, ref.types.char_ptr ] ],
//int taos_affected_rows(TAOS *taos)
'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ],
//int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)
'taos_fetch_block': [ ref.types.int, [ ref.types.void_ptr, ref.types.void_ptr] ],
//int taos_result_precision(TAOS_RES *res)
'taos_result_precision': [ ref.types.int, [ ref.types.void_ptr ] ],
//void taos_free_result(TAOS_RES *res)
'taos_free_result': [ ref.types.void, [ ref.types.void_ptr] ],
//int taos_field_count(TAOS *taos)
'taos_field_count': [ ref.types.int, [ ref.types.void_ptr ] ],
//TAOS_FIELD *taos_fetch_fields(TAOS_RES *res)
'taos_fetch_fields': [ ref.refType(TaosField), [ ref.types.void_ptr ] ],
//int taos_errno(TAOS *taos)
'taos_errno': [ ref.types.int, [ ref.types.void_ptr] ],
//char *taos_errstr(TAOS *taos)
'taos_errstr': [ ref.types.char, [ ref.types.void_ptr] ]
});
if (pass == false) {
if (config == null) {
//check this buffer
this._config = ref.alloc(ref.types.char_ptr, ref.NULL);
}
else {
try {
this._config = ref.allocCString(config);;
}
catch(err){
throw "Attribute Error: config is expected as a str";
}
}
if (config != null) {
this.libtaos.taos_options(3, this._config);
}
this.libtaos.taos_init();
}
}
CTaosInterface.prototype.config = function config() {
return this._config;
}
CTaosInterface.prototype.connect = function connect(host=null, user="root", password="taosdata", db=null, port=0) {
let _host,_user,_password,_db,_port;
try {
_host = host != null ? ref.allocCString(host) : ref.alloc(ref.types.char_ptr, ref.NULL);
}
catch(err) {
throw "Attribute Error: host is expected as a str";
}
try {
_user = ref.allocCString(user)
}
catch(err) {
throw "Attribute Error: user is expected as a str";
}
try {
_password = ref.allocCString(password);
}
catch(err) {
throw "Attribute Error: password is expected as a str";
}
try {
_db = db != null ? ref.allocCString(db) : ref.alloc(ref.types.char_ptr, ref.NULL);
}
catch(err) {
throw "Attribute Error: db is expected as a str";
}
try {
_port = ref.alloc(ref.types.int, port);
}
catch(err) {
throw TypeError("port is expected as an int")
}
let connection = this.libtaos.taos_connect(_host, _user, _password, _db, _port);
if (ref.isNull(connection)) {
throw new errors.TDError('Failed to connect to TDengine');
}
else {
console.log('Successfully connected to TDengine');
}
return connection;
}
CTaosInterface.prototype.close = function close(connection) {
this.libtaos.taos_close(connection);
console.log("Connection is closed");
}
CTaosInterface.prototype.query = function query(connection, sql) {
let res = this.libtaos.taos_query(connection, ref.allocCString(sql));
return res;
}
CTaosInterface.prototype.affectedRows = function affectedRows(connection) {
return this.libtaos.taos_affected_rows(connection);
}
CTaosInterface.prototype.useResult = function useResult(connection) {
let result = this.libtaos.taos_use_result(connection);
let fields = [];
let pfields = this.fetchFields(result);
if (ref.isNull(pfields) == false) {
let fullpfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0);
for (let i = 0; i < fullpfields.length; i += 68) {
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
fields.push( {
name: ref.readCString(ref.reinterpret(fullpfields,64,i)),
bytes: fullpfields[i + 64],
type: fullpfields[i + 66]
})
}
}
return {result:result, fields:fields}
}
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
let pblock = ref.ref(ref.ref(ref.NULL));
let num_of_rows = this.libtaos.taos_fetch_block(result, pblock)
if (num_of_rows == 0) {
return {block:null, num_of_rows:0};
}
let isMicro = (this.libtaos.taos_result_precision(result) == fieldTypes.C_TIMESTAMP_MICRO)
let blocks = new Array(fields.length);
blocks.fill(null);
num_of_rows = Math.abs(num_of_rows);
let offset = 0;
for (let i = 0; i < fields.length; i++) {
if (!convertFunctions[fields[i]['type']] ) {
throw new errors.DatabaseError("Invalid data type returned from database");
}
let data = ref.reinterpret(pblock.deref().deref(), fields[i]['bytes'], offset);
blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro);
offset += fields[i]['bytes'] * num_of_rows;
}
return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)}
}
CTaosInterface.prototype.freeResult = function freeResult(result) {
this.libtaos.taos_free_result(result);
result = null;
}
CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) {
return this.libtaos.taos_field_count(connection);
}
CTaosInterface.prototype.fetchFields = function fetchFields(result) {
return this.libtaos.taos_fetch_fields(result);
}
CTaosInterface.prototype.errno = function errno(connection) {
return this.libtaos.taos_errno(connection);
}
CTaosInterface.prototype.errStr = function errStr(connection) {
return (this.libtaos.taos_errstr(connection));
}
const TDengineCursor = require('./cursor')
const CTaosInterface = require('./cinterface')
module.exports = TDengineConnection;
/*
* TDengine Connection object
* @param {Object.<string, string>} options - Options for configuring the connection with TDengine
* @return {TDengineConnection}
*
*
*/
function TDengineConnection(options) {
this._conn = null;
this._host = null;
this._user = "root"; //The default user
this._password = "taosdata"; //The default password
this._database = null;
this._port = 0;
this._config = null;
this._chandle = null;
this.config(options)
return this;
}
TDengineConnection.prototype.config = function config(options) {
if (options['host']) {
this._host = options['host'];
}
if (options['user']) {
this._user = options['user'];
}
if (options['password']) {
this._password = options['password'];
}
if (options['database']) {
this._database = options['database'];
}
if (options['port']) {
this._port = options['port'];
}
if (options['config']) {
this._config = options['config'];
}
this._chandle = new CTaosInterface(this._config);
this._conn = this._chandle.connect(this._host, this._user, this._password, this._database, this._port);
}
TDengineConnection.prototype.close = function close() {
return this._chandle.close(this._conn);
}
TDengineConnection.prototype.cursor = function cursor() {
//Pass the connection object to the cursor
return new TDengineCursor(this);
}
TDengineConnection.prototype.commit = function commit() {
return this;
}
TDengineConnection.prototype.rollback = function rollback() {
return this;
}
TDengineConnection.prototype.clear_result_set = function clear_result_set() {
var result = this._chandle.useResult(this._conn).result;
if (result) {
this._chandle.freeResult(result)
}
}
/*
* TDengine Field Types
*
*
*/
module.exports = {
// Type Code
C_NULL : 0,
C_BOOL : 1,
C_TINYINT : 2,
C_SMALLINT : 3,
C_INT : 4,
C_BIGINT : 5,
C_FLOAT : 6,
C_DOUBLE : 7,
C_BINARY : 8,
C_TIMESTAMP : 9,
C_NCHAR : 10,
// NULL value definition
// NOTE: These values should change according to C definition in tsdb.h
C_BOOL_NULL : 0x02,
C_TINYINT_NULL : -128,
C_SMALLINT_NULL : -32768,
C_INT_NULL : -2147483648,
C_BIGINT_NULL : -9223372036854775808,
C_TIMESTAMP_MILLI : 0,
C_TIMESTAMP_MICRO : 1,
}
const CTaosInterface = require('./cinterface')
const errors = require ('./error')
module.exports = TDengineCursor;
function TDengineCursor(connection=null) {
this._description = null;
this._rowcount = -1;
this._connection = null;
this._result = null;
this._fields = null;
this.data = null;
this.fieldNames = null;
this.chandle = new CTaosInterface(null, true); //pass through, just need library loaded.
if (connection != null) {
this._connection = connection
}
}
TDengineCursor.prototype.description = function description() {
return this._description;
}
TDengineCursor.prototype.rowcount = function rowcount() {
return this._rowcount;
}
TDengineCursor.prototype.callproc = function callproc() {
return;
}
TDengineCursor.prototype.close = function close() {
if (this._connection == null) {
return false;
}
this._connection.clear_result_set();
this._reset_result();
this._connection = null;
return true;
}
TDengineCursor.prototype.execute = function execute(operation, params=null) {
if (operation == undefined) {
return null;
}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
this._connection.clear_result_set();
this._reset_result();
let stmt = operation;
if (params != null) {
//why pass?
}
res = this.chandle.query(this._connection._conn, stmt);
if (res == 0) {
let fieldCount = this.chandle.fieldsCount(this._connection._conn);
if (fieldCount == 0) {
return this.chandle.affectedRows(this._connection._conn); //return num of affected rows, common with insert, use statements
}
else {
let resAndField = this.chandle.useResult(this._connection._conn, fieldCount)
this._result = resAndField.result;
this._fields = resAndField.fields;
this.fieldNames = resAndField.fields.map(fieldData => fieldData.name);
return this._handle_result(); //return a pointer
}
}
else {
throw new errors.ProgrammingError(this.chandle.errStr(this._connection._conn))
}
}
TDengineCursor.prototype.executemany = function executemany() {
}
TDengineCursor.prototype.fetchone = function fetchone() {
}
TDengineCursor.prototype.fetchmany = function fetchmany() {
}
TDengineCursor.prototype.fetchall = function fetchall() {
if (this._result == null || this._fields == null) {
throw new errors.OperationalError("Invalid use of fetchall, either result or fields from query are null");
}
let data = [];
this._rowcount = 0;
let k = 0;
while(true) {
k+=1;
let blockAndRows = this.chandle.fetchBlock(this._result, this._fields);
let block = blockAndRows.blocks;
let num_of_rows = blockAndRows.num_of_rows;
if (num_of_rows == 0) {
break;
}
this._rowcount += num_of_rows;
for (let i = 0; i < num_of_rows; i++) {
data.push([]);
for (let j = 0; j < this._fields.length; j++) {
data[data.length-1].push(block[j][i]);
}
}
}
this._connection.clear_result_set();
this.data = data;
return data; //data[i] returns the ith row, with all the data
}
TDengineCursor.prototype.nextset = function nextset() {
return;
}
TDengineCursor.prototype.setinputsize = function setinputsize() {
return;
}
TDengineCursor.prototype.setoutputsize = function setoutputsize(size, column=null) {
return;
}
TDengineCursor.prototype._reset_result = function _reset_result() {
this._description = null;
this._rowcount = -1;
this._result = null;
this._fields = null;
this.data = null;
this.fieldNames = null;
}
TDengineCursor.prototype._handle_result = function _handle_result() {
this._description = [];
for (let field of this._fields) {
this._description.push([field.name, field.type, null, null, null, null, false]);
}
return this._result;
}
/*
Classes for exceptions
Note: All exceptions thrown by Node.js or the JavaScript runtime will be instances of Error.
https://nodejs.org/api/errors.html#errors_exceptions_vs_errors
*/
class TDError extends Error {
constructor(args) {
super(args)
this.name = "TDError";
}
}
class Warning extends Error {
// Exception raised for important warnings like data truncations while inserting.
constructor(args) {
super(args)
this.name = "Warning";
}
}
class InterfaceError extends TDError {
// Exception raised for errors that are related to the database interface rather than the database itself.
constructor(args) {
super(args)
this.name = "TDError.InterfaceError";
}
}
class DatabaseError extends TDError {
// Exception raised for errors that are related to the database.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError";
}
}
class DataError extends DatabaseError {
// Exception raised for errors that are due to problems with the processed data like division by zero, numeric value out of range.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.DataError";
}
}
class OperationalError extends DatabaseError {
// Exception raised for errors that are related to the database's operation and not necessarily under the control of the programmer
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.OperationalError";
}
}
class IntegrityError extends DatabaseError {
// Exception raised when the relational integrity of the database is affected.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.IntegrityError";
}
}
class InternalError extends DatabaseError {
// Exception raised when the database encounters an internal error.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.InternalError";
}
}
class ProgrammingError extends DatabaseError {
// Exception raised for programming errors.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.ProgrammingError";
}
}
class NotSupportedError extends DatabaseError {
// Exception raised in case a method or database API was used which is not supported by the database.
constructor(args) {
super(args)
this.name = "TDError.DatabaseError.NotSupportedError";
}
}
module.exports = {
TDError, Warning, InterfaceError, DatabaseError, DataError, OperationalError, IntegrityError, InternalError, ProgrammingError, NotSupportedError
};
此差异已折叠。
{
"name": "td-connector",
"version": "1.0.5",
"description": "A Node.js connector for TDengine.",
"main": "tdengine.js",
"scripts": {
"test": "node test/test.js"
},
"keywords": [
"TDengine",
"TAOS Data",
"Time Series Database",
"Connector"
],
"author": "StoneT2000",
"license": "AGPL-3.0",
"dependencies": {
"ffi": "^2.3.0",
"node-gyp": "^5.0.2",
"ref": "^1.3.5",
"ref-array": "^1.2.0"
},
"homepage": "https://github.com/taosdata/tdengine",
"bugs": {
"url": "https://github.com/taosdata/tdengine/issues"
},
"repository": {
"type": "git",
"url": "https://github.com/taosdata/tdengine.git"
}
}
# TDengine Node.js connector
This is the Node.js library that lets you connect to [TDengine](https://www.github.com/taosdata/tdengine).
## Installation
To get started, just type in the following to install the connector through [npm](https://www.npmjs.com/)
```cmd
npm install td-connector
```
To interact with TDengine, we make use of the [node-gyp[(https://github.com/nodejs/node-gyp)] library. To install, you will need to install the following depending on platform (the following instructions are quoted from node-gyp)
### On Unix
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported)
- `make`
- A proper C/C++ compiler toolchain, like [GCC](https://gcc.gnu.org)
### On macOS
- `python` (`v2.7` recommended, `v3.x.x` is **not** supported) (already installed on macOS)
- Xcode
- You also need to install the
```
Command Line Tools
```
via Xcode. You can find this under the menu
```
Xcode -> Preferences -> Locations
```
(or by running
```
xcode-select --install
```
in your Terminal)
- This step will install `gcc` and the related toolchain containing `make`
### On Windows
#### Option 1
Install all the required tools and configurations using Microsoft's [windows-build-tools](https://github.com/felixrieseberg/windows-build-tools) using `npm install --global --production windows-build-tools` from an elevated PowerShell or CMD.exe (run as Administrator).
#### Option 2
Install tools and configuration manually:
- Install Visual C++ Build Environment: [Visual Studio Build Tools](https://visualstudio.microsoft.com/thank-you-downloading-visual-studio/?sku=BuildTools) (using "Visual C++ build tools" workload) or [Visual Studio 2017 Community](https://visualstudio.microsoft.com/pl/thank-you-downloading-visual-studio/?sku=Community) (using the "Desktop development with C++" workload)
- Install [Python 2.7](https://www.python.org/downloads/) (`v3.x.x` is not supported), and run `npm config set python python2.7` (or see below for further instructions on specifying the proper Python version and path.)
- Launch cmd, `npm config set msvs_version 2017`
If the above steps didn't work for you, please visit [Microsoft's Node.js Guidelines for Windows](https://github.com/Microsoft/nodejs-guidelines/blob/master/windows-environment.md#compiling-native-addon-modules) for additional tips.
To target native ARM64 Node.js on Windows 10 on ARM, add the components "Visual C++ compilers and libraries for ARM64" and "Visual C++ ATL for ARM64".
## Usage
To use the connector, first request the library ```td-connector```. Running the function ```taos.connect``` with the connection options passed in as an object will return a TDengine connection object. A cursor needs to be intialized in order to interact with TDengine from node.
```javascript
const taos = require('td-connector');
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0})
var c1 = conn.cursor(); // Initializing a new cursor
```
We can now start executing queries through the ```cursor.execute``` function.
```javascript
c1.execute('show databases;')
```
We can get the results of the queries by doing the following
```javascript
var data = c1.fetchall();
console.log(c1.fieldNames); // Returns the names of the columns/fields
console.log(data); // Logs all the data from the query as an array of arrays, each of which represents a row and data[row_number] is sorted in order of the fields
```
## Example
The following is an example use of the connector showing how to make a table with weather data, insert random data, and then retrieve it.
```javascript
// Get the td-connector package
const taos = require('td-connector');
/* We will connect to TDengine by passing an object comprised of connection options to taos.connect and store the
* connection to the variable conn
*/
/*
* Connection Options
* host: the host to connect to
* user: the use to login as
* password: the password for the above user to login
* config: the location of the taos.cfg file, by default it is in /etc/taos
* port: the port we connect through
*/
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0});
// Initialize our TDengineCursor, which we use to interact with TDengine
var c1 = conn.cursor();
// c1.execute(query) will execute the query
// Let's create a database named db
try {
c1.execute('create database db;');
}
catch(err) {
conn.close();
throw err;
}
// Now we will use database db
try {
c1.execute('use db;');
}
catch (err) {
conn.close();
throw err;
}
// Let's create a table called weather
// which stores some weather data like humidity, AQI (air quality index), temperature, and some notes as text
try {
c1.execute('create table if not exists weather (ts timestamp, humidity smallint, aqi int, temperature float, notes binary(30));');
}
catch (err) {
conn.close();
throw err;
}
// Let's get the description of the table weather
try {
c1.execute('describe db.weather');
}
catch (err) {
conn.close();
throw err;
}
// To get results, we run the function c1.fetchall()
// It only returns the query results as an array of result rows, but also stores the latest results in c1.data
try {
var tableDesc = c1.fetchall(); // The description variable here is equal to c1.data;
console.log(tableDesc);
}
catch (err) {
conn.close();
throw err;
}
// Let's try to insert some random generated data to test with
let stime = new Date();
let interval = 1000;
// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds
// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds
// Thus, we create the following function to convert a javascript Date object to the correct formatting
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\"";
}
try {
for (let i = 0; i < 10000; i++) {
stime.setMilliseconds(stime.getMilliseconds() + interval);
let insertData = [convertDateToTS(stime),
parseInt(Math.random()*100),
parseInt(Math.random()*300),
parseFloat(Math.random()*10 + 30),
"\"random note!\""];
c1.execute('insert into db.weather values(' + insertData.join(',') + ' );');
}
}
catch (err) {
conn.close();
throw err;
}
// Now let's look at our newly inserted data
var retrievedData;
try {
c1.execute('select * from db.weather;')
retrievedData = c1.fetchall();
// c1.fieldNames stores the names of each column retrieved
console.log(c1.fieldNames);
console.log(retrievedData);
// timestamps retrieved are always JS Date Objects
// Numbers are numbers, big ints are big ints, and strings are strings
}
catch (err) {
conn.close();
throw err;
}
// Let's try running some basic functions
try {
c1.execute('select count(*), avg(temperature), max(temperature), min(temperature), stddev(temperature) from db.weather;')
c1.fetchall();
console.log(c1.fieldNames);
console.log(c1.data);
}
catch(err) {
conn.close();
throw err;
}
conn.close();
// Feel free to fork this repository or copy this code and start developing your own apps and backends with NodeJS and TDengine!
```
## Contributing to TDengine
Please follow the [contribution guidelines](https://github.com/taosdata/TDengine/blob/master/CONTRIBUTING.md) to contribute to the project.
## License
[GNU AGPL v3.0](http://www.gnu.org/licenses/agpl-3.0.html)
\ No newline at end of file
var TDengineConnection = require('./nodetaos/connection.js')
module.exports.connect = function (connection=null) {
return new TDengineConnection(connection);
}
const taos = require('td-connector');
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0});
var c1 = conn.cursor();
let stime = new Date();
let interval = 1000;
// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds
// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds
// Thus, we create the following function to convert a javascript Date object to the correct formatting
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\"";
}
function R(l,r) {
return Math.random() * (r - l) - r;
}
function randomBool() {
if (Math.random() < 0.5) {
return true;
}
return false;
}
c1.execute('create database td_connector_test;');
c1.execute('use td_connector_test;')
c1.execute('create table if not exists all_types (ts timestamp, _int int, _bigint bigint, _float float, _double double, _binary binary(40), _smallint smallint, _tinyint tinyint, _bool bool, _nchar nchar(40));');
for (let i = 0; i < 10000; i++) {
stime.setMilliseconds(stime.getMilliseconds() + interval);
let insertData = [convertDateToTS(stime), // Timestamp
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // Int
parseInt( R(-Math.pow(2,31) + 1 , Math.pow(2,31) - 1) ), // BigInt
parseFloat( R(-3.4E38, 3.4E38) ), // Float
parseFloat( R(-1.7E308, 1.7E308) ), // Double
"\"Long Binary\"", // Binary
parseInt( R(-32767, 32767) ), // Small Int
parseInt( R(-127, 127) ), // Tiny Int
randomBool(),
"\"Nchars 一些中文字幕\""]; // Bool
c1.execute('insert into td_connector_test.all_types values(' + insertData.join(',') + ' );');
}
c1.execute('select * from td_connector_test.all_types limit 10 offset 1000;');
var d = c1.fetchall();
console.log(c1.fieldNames);
console.log(d);
c1.execute('select count(*), avg(_int), sum(_float), max(_bigint), min(_double) from td_connector_test.all_types;');
var d = c1.fetchall();
console.log(c1.fieldNames);
console.log(d);
c1.execute('drop database td_connector_test;')
conn.close();
// Get the td-connector package
const taos = require('td-connector');
/* We will connect to TDengine by passing an object comprised of connection options to taos.connect and store the
* connection to the variable conn
*/
/*
* Connection Options
* host: the host to connect to
* user: the use to login as
* password: the password for the above user to login
* config: the location of the taos.cfg file, by default it is in /etc/taos
* port: the port we connect through
*/
var conn = taos.connect({host:"127.0.0.1", user:"root", password:"taosdata", config:"/etc/taos",port:0});
// Initialize our TDengineCursor, which we use to interact with TDengine
var c1 = conn.cursor();
// c1.execute(query) will execute the query
// Let's create a database named db
try {
c1.execute('create database db;');
}
catch(err) {
conn.close();
throw err;
}
// Now we will use database db
try {
c1.execute('use db;');
}
catch (err) {
conn.close();
throw err;
}
// Let's create a table called weather
// which stores some weather data like humidity, AQI (air quality index), temperature, and some notes as text
try {
c1.execute('create table if not exists weather (ts timestamp, humidity smallint, aqi int, temperature float, notes binary(30));');
}
catch (err) {
conn.close();
throw err;
}
// Let's get the description of the table weather
try {
c1.execute('describe db.weather');
}
catch (err) {
conn.close();
throw err;
}
// To get results, we run the function c1.fetchall()
// It only returns the query results as an array of result rows, but also stores the latest results in c1.data
try {
var tableDesc = c1.fetchall(); // The description variable here is equal to c1.data;
console.log(tableDesc);
}
catch (err) {
conn.close();
throw err;
}
// Let's try to insert some random generated data to test with
let stime = new Date();
let interval = 1000;
// Timestamps must be in the form of "YYYY-MM-DD HH:MM:SS.MMM" if they are in milliseconds
// "YYYY-MM-DD HH:MM:SS.MMMMMM" if they are in microseconds
// Thus, we create the following function to convert a javascript Date object to the correct formatting
function convertDateToTS(date) {
let tsArr = date.toISOString().split("T")
return "\"" + tsArr[0] + " " + tsArr[1].substring(0, tsArr[1].length-1) + "\"";
}
try {
for (let i = 0; i < 10000; i++) {
stime.setMilliseconds(stime.getMilliseconds() + interval);
let insertData = [convertDateToTS(stime),
parseInt(Math.random()*100),
parseInt(Math.random()*300),
parseFloat(Math.random()*10 + 30),
"\"random note!\""];
c1.execute('insert into db.weather values(' + insertData.join(',') + ' );');
}
}
catch (err) {
conn.close();
throw err;
}
// Now let's look at our newly inserted data
var retrievedData;
try {
c1.execute('select * from db.weather;')
retrievedData = c1.fetchall();
// c1.fieldNames stores the names of each column retrieved
console.log(c1.fieldNames);
console.log(retrievedData);
// timestamps retrieved are always JS Date Objects
// Numbers are numbers, big ints are big ints, and strings are strings
}
catch (err) {
conn.close();
throw err;
}
// Let's try running some basic functions
try {
c1.execute('select count(*), avg(temperature), max(temperature), min(temperature), stddev(temperature) from db.weather;')
c1.fetchall();
console.log(c1.fieldNames);
console.log(c1.data);
}
catch(err) {
conn.close();
throw err;
}
conn.close();
// Feel free to fork this repository or copy this code and start developing your own apps and backends with NodeJS and TDengine!
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册