Allow restricting by timestamp or position.

Closes #181.
This commit is contained in:
D. Berge
2022-02-27 18:27:49 +01:00
parent d7d75f34cd
commit 8790a797d9

View File

@@ -1,4 +1,4 @@
const { transaction, pool } = require('../connection');
function fields (obj, keys) {
@@ -9,12 +9,76 @@ function fields (obj, keys) {
return retval;
}
/** Perform a restrict (aka select) operation on the relation.
*
* Recognised queries are:
*
* - tstamp:i; tolerance:j
* - pos:λ,φ; radius: ρ
*
* Defaults:
*
* - tolerance: 1000 (ms)
* - radius: 1 (m)
*
* Queries are ANDed together.
*/
function restrict (q="") {
let sql = [];
/* This takes in a string of the form:
* "key0=value0;key1=value1;key2=value=2"
* and returns an object like:
* { key0: "value0", key1: "value1", key2: "value=2" }
*/
const constraints = Object.fromEntries(q.split(/\s*;\s*/).map(i => {
const parts = i.split(/(\s*:\s*)/);
return [parts[0], parts.slice(2).join("")]
}));
// TODO Consider adding an event_tstamp field, or indexing meta->>tstamp
if ("tstamp" in constraints) {
const ts = new Date(constraints.tstamp);
if (isNaN(ts)) {
throw new Error("Invalid query");
}
const tolerance = constraints.tolerance ?? 1000;
const ts0 = new Date(ts.valueOf()-Number(tolerance));
const ts1 = new Date(ts.valueOf()+Number(tolerance));
console.log(tolerance, ts, ts0, ts1);
console.log(constraints);
if (!isNaN(ts0) && !isNaN(ts1)) {
sql.push(`(meta->>'tstamp')::timestamptz BETWEEN '${ts0.toISOString()}' AND '${ts1.toISOString()}'`);
} else {
throw new Error("Invalid query");
}
}
if ("pos" in constraints) {
const [ lon, lat ] = constraints.pos.split(",").map(i => Number(i));
if (isNaN(lon) || isNaN(lat)) {
throw new Error("Invalid query");
}
const radius = constraints.radius ?? 1;
sql.push(`ST_DistanceSphere(geometry, ST_Point(${lon}, ${lat}, 4326)) <= ${radius}`)
}
return sql.length ? ("WHERE " + sql.join(" AND ")) : "";
}
async function get (opts = {}) {
const client = await pool.connect();
const text = `
SELECT meta
FROM real_time_inputs
${restrict(opts.q)}
ORDER BY tstamp DESC
LIMIT $1
OFFSET $2
@@ -24,6 +88,7 @@ async function get (opts = {}) {
const res = await client.query(text, values);
client.release();
// TODO Must change this to use utils/project instead.
return opts.fields
? res.rows.map(r => fields(r.meta, opts.fields.split(/[;,\s]+/)))
: res.rows.map(r => r.meta);