Use Javascript
In this section, we provide a high-level overview of how a JavaScript client might interact with a Deephaven installation.
This will be done with pseudo-code and simple explanations to give a complete picture of how the system can be used, and a brief introduction into Deephaven terminology.
A more technical specification, complete with glossary and important implementation details follows.
Example 1: Logging In
class MyApp {
constructor() {
this.client = new Client('https://myserver/iris');
// the actual token here can be a password, or any other supported auth token
this.client.login({username: 'bob', token: '$₪£€₽$€₡₪₽€', type: 'password'})
.then(() => this.startApp(), console.error);
// .then() is used to add callbacks to a Promise,
// which will be used anywhere data is asynchronously loaded
}
startApp() {
// now that the client is authenticated, all Deephaven functions,
// such as opening persistent queries and opening tables, are available
}
}
new MyApp();
In Example 1 above, we are sending the username, the token, and the type of auth to use. You can configure your own auth handler to integrate with any external system, and Deephaven will simply check with your server to verify user identities.
The underlying network transport mechanism is not yet available to clients as we will manage web socket connections and the auth token refreshes automatically; all the client needs to do is add callbacks to Promises as needed
Example 2: Discovering Database Tables
startApp() {
// Subscribe to updates on all persistent queries
this.client.addEventListener('queryadded', e => addQuery(e.detail));
this.client.addEventListener('queryremoved', (e) => removeQuery(e.detail));
this.client.addEventListener('queryupdated', (e) => updateQuery(e.detail));
// Fetch all known persistent queries (collections of tables)
this.client.getKnownConfigs().forEach(queryInfo => addQuery(queryInfo));
}
addQuery(queryInfo) {
console.log(`Loaded query ${queryInfo.name} [${queryInfo.serial}]`);
for ( name in queryInfo.tableNames) {
this.addTable(queryInfo, name);
}
}
removeQuery(queryInfo) { ... }
updateQuery(queryInfo) { ... }
addTable(queryInfo, name) {
// Render a list of queries and tables to choose from
}
In Example 2 above, we are retrieving and subscribing to changes in Persistent Queries, which include collections of tables and the configuration for running those tables; you can read more about Persistent Query settings in the technical specification.
Example 3: Selecting a Table
addTable(queryInfo, name) {
// Create an element to click to select table.
show(new TableSelector(queryInfo, name);
}
class TableSelector {
constructor(query, name) {
// Create a function that returns a promise to load the table.
// Connecting to a table to read information can be expensive, so we avoid doing it.
this._table = () => queryInfo.table(name);
this._name = name;
someElement.innerText = name;
}
onClick() {
this._table().then(table =>
show(new TableView(this._name, table))
);
}
}
class TableView {
constructor(name, table) {
this._table = table;
// Draw a placeholder
renderFrame(name, table);
// Subscribe to the updated event to get table data to render
// addEventListener returns a function to easily perform cleanup
this._cleanup = table.addEventListener('updated', e => this.renderTable(e));
// This remote call does not return data directly;
// instead, we use our subscription to the `updated` event, above.
table.setViewport(startRow, endRow, table.columns);
}
renderFrame(name, table) {
someHeader.innerText = name;
}
renderTable(event) {
this._viewport = event.detail;
drawViewport();
}
drawViewport() {
var cnt = this._viewport.offset;
this._viewport.rows.forEach(row => this.drawRow(cnt++, row));
}
drawRow(rowNum, row) {
// Render a row of data using this._table.columns for type information.
this._table._columns.forEach(column=> this.drawCell(rowNum, row, column) );
}
drawCell(rowNum, row, column) {
doHtmlThings(rowNum, row.get(column));
}
dispose() {
// Stop subscribing to the events from this table
this._cleanup();
// Additionally, it may make sense to close the Table entirely, if it is not to be used again
this._table.close();
}
}
In Example 3 above, we are loading the actual table data for rendering; metadata such as table size, columns, sorts and filters will be kept up to date for you, but the actual table data will not load until you call table.setViewport(), and receive updated events to trigger drawing.
This allows both the client and the server to decide to trigger a redraw, without burdening the client with extra state or callback management.
Example 4: Sorting and Filtering
class TableView {
// Given a table with columns: type(String), index(Number), created(Date), modified(Date),
// setSort(columns.type.asc(), columns.created.desc())
setSort() {
// all arguments should be iris.Sort objects.
this._sorts = Array.prototype.slice.apply(arguments);
var promise = this._table.applySort(this._sorts);
return promise;
}
addSort(sort) {
this._sorts.push(sort);
return this._table.applySort(this._sorts);
}
// setFilter(
// columns.type.eq(FilterValue.ofString('Awesome')),
// columns.modified.greaterThan(FilterValue.ofDateTime(yesterday()))
// )
setFilter() {
this._filters = Array.prototype.slice.apply(arguments);
return this._table.applyFilter(this._filters);
}
addFilter(filter) {
this._filters.push(filter);
return this._table.applyFilter(this._filters);
}
// Use cloning when you want to create a new table to apply sorts and filters without modifying the existing table.
// Note that each the original and the clone will fire their own events, maintain their own viewport, and
// individual close() calls will need to be applied individually as appropriate.
clone(name) {
if (!name) {
name = `${this._name}Clone`;
}
return this._table.copy()
.then(newTable=>return new TableView(name, newTable));
}
}
Note
See: For more details and information, see our full documentation on Javascript.