UNPKG

@cocalc/database

Version:

CoCalc: code for working with our PostgreSQL database

67 lines (60 loc) 2.23 kB
import { is_date as isDate } from "@cocalc/util/misc"; /* jsonbSet: This little piece of very hard to write (and clever?) code makes it so we can set or **merge in at any nested level** (!) arbitrary JSON objects. We can also delete any key at any level by making the value null or undefined! This is amazingly easy to use in queries -- basically making JSONP with postgres as expressive as RethinkDB REQL (even better in some ways). */ // The input desc is an object that describes what is being set, e.g., // account_id = '... some uuid '; // desc = {"users": {[account_id]:{group:'collaborator'}}} // changes the users JSONB field of the table so that users[account_id] has // the group set to "collaborator". // If the merge field is set then we merge in the change; otherwise, // we replace the value. // IMPORTANT: this is a dangerous attack vector -- do not call this function // with unsanitized input from a user! export function jsonbSet( desc: object, merge: boolean = false ): { set: string; params: any[] } { const params: any[] = []; function pushParam(val: any, type: string): number { if (type.toUpperCase() == "JSONB") { val = JSON.stringify(val); // this is needed by the driver....} } params.push(val); return params.length; } function set(field: string, data: object, path: string[]): string { let obj = `COALESCE(${field}#>'{${path.join(",")}}', '{}'::JSONB)`; for (const key in data) { const val = data[key]; if (val == null) { // remove key from object obj = `(${obj} - '${key}')`; } else { // set key in object if (merge && typeof val === "object" && !isDate(val)) { const subobj = set(field, val, path.concat([key])); obj = `JSONB_SET(${obj}, '{${key}}', ${subobj})`; } else { // completely replace field[key] with val. obj = `JSONB_SET(${obj}, '{${key}}', $${pushParam( val, "JSONB" )}::JSONB)`; } } } return obj; } const v: string[] = []; for (const field in desc) { const data = desc[field]; v.push(`${field}=${set(field, data, [])}`); } return { set: v.join(","), params }; }