@naturalcycles/db-lib
Version:
Lowest Common Denominator API to supported Databases
77 lines (76 loc) • 2.69 kB
JavaScript
import { _isTruthy } from '@naturalcycles/js-lib';
import { DBQuery } from '../query/dbQuery.js';
const _TIMESERIES_RAW = '_TIMESERIES_RAW';
/**
* TimeSeries DB implementation based on provided CommonDB database.
* Turns any CommonDB database into TimeSeries DB. Kind of.
*
* @experimental
*/
export class CommonTimeSeriesDao {
cfg;
constructor(cfg) {
this.cfg = cfg;
}
async ping() {
await this.cfg.db.ping();
}
async getSeries() {
return (await this.cfg.db.getTables())
.map(t => /^(.*)_TIMESERIES_RAW$/.exec(t)?.[1])
.filter(_isTruthy);
}
// convenience method
async save(series, tsMillis, value) {
await this.saveBatch(series, [[tsMillis, value]]);
}
async saveBatch(series, dataPoints) {
if (!dataPoints.length)
return;
const rows = dataPoints.map(([ts, v]) => ({
id: String(ts), // Convert Number id into String id, as per CommonDB
ts, // to allow querying by ts, since querying by id is not always available (Datastore is one example)
v,
}));
await this.cfg.db.saveBatch(`${series}${_TIMESERIES_RAW}`, rows);
}
/**
* All ops are executed as a single CommonDB Transaction.
*/
async commitTransaction(ops) {
if (!ops.length)
return;
await this.cfg.db.runInTransaction(async (tx) => {
for (const op of ops) {
const rows = op.dataPoints.map(([ts, v]) => ({
id: String(ts), // Convert Number id into String id, as per CommonDB
ts, // to allow querying by ts, since querying by id is not always available (Datastore is one example)
v,
}));
await tx.saveBatch(`${op.series}${_TIMESERIES_RAW}`, rows);
}
});
}
async deleteById(series, tsMillis) {
await this.deleteByIds(series, [tsMillis]);
}
async deleteByIds(series, ids) {
// Save to _RAW table with v=null
await this.saveBatch(series, ids.map(id => [id, null]));
}
async query(q) {
const dbq = DBQuery.create(`${q.series}${_TIMESERIES_RAW}`).order('ts');
if (q.fromIncl)
dbq.filter('ts', '>=', q.fromIncl);
if (q.toExcl)
dbq.filter('ts', '<', q.toExcl);
const rows = (await this.cfg.db.runQuery(dbq)).rows;
// todo: query from aggregated tables when step is above 'hour'
return rows
.filter(r => r.v !== null && r.v !== undefined) // can be 0
.map(r => [r.ts, r.v]);
}
async optimize() {
// todo
}
}