# eyros

multi-dimensional interval database

This database is written in rust and compiled to wasm with a javascript
interface.

The data structures are based on bkd and interval trees and designed for:

* high batch write performance
* peer to peer distribution and query-driven sparse replication
* geospatial and time-series use cases

# example

In this example, we populate an in-memory database with 2-dimensional points and
intervals. Any points or intervals that intersect the bounding box from `-10` to
`+10` on the x and y coordinates is returned in the query result.

``` js
const RAM = require('random-access-memory')
const eyros = require('eyros/2d')

;(async function () {
  var db = await eyros({
    storage: RAM,
    wasmSource: fetch('2d.wasm')
  })
  await db.batch([
    { type:'insert', point:[+1,+2], value: Uint8Array.from([97,98,99]) },
    { type:'insert', point:[-5,+4], value: Uint8Array.from([100,101]) },
    { type:'insert', point:[+9,-3], value: Uint8Array.from([102,103,104]) },
    { type:'insert', point:[+5,-15], value: Uint8Array.from([105,106]) },
    { type:'insert', point:[[+5,+12],[-15,-3]], value: Uint8Array.from([105,106]) },
    { type:'insert', point:[[-20,-11],[-8,+2]], value: Uint8Array.from([107]) },
  ])
  var q = await db.query([-10,-10,+10,+10])
  var row
  while (row = await q.next()) {
    console.log('row=',row)
  }
})()
```

To use the database from node, you can use `fs.readFileSync()` with
`require.resolve()` to get the wasmSource:

``` js
const RAM = require('random-access-memory')
const eyros = require('eyros/2d')
const fs = require('fs')

;(async function () {
  var db = await eyros({
    storage: RAM,
    wasmSource: fs.readFileSync(require.resolve('eyros/2d.wasm'))
  })
  await db.batch([
    { type:'insert', point:[+1,+2], value: Uint8Array.from([97,98,99]) },
    { type:'insert', point:[-5,+4], value: Uint8Array.from([100,101]) },
    { type:'insert', point:[+9,-3], value: Uint8Array.from([102,103,104]) },
    { type:'insert', point:[+5,-15], value: Uint8Array.from([105,106]) },
    { type:'insert', point:[[+5,+12],[-15,-3]], value: Uint8Array.from([105,106]) },
    { type:'insert', point:[[-20,-11],[-8,+2]], value: Uint8Array.from([107]) },
  ])
  var q = await db.query([-10,-10,+10,+10])
  var row
  while (row = await q.next()) {
    console.log('row=',row)
  }
})()
```

# api

```
const eyros2d = require('eyros/2d')
const eyros3d = require('eyros/3d')
const eyros4d = require('eyros/4d')
const eyros5d = require('eyros/5d')
const eyros6d = require('eyros/6d')
const eyros7d = require('eyros/7d')
const eyros8d = require('eyros/8d')
```

The precision for all databases is presently f32.

## `var db = await eyros{N}d(opts)`

Open a database for the given dimension from:

* `opts.wasmSource` - wasm data contained in an arraybuffer, typed array, [Response][],
  or a promise resolving to any of those types
* `opts.wasmStream` - wasm source to pass directly to `WebAssembly.instantiateStreaming()`
  without inferring whether to stream or not
* `opts.wasmModule` - already-created WebAssembly.Module instance
* `opts.storage(name)` - function that returns a random-access interface
* `opts.getId(value)` - return a Uint8Array `id` for a given `value`.
  defaults to returning the value
* `opts.branchFactor` - number of non-intersecting branches per node. default: `6`
* `opts.maxDepth` - maximum tree depth before splitting into a separate tree file. default: `8`
* `opts.maxRecords` - maximum number of records to store per tree file. default: `20_000`
* `opts.inline` - threshold under which records will be written out in a list rather than branches.
  default: `500`
* `opts.treeCacheSize` - maximum number of trees to cache in the lru. default: `1000`
* `opts.rebuildDepth` - number of levels to rebuild each batch in an optimization pass: default `2`
* `opts.debug` - optionally supply a function to receive internal debug messages

One of `opts.wasmSource` or `opts.wasmModule` must be provided.

The `opts.storage` function must return random-access instances that implement a
`.length` property, `.len(cb)`, or `.length(cb)` function which returns the size
in bytes that has been allocated for the given file.

Files to supply to `opts.wasmSource` can be obtained from the root of this
package under the convention `${N}d.wasm` for a dimension `N`.

[Response]: https://developer.mozilla.org/en-US/docs/Web/API/Response

## `await db.batch(rows, opts={})`

Insert `rows`, an array of operations to perform on the database.

Each `row` must have a `row.type` set to `'insert'` or `'delete'` with a `row.point`
and a `row.value` Uint8Array (for inserts) or a `row.id` (for deletes).

`row.point` is an n-dimensional array of scalar floats or 2-item arrays of `[min,max]` floats for
each dimension.

Optionally provide:

* `opts.rebuildDepth` - depth to calculate an optimizing rebuild up to (default: 2)
* `opts.errorIfMissing` - whether to raise an error if deleted records are not found before removal

## `await db.sync()`

Write database changes to the underlying data storage.

## `var q = await db.query(bbox, opts={})`

Return an async iterator `q` containing all records from the database that intersect the `bbox`.

Obtain results by calling `row = await q.next()` until it yields a falsy result.
Each `row` is a 2-item array of the form `[point,value]` and each `point` is an n-dimensional array
of scalar floats or 2-item arrays of `[min,max]` floats for each dimension.

`bbox` is an array of the form `[minX,minY,...,maxX,maxY,...]`.
For 2 dimensions, the `bbox` would be `[west,south,east,north]` for `lon,lat` coordinates.

Optionally provide a function `opts.trace(tr)` which will receive a `tr` object just before the
corresponding file is read from storage:

* `tr.id` - integer id for this tree file
* `tr.file` - file path string for the tree
* `tr.bbox` - bounding extents of the tree in `minX,minY,...,maxX,maxY,...]` form

The motivating use case for `opts.trace` is to cancel open requests for content which is no longer
in view when panning a map. The information from the trace can be passed to the storage layer to
make these decisions.

# install

```
npm install eyros
```

# license

bsd
