Skip to content

michaelwittig/node-q

Repository files navigation

node-q

Q interfacing with Node.js. Supports decompression. Can deserialize all q data types (including guid) to JavaScript. Can serialize all JavaScript data types to q.

Installation

npm install node-q

Usage

Create Connection

var nodeq = require("node-q");
nodeq.connect({host: "localhost", port: 5000}, function(err, con) {
	if (err) throw err;
	console.log("connected");
	// interact with con like demonstrated below
});

Create TLS Connection

var nodeq = require("node-q");
nodeq.connect({host: "localhost", port: 6000, useTLS: true}, function(err, con) {
	if (err) throw err;
	console.log("connected");
	// interact with con like demonstrated below
});

Create Connection with user and password auth

var nodeq = require("node-q");
nodeq.connect({host: "localhost", port: 5000, user: "user", password: "password"}, function(err, con) {
	if (err) throw err;
	console.log("connected");
	// interact with con like demonstrated below
});

Create Connection with Unix Domain Socket (Doesn't support abstract namespace sockets: KDB 3.5+ on Linux)

nodeq.connect({ unixSocket: "/path/to/socket" }, function(err, con) {
	if (err) throw err;
	console.log("connected");
});

Execute Q code and receive result

con.k("sum 1 2 3", function(err, res) {
	if (err) throw err;
	console.log("result", res); // 6
});

Execute function with one parameter and receive result

con.k("sum", [1, 2, 3], function(err, res) {
	if (err) throw err;
	console.log("result", res); // 6
});

Execute function with two parameters and receive result

con.k("cor", [1, 2, 3], [4, 5, 6], function(err, res) {
	if (err) throw err;
	console.log("result", res); // 1
});

Async execute Q code

con.ks("show 1 2 3", function(err) {
	if (err) throw err;
});

Async execute function with parameters

con.ks("show", [1, 2, 3], function(err) {
	if (err) throw err;
});

Listen to a handle

con.k(function(err, res) {
	if (err) throw err;
	console.log("result", res);
});

Subscribe to kdb+tick

con.on("upd", function(table, data) {
	console.log(table, data);
});

con.ks(".u.sub[`;`]", function(err) { // subscribe to all tables and all symbols
	if (err) throw err;
});

Close connection

con.close(function() {
	console.log("con closed");
});

Types

q has more data types than JavaScript. Therefore you need to know how types are converted.

From q to JavaScript (deserialization)

q type JavaScript type Null +Infinity -Infinity
boolean Boolean
guid String Null
byte Number
short Number Null Infinity -Infinity
int Number Null Infinity -Infinity
long Number 5 Null Infinity -Infinity
real Number Null Infinity -Infinity
float Number Null Infinity -Infinity
char String Null 4
symbol String Null
timestamp Date 1, 2 Null
month Date 2 Null
date Date 2 Null
datetime Date 2 Null
timespan Date 1, 2, 3 Null
minute Date 2, 3 Null
second Date 2, 3 Null
time Date 2, 3 Null
  • 1: q comes with nanoseconds precision. JavaScript only with milliseconds. You can disable nanos2date deserialization during connect(params, cb) to get the nanoseconds timestamp as a plain Number.
  • 2: think about running your Node.js process with TZ=UTC node ... to run in UTC timezone. q doesn't know timezones.
  • 3: date is set to 2000-01-01 in the Date object. Only evaluate the time part.
  • 4: You can disable emptyChar2null deserialization during connect(params, cb) to keep the empty char.
  • 5: You can disable long2number deserialization during connect(params, cb) to represent longs as long.js.

dict

q) (`a`b`c)!(1 2 3i)

becomes Object

{
	a: 1,
	b: 2,
	c: 3
}

list

q) 1 2 3i

becomes Array

[1, 2, 3]

table

q) ([] sym:`a`b`c; size:(1 2 3i))

becomes Array of Object per row.

[
	{sym: "a", size: 1},
	{sym: "b", size: 2},
	{sym: "c", size: 3}
]

You can disable flipTables during connect(params, cb) to get a table as an Object with an Array per column.

{
	sym: ["a", "b", "c"],
	size: [1, 2, 3]
}

From JavaScript to q (serialization)

Simple (infer type)

JavaScript type q type
Boolean boolean
String starting with ` symbol
String list[char]
Number float
Date datetime
Object dict
Array[*] list[*]
Null unary primitive
Infinity float
-Infinity float

Advanced (explicit types)

If you want to explicitly serialize a JavaScript type as a q type you need to use the typed API.

Let's start with two examples:

con.k("type", nodeq.short(1), function(err, res) {
	if (err) throw err;
	console.log("result", res); // -5
});

con.k("type", nodeq.shorts([1, 2, 3]), function(err, res) {
	if (err) throw err;
	console.log("result", res); // 5
});

For every primitive type in q, this module exports a method to wrap the JavaScript value. You can also wrap a JavaScript array into a q type by appending an s to the primitive wrapper's name.

q type primitive wrapper array wrapper
boolean boolean(Boolean) booleans(Array[Boolean])
guid guid(String) guids(Array[String])
byte byte(Number) bytes(Array[Number])
short short(Number) shorts(Array[Number])
int int(Number) ints(Array[Number])
long long(long) 1 longs(Array[long]) 1
real real(Number) reals(Array[Number])
float float(Number) floats(Array[Number])
char char(String) chars(Array[String])
symbol symbol(String) symbols(Array[String])
timestamp timestamp(Date) timestamps(Array[Date])
month month(Date) months(Array[Date])
date date(Date) dates(Array[Date])
datetime datetime(Date) datetimes(Array[Date])
timespan timespan(Date) timespans(Array[Date])
minute minute(Date) minutes(Array[Date])
second second(Date) seconds(Array[Date])
time time(Date) times(Array[Date])
  • 1: JavaScript can not represent 64bit longs. Therefore this module uses the long.js module to represent longs.

API

connect(params, cb)

  • params: Object
    • host: String (e. g. "localhost") (optional)
    • port: Number (e. g. 5000) (optional)
    • unixSocket: String (e. g. "/path/to/socket") (optional)
    • user: String (optional)
    • password: String (optional)
    • useTLS: Boolean (optional)
    • ca: Buffer | String (e.g. fs.readFileSync('path\to\cert.pem')) (optional)
    • socketNoDelay : Boolean (optional, see http://nodejs.org/api/net.html#net_socket_setnodelay_nodelay)
    • socketTimeout: Number (optional, see http://nodejs.org/api/net.html#net_socket_settimeout_timeout_callback)
    • nanos2date: Boolean (optional, default: true)
    • flipTables: Boolean (optional, default: true)
    • emptyChar2null: Boolean (optional, default: true)
    • long2number: Boolean (optional, default: true)
  • cb: Function(err, con)
    • err: Error or undefined
    • conn: Connection or undefined

@deprecated connect(host, port, [user, password,] cb)

This is deprecated. Please use the new, mor flexible API above!

  • host: String (e. g. "localhost")
  • port: Number (e. g. 5000)
  • user: String (optional)
  • password: String (optional)

Connection

Is an EventEmitter.

k(s, [x, [y, [z, [...,] ] ] ] cb)

Sync request/response.

  • s: String
  • x: Object (optional)
  • y: Object (optional)
  • z: Object (optional)
  • ...: Object (optional)
  • cb: Function(err, res)
    • err: Error or undefined
    • res: Object or undefined

ks(s, [x, [y, [z, [...,] ] ] ] cb)

Async request.

  • s: String
  • x: Object (optional)
  • y: Object (optional)
  • z: Object (optional)
  • ...: Object (optional)
  • cb: Function(err)
    • err: Error or undefined

close(cb)

  • cb: Function(err) (optional)
    • err: Error or undefined

Events

upd(table, data)

If you use kdb+tick and subscribe like con.ks(".u.sub[;]", function(err) { throw err; }) you will receive all Updates via upd Event.

  • table: String (e.g. trades)
  • data: Object (table represented in JavaScript as Array of Object)
error(err)

If the socket emit an error event.

  • err: Error
end()

If the socket emit an end event.

timeout()

If the socket emit a timeout event.

close(had_error)

If the socket emit a close event.

  • had_error: Boolean (true if the socket had a transmission error)

Contribution

If you want to create a Pull-Request please make sure that make test runs without failures.

If you have a kdb+tick setup please also run make mochait.

Code Style

make jshint

Unit Tests

make mocha

Integration Test

Assumes a running q process on port 5000 with kdb+tick available in QHOME (QHOME=~/q ~/q/m32/q -p 5000). For the tls tests you will also need a running q process on port 6000 set up to require tls. Instructions for this can be found here. If you are using a self signed certificate you will also need to set the NODE_TLS_REJECT_UNAUTHORIZED environment variable to 0.

make mochait

Circular depdendencies

make circular