UNPKG

shelving

Version:

Toolkit for using data in JavaScript.

73 lines (72 loc) 3.03 kB
import { SQLProvider } from "./SQLProvider.js"; /** Abstract PostgreSQL provider with JSONB function support for nested keys, array containment, and array mutations. */ export class PostgreSQLProvider extends SQLProvider { /** Get the Postgres JSONB path for the nested segments of a key, e.g. `{"b","c"}`. */ sqlPath(key) { return this.sqlConcat(key.slice(1).map(k => this.sqlIdentifier(k)), ",", "{", "}"); } /** Get the Postgres JSONB extract syntax, e.g. `"a" #>> {"b","c"}` */ sqlExtract(key) { const column = this.sqlIdentifier(key[0]); if (key.length > 1) { const path = this.sqlPath(key); return this.sql `${column} #>> ${path}`; } return column; } // Override to implement `with` and `omit` updates and deeply nested JSONB values. sqlUpdate(update) { const { action, key, value } = update; const column = this.sqlIdentifier(key[0]); // Implement all updates for deeply nested paths. if (key.length > 1) { const path = this.sqlPath(key); if (action === "set") { return this.sql `${column} = jsonb_set(${column}, ${path}, ${value})`; } if (action === "sum") { return this.sql `${column} = jsonb_set(${column}, ${path}, (${column} #>> ${path}) + ${value})`; } const source = this.sql `${column} #> ${path}`; if (action === "with") { return this.sql `${column} = jsonb_set(${column}, ${path}, ${source} || ( SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) FROM jsonb_array_elements(${value}) AS elem WHERE NOT ${source} @> jsonb_build_array(elem) ), false)`; } if (action === "omit") { return this.sql `${column} = jsonb_set(${column}, ${path}, ( SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) FROM jsonb_array_elements(${source}) AS elem WHERE NOT ${value} @> jsonb_build_array(elem) ), false)`; } return action; // Never happens. } // Implement `with` and `omit` on flat values. if (action === "with") { return this.sql `${column} = ${column} || ( SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) FROM jsonb_array_elements(${value}) AS elem WHERE NOT ${column} @> jsonb_build_array(elem) )`; } if (action === "omit") { return this.sql `${column} = ( SELECT COALESCE(jsonb_agg(elem), '[]'::jsonb) FROM jsonb_array_elements(${column}) AS elem WHERE NOT ${value} @> jsonb_build_array(elem) )`; } return super.sqlUpdate(update); } // Override to implement deeply-nested JSONB queries. sqlFilter(filter) { const { key, operator, value } = filter; // Implement `contains` filters. if (operator === "contains") return this.sql `${this.sqlExtract(key)} @> ${[value]}`; return super.sqlFilter(filter); } }