node-q
Version:
Q interfacing with Node.js
391 lines (288 loc) • 15.1 kB
Markdown
# node-q
Q interfacing with Node.js. Supports [decompression](http://code.kx.com/wiki/Reference/ipcprotocol#Compression). 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
```javascript
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
```javascript
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
```javascript
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)
```javascript
nodeq.connect({ unixSocket: "/path/to/socket" }, function(err, con) {
if (err) throw err;
console.log("connected");
});
```
### Execute Q code and receive result
```javascript
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
```javascript
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
```javascript
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
```javascript
con.ks("show 1 2 3", function(err) {
if (err) throw err;
});
```
### Async execute function with parameters
```javascript
con.ks("show", [1, 2, 3], function(err) {
if (err) throw err;
});
```
### Listen to a handle
```javascript
con.k(function(err, res) {
if (err) throw err;
console.log("result", res);
});
```
### Subscribe to kdb+tick
```javascript
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
```javascript
con.close(function() {
console.log("con closed");
});
```
## Types
q has more [data types](http://code.kx.com/wiki/Reference/Datatypes) 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](https://developer.mozilla.org/docs/Glossary/Boolean) | | | |
| guid | [String](https://developer.mozilla.org/docs/Glossary/String) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| byte | [Number](https://developer.mozilla.org/docs/Glossary/Number) | | | |
| short | [Number](https://developer.mozilla.org/docs/Glossary/Number) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) |
| int | [Number](https://developer.mozilla.org/docs/Glossary/Number) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) |
| long | [Number](https://developer.mozilla.org/docs/Glossary/Number) <sup>[5](#types-footnote5)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) |
| real | [Number](https://developer.mozilla.org/docs/Glossary/Number) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) |
| float | [Number](https://developer.mozilla.org/docs/Glossary/Number) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) |
| char | [String](https://developer.mozilla.org/docs/Glossary/String) | [Null](https://developer.mozilla.org/docs/Glossary/Null) <sup>[4](#types-footnote4)</sup> | | |
| symbol | [String](https://developer.mozilla.org/docs/Glossary/String) | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| timestamp | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[1](#types-footnote1), [2](#types-footnote2)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| month | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| date | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| datetime | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| timespan | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[1](#types-footnote1), [2](#types-footnote2), [3](#types-footnote3)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| minute | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2), [3](#types-footnote3)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| second | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2), [3](#types-footnote3)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
| time | [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) <sup>[2](#types-footnote2), [3](#types-footnote3)</sup> | [Null](https://developer.mozilla.org/docs/Glossary/Null) | | |
* <a name="types-footnote1">1</a>: 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](https://developer.mozilla.org/docs/Glossary/Number).
* <a name="types-footnote2">2</a>: think about running your Node.js process with `TZ=UTC node ...` to run in UTC timezone. q doesn't know timezones.
* <a name="types-footnote3">3</a>: date is set to `2000-01-01` in the Date object. Only evaluate the time part.
* <a name="types-footnote4">4</a>: You can disable `emptyChar2null` deserialization during `connect(params, cb)` to keep the empty char.
* <a name="types-footnote5">5</a>: You can disable `long2number` deserialization during `connect(params, cb)` to represent longs as [long.js](https://www.npmjs.com/package/long).
#### dict
```
q) (`a`b`c)!(1 2 3i)
```
becomes [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object)
```javascript
{
a: 1,
b: 2,
c: 3
}
```
#### list
```
q) 1 2 3i
```
becomes [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)
```javascript
[1, 2, 3]
```
#### table
```
q) ([] sym:`a`b`c; size:(1 2 3i))
```
becomes [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) of [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) per row.
```javascript
[
{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](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) with an [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) per column.
```javascript
{
sym: ["a", "b", "c"],
size: [1, 2, 3]
}
```
### From JavaScript to q (serialization)
#### Simple (infer type)
| JavaScript type | q type |
| --------------- | ------ |
| [Boolean](https://developer.mozilla.org/docs/Glossary/Boolean) | boolean |
| [String](https://developer.mozilla.org/docs/Glossary/String) starting with ` | symbol |
| [String](https://developer.mozilla.org/docs/Glossary/String) | list[char] |
| [Number](https://developer.mozilla.org/docs/Glossary/Number) | float |
| [Date](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Date) | datetime |
| [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Object) | dict |
| [Array](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array)[*] | list[*] |
| [Null](https://developer.mozilla.org/docs/Glossary/Null) | unary primitive |
| [Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Infinity) | float |
| -[Infinity](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/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:
```javascript
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)` <sup>[1](#wrappers-footnote1)</sup> | `longs(Array[long])` <sup>[1](#wrappers-footnote1)</sup> |
| 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])` |
* <a name="wrappers-footnote1">1</a>: JavaScript can not represent 64bit longs. Therefore this module uses the [long.js](https://www.npmjs.com/package/long) 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](http://nodejs.org/api/events.html#events_class_events_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](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array) of [Object](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/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](https://code.kx.com/q/kb/ssl/). 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