@graffy/pg 0.15.9 → 0.15.11-alpha.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Readme.md +0 -39
- package/index.cjs +81 -39
- package/index.mjs +82 -40
- package/package.json +2 -2
- package/types/filter/getSql.d.ts +1 -1
package/Readme.md
CHANGED
|
@@ -16,42 +16,3 @@ Connection parameters should be set in environment variables. Uses the [pg](http
|
|
|
16
16
|
|
|
17
17
|
In this document, _property names_ and _paths_ refer to the structures in the Graffy graph objects, _columns_ refer to Postgres table columns and _args_ refer to structures in the filtering and pagination arguments of Graffy query objects.
|
|
18
18
|
|
|
19
|
-
- **table**, the name of the PostgreSQL table
|
|
20
|
-
- **columns**, an object with column names as keys and an objects describing each column as value. Each descriptor object contains a mandatory **role** property, which may be:
|
|
21
|
-
|
|
22
|
-
- **primary**, for the primary key column of this table. There must be exactly one primary column.
|
|
23
|
-
- **simple**, for a normal column, which stores the value of a particular property of this object.
|
|
24
|
-
- **default**, for a JSON column where all data that isn't mapped to another column is placed. There may be zero or one default column.
|
|
25
|
-
- **version**, for a numeric column used to store the version number of the object. There must be exactly one version column.
|
|
26
|
-
- **gin**, for a JSONB column into which some properties are copied, for enabling filtering and sorting using that property.
|
|
27
|
-
- **tsv**, for an indexed tsvector column for full text searches
|
|
28
|
-
- **trgm**, for a trigram-indexed text column for typeahead searches
|
|
29
|
-
|
|
30
|
-
The descriptor object may also have the following additional properties.
|
|
31
|
-
|
|
32
|
-
- **prop**, the property name or path that maps to this column. Valid for **simple** and **primary** columns. Defaults to the name of the column.
|
|
33
|
-
- **props**, an array of property names or paths to copy into a **gin**, **tsv** or **trgm** column. Mandatory for these columns.
|
|
34
|
-
- **arg**, the filter argument for querying a **tsv** or **trgm** column. Defaults to the column name.
|
|
35
|
-
|
|
36
|
-
- **links**, an object with props as keys and link templates as values. The
|
|
37
|
-
link template is an array of strings and objects, which may contain at any _value_ position (including inside the object) a string `$$` followed by a property name. This string will be replaced with a value of that property in this object.
|
|
38
|
-
- **pollInterval**, the interval at which the table is polled, for watch.
|
|
39
|
-
|
|
40
|
-
```js
|
|
41
|
-
{
|
|
42
|
-
table: string, // the name of the PostgreSQL table
|
|
43
|
-
columns: {
|
|
44
|
-
[columnName]: {
|
|
45
|
-
role: 'primary' | 'simple' | 'default' |
|
|
46
|
-
'version' | 'gin' | 'tsv' | 'trgm',
|
|
47
|
-
prop: string // primary or simple only
|
|
48
|
-
props: string[] // gin, tsv or trgm only
|
|
49
|
-
arg: string // tsv or trgm only
|
|
50
|
-
}
|
|
51
|
-
},
|
|
52
|
-
links: {
|
|
53
|
-
[prop]: string | (string|object)[]
|
|
54
|
-
}
|
|
55
|
-
pollInterval: number
|
|
56
|
-
}
|
|
57
|
-
```
|
package/index.cjs
CHANGED
|
@@ -29,8 +29,8 @@ var __objRest = (source, exclude) => {
|
|
|
29
29
|
};
|
|
30
30
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
31
|
exports[Symbol.toStringTag] = "Module";
|
|
32
|
-
var pg$1 = require("pg");
|
|
33
32
|
var common = require("@graffy/common");
|
|
33
|
+
var pg$1 = require("pg");
|
|
34
34
|
var sql = require("sql-template-tag");
|
|
35
35
|
var debug = require("debug");
|
|
36
36
|
function _interopDefaultLegacy(e) {
|
|
@@ -169,51 +169,73 @@ function simplify(node) {
|
|
|
169
169
|
}
|
|
170
170
|
return node;
|
|
171
171
|
}
|
|
172
|
-
function defaultColumnType() {
|
|
173
|
-
return "
|
|
172
|
+
function defaultColumnType(_) {
|
|
173
|
+
return "any";
|
|
174
|
+
}
|
|
175
|
+
function getCompatibleTypes(value) {
|
|
176
|
+
if (value === null)
|
|
177
|
+
return "any";
|
|
178
|
+
if (Array.isArray(value))
|
|
179
|
+
return "array";
|
|
180
|
+
if (typeof value === "object")
|
|
181
|
+
return "jsonb";
|
|
182
|
+
if (typeof value === "number")
|
|
183
|
+
return "numeric";
|
|
184
|
+
if (typeof value === "string")
|
|
185
|
+
return "text";
|
|
174
186
|
}
|
|
175
187
|
function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
176
|
-
function
|
|
188
|
+
function lookup(string, type) {
|
|
177
189
|
if (string.substr(0, 3) === "el$")
|
|
178
190
|
return sql__default["default"]`"${sql.raw(string)}"`;
|
|
179
|
-
return getLookupSql(string);
|
|
191
|
+
return getLookupSql(string, type);
|
|
192
|
+
}
|
|
193
|
+
function binop(op, left, right) {
|
|
194
|
+
const lType = left.substr(0, 3) === "el$" ? "any" : getColumnType(left);
|
|
195
|
+
const rType = getCompatibleTypes(right);
|
|
196
|
+
if (lType === "any" || rType === "any" || rType === lType) {
|
|
197
|
+
return sql__default["default"]`${lookup(left)} ${sql.raw(op)} ${right}`;
|
|
198
|
+
} else {
|
|
199
|
+
return sql__default["default"]`(${lookup(left, rType)})::${sql.raw(rType)} ${sql.raw(op)} ${right}`;
|
|
200
|
+
}
|
|
180
201
|
}
|
|
181
202
|
function getNodeSql(ast) {
|
|
182
203
|
switch (ast[0]) {
|
|
183
204
|
case "$eq":
|
|
184
205
|
if (ast[2] === null)
|
|
185
|
-
return sql__default["default"]`${
|
|
186
|
-
return
|
|
206
|
+
return sql__default["default"]`${lookup(ast[1])} IS NULL`;
|
|
207
|
+
return binop("=", ast[1], ast[2]);
|
|
187
208
|
case "$neq":
|
|
188
209
|
if (ast[2] === null)
|
|
189
|
-
return sql__default["default"]`${
|
|
190
|
-
return
|
|
210
|
+
return sql__default["default"]`${lookup(ast[1])} IS NOT NULL`;
|
|
211
|
+
return binop("<>", ast[1], ast[2]);
|
|
191
212
|
case "$lt":
|
|
192
|
-
return
|
|
213
|
+
return binop("<", ast[1], ast[2]);
|
|
193
214
|
case "$lte":
|
|
194
|
-
return
|
|
215
|
+
return binop("<=", ast[1], ast[2]);
|
|
195
216
|
case "$gt":
|
|
196
|
-
return
|
|
217
|
+
return binop(">", ast[1], ast[2]);
|
|
197
218
|
case "$gte":
|
|
198
|
-
return
|
|
219
|
+
return binop(">=", ast[1], ast[2]);
|
|
199
220
|
case "$re":
|
|
200
|
-
return
|
|
221
|
+
return binop("~", ast[1], ast[2]);
|
|
201
222
|
case "$ire":
|
|
202
|
-
return
|
|
223
|
+
return binop("~*", ast[1], ast[2]);
|
|
203
224
|
case "$in":
|
|
204
|
-
return sql__default["default"]`${
|
|
225
|
+
return sql__default["default"]`${lookup(ast[1])} IN (${sql.join(ast[2])})`;
|
|
205
226
|
case "$nin":
|
|
206
|
-
return sql__default["default"]`${
|
|
227
|
+
return sql__default["default"]`${lookup(ast[1])} NOT IN (${sql.join(ast[2])})`;
|
|
207
228
|
case "$cts":
|
|
208
|
-
return sql__default["default"]`${
|
|
229
|
+
return sql__default["default"]`${lookup(ast[1])} @> ${ast[2]}`;
|
|
209
230
|
case "$ctd":
|
|
210
|
-
return sql__default["default"]`${
|
|
231
|
+
return sql__default["default"]`${lookup(ast[1])} <@ ${ast[2]}`;
|
|
211
232
|
case "$ovl":
|
|
212
233
|
switch (getColumnType(ast[1])) {
|
|
213
234
|
case "jsonb":
|
|
214
|
-
|
|
235
|
+
case "any":
|
|
236
|
+
return sql__default["default"]`${lookup(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
|
|
215
237
|
case "array":
|
|
216
|
-
return sql__default["default"]`${
|
|
238
|
+
return sql__default["default"]`${lookup(ast[1])} && ${ast[2]}`;
|
|
217
239
|
default:
|
|
218
240
|
throw Error("pg.getSql_ovl_unknown_column_type");
|
|
219
241
|
}
|
|
@@ -224,11 +246,11 @@ function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
|
224
246
|
case "$not":
|
|
225
247
|
return sql__default["default"]`NOT (${getNodeSql(ast[1])})`;
|
|
226
248
|
case "$any":
|
|
227
|
-
return sql__default["default"]`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${
|
|
249
|
+
return sql__default["default"]`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
228
250
|
case "$all":
|
|
229
|
-
return sql__default["default"]`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${
|
|
251
|
+
return sql__default["default"]`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
230
252
|
case "$has":
|
|
231
|
-
return sql__default["default"]`(SELECT bool_or(${sql.join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${
|
|
253
|
+
return sql__default["default"]`(SELECT bool_or(${sql.join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
232
254
|
default:
|
|
233
255
|
throw Error("pg.getSql_unknown_operator: " + ast[0]);
|
|
234
256
|
}
|
|
@@ -247,7 +269,7 @@ const getJsonBuildValue = (value) => {
|
|
|
247
269
|
return value;
|
|
248
270
|
if (typeof value === "string")
|
|
249
271
|
return sql__default["default"]`${value}::text`;
|
|
250
|
-
return sql__default["default"]`${stripAttributes(value)}::jsonb`;
|
|
272
|
+
return sql__default["default"]`${JSON.stringify(stripAttributes(value))}::jsonb`;
|
|
251
273
|
};
|
|
252
274
|
const getSelectCols = (table) => {
|
|
253
275
|
return sql__default["default"]`to_jsonb("${sql.raw(table)}")`;
|
|
@@ -257,13 +279,13 @@ const getInsert = (row, options) => {
|
|
|
257
279
|
const vals = [];
|
|
258
280
|
Object.entries(row).filter(([name]) => name !== options.verCol && name[0] !== "$").concat([[options.verCol, nowTimestamp]]).forEach(([col, val]) => {
|
|
259
281
|
cols.push(sql__default["default"]`"${sql.raw(col)}"`);
|
|
260
|
-
vals.push(val);
|
|
282
|
+
vals.push(val instanceof sql.Sql || typeof val !== "object" || !val ? val : sql__default["default"]`${JSON.stringify(stripAttributes(val))}::jsonb`);
|
|
261
283
|
});
|
|
262
284
|
return { cols: sql.join(cols, ", "), vals: sql.join(vals, ", ") };
|
|
263
285
|
};
|
|
264
286
|
const getUpdates = (row, options) => {
|
|
265
287
|
return sql.join(Object.entries(row).filter(([name]) => name !== options.idCol && name[0] !== "$").map(([name, value]) => {
|
|
266
|
-
return sql__default["default"]`"${sql.raw(name)}" = ${typeof value
|
|
288
|
+
return sql__default["default"]`"${sql.raw(name)}" = ${value instanceof sql.Sql || typeof value !== "object" || !value ? value : !value.$put ? sql__default["default"]`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : sql__default["default"]`${JSON.stringify(stripAttributes(value))}::jsonb`}`;
|
|
267
289
|
}).concat(sql__default["default"]`"${sql.raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
268
290
|
};
|
|
269
291
|
function getJsonUpdate(_a, col, path) {
|
|
@@ -281,10 +303,15 @@ function getJsonUpdate(_a, col, path) {
|
|
|
281
303
|
function stripAttributes(object) {
|
|
282
304
|
if (typeof object !== "object" || !object)
|
|
283
305
|
return object;
|
|
284
|
-
if (Array.isArray(object))
|
|
285
|
-
return
|
|
286
|
-
|
|
287
|
-
return
|
|
306
|
+
if (Array.isArray(object)) {
|
|
307
|
+
return object.map((item) => stripAttributes(item));
|
|
308
|
+
}
|
|
309
|
+
return Object.entries(object).reduce((out, [key, val]) => {
|
|
310
|
+
if (key === "$put")
|
|
311
|
+
return out;
|
|
312
|
+
out[key] = stripAttributes(val);
|
|
313
|
+
return out;
|
|
314
|
+
}, {});
|
|
288
315
|
}
|
|
289
316
|
const getIdMeta = ({ idCol }) => getJsonBuildObject({
|
|
290
317
|
$key: sql__default["default"]`"${sql.raw(idCol)}"`,
|
|
@@ -299,16 +326,21 @@ function getArgSql(_c, options) {
|
|
|
299
326
|
var _d = _c, { $first, $last, $after, $before, $since, $until, $all, $cursor: _ } = _d, rest = __objRest(_d, ["$first", "$last", "$after", "$before", "$since", "$until", "$all", "$cursor"]);
|
|
300
327
|
const _a = rest, { $order } = _a, filter = __objRest(_a, ["$order"]);
|
|
301
328
|
const { prefix, idCol } = options;
|
|
302
|
-
const lookup = (prop) => {
|
|
329
|
+
const lookup = (prop, type) => {
|
|
303
330
|
const [prefix2, ...suffix] = common.encodePath(prop);
|
|
304
|
-
|
|
331
|
+
const op = type === "text" ? sql__default["default"]`#>>` : sql__default["default"]`#>`;
|
|
332
|
+
return suffix.length ? sql__default["default"]`"${sql.raw(prefix2)}" ${op} ${suffix}` : sql__default["default"]`"${sql.raw(prefix2)}"`;
|
|
333
|
+
};
|
|
334
|
+
const getType = (prop) => {
|
|
335
|
+
const [_prefix, ...suffix] = common.encodePath(prop);
|
|
336
|
+
return suffix.length ? "jsonb" : "any";
|
|
305
337
|
};
|
|
306
338
|
const meta = (key2) => getArgMeta(key2, prefix, idCol);
|
|
307
339
|
const hasRangeArg = $before || $after || $since || $until || $first || $last || $all || $order;
|
|
308
340
|
let key;
|
|
309
341
|
const where = [];
|
|
310
342
|
if (!common.isEmpty(filter)) {
|
|
311
|
-
where.push(getSql(filter, lookup));
|
|
343
|
+
where.push(getSql(filter, lookup, getType));
|
|
312
344
|
key = sql__default["default"]`${JSON.stringify(filter)}::jsonb`;
|
|
313
345
|
}
|
|
314
346
|
if (!hasRangeArg)
|
|
@@ -494,7 +526,7 @@ class Db {
|
|
|
494
526
|
promises.push(getByIds());
|
|
495
527
|
await Promise.all(promises);
|
|
496
528
|
log("dbRead", rootQuery, results);
|
|
497
|
-
return common.slice(common.finalize(results,
|
|
529
|
+
return common.slice(common.finalize(results, common.wrap(query, prefix)), rootQuery).known || [];
|
|
498
530
|
}
|
|
499
531
|
async write(rootChange, tableOptions) {
|
|
500
532
|
const sqls = [];
|
|
@@ -537,13 +569,23 @@ const pg = ({ table, idCol, verCol, links, connection }) => (store) => {
|
|
|
537
569
|
links: links || {}
|
|
538
570
|
};
|
|
539
571
|
const defaultDb = new Db(connection);
|
|
540
|
-
function read(query, options) {
|
|
572
|
+
function read(query, options, next) {
|
|
541
573
|
const _a = options, { transactionDb = defaultDb } = _a, readOpts = __objRest(_a, ["transactionDb"]);
|
|
542
|
-
|
|
574
|
+
const readPromise = transactionDb.read(query, tableOpts, readOpts);
|
|
575
|
+
const remainingQuery = common.remove(query, prefix);
|
|
576
|
+
const nextPromise = next(remainingQuery);
|
|
577
|
+
return Promise.all([readPromise, nextPromise]).then(([readRes, nextRes]) => {
|
|
578
|
+
return common.merge(readRes, nextRes);
|
|
579
|
+
});
|
|
543
580
|
}
|
|
544
|
-
function write(change, options) {
|
|
581
|
+
function write(change, options, next) {
|
|
545
582
|
const _a = options, { transactionDb = defaultDb } = _a, writeOpts = __objRest(_a, ["transactionDb"]);
|
|
546
|
-
|
|
583
|
+
const writePromise = transactionDb.write(change, tableOpts, writeOpts);
|
|
584
|
+
const remainingChange = common.remove(change, prefix);
|
|
585
|
+
const nextPromise = next(remainingChange);
|
|
586
|
+
return Promise.all([writePromise, nextPromise]).then(([writeRes, nextRes]) => {
|
|
587
|
+
return common.merge(writeRes, nextRes);
|
|
588
|
+
});
|
|
547
589
|
}
|
|
548
590
|
};
|
|
549
591
|
exports.pg = pg;
|
package/index.mjs
CHANGED
|
@@ -26,9 +26,9 @@ var __objRest = (source, exclude) => {
|
|
|
26
26
|
}
|
|
27
27
|
return target;
|
|
28
28
|
};
|
|
29
|
+
import { isEmpty, encodePath, isPlainObject, unwrap, decodeArgs, slice, finalize, wrap, isRange, decodeGraph, mergeObject, merge, encodeGraph, wrapObject, remove } from "@graffy/common";
|
|
29
30
|
import { Pool, Client } from "pg";
|
|
30
|
-
import
|
|
31
|
-
import sql, { join, raw, empty, Sql } from "sql-template-tag";
|
|
31
|
+
import sql, { join, raw, Sql, empty } from "sql-template-tag";
|
|
32
32
|
import debug from "debug";
|
|
33
33
|
const valid = {
|
|
34
34
|
$eq: true,
|
|
@@ -161,51 +161,73 @@ function simplify(node) {
|
|
|
161
161
|
}
|
|
162
162
|
return node;
|
|
163
163
|
}
|
|
164
|
-
function defaultColumnType() {
|
|
165
|
-
return "
|
|
164
|
+
function defaultColumnType(_) {
|
|
165
|
+
return "any";
|
|
166
|
+
}
|
|
167
|
+
function getCompatibleTypes(value) {
|
|
168
|
+
if (value === null)
|
|
169
|
+
return "any";
|
|
170
|
+
if (Array.isArray(value))
|
|
171
|
+
return "array";
|
|
172
|
+
if (typeof value === "object")
|
|
173
|
+
return "jsonb";
|
|
174
|
+
if (typeof value === "number")
|
|
175
|
+
return "numeric";
|
|
176
|
+
if (typeof value === "string")
|
|
177
|
+
return "text";
|
|
166
178
|
}
|
|
167
179
|
function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
168
|
-
function
|
|
180
|
+
function lookup(string, type) {
|
|
169
181
|
if (string.substr(0, 3) === "el$")
|
|
170
182
|
return sql`"${raw(string)}"`;
|
|
171
|
-
return getLookupSql(string);
|
|
183
|
+
return getLookupSql(string, type);
|
|
184
|
+
}
|
|
185
|
+
function binop(op, left, right) {
|
|
186
|
+
const lType = left.substr(0, 3) === "el$" ? "any" : getColumnType(left);
|
|
187
|
+
const rType = getCompatibleTypes(right);
|
|
188
|
+
if (lType === "any" || rType === "any" || rType === lType) {
|
|
189
|
+
return sql`${lookup(left)} ${raw(op)} ${right}`;
|
|
190
|
+
} else {
|
|
191
|
+
return sql`(${lookup(left, rType)})::${raw(rType)} ${raw(op)} ${right}`;
|
|
192
|
+
}
|
|
172
193
|
}
|
|
173
194
|
function getNodeSql(ast) {
|
|
174
195
|
switch (ast[0]) {
|
|
175
196
|
case "$eq":
|
|
176
197
|
if (ast[2] === null)
|
|
177
|
-
return sql`${
|
|
178
|
-
return
|
|
198
|
+
return sql`${lookup(ast[1])} IS NULL`;
|
|
199
|
+
return binop("=", ast[1], ast[2]);
|
|
179
200
|
case "$neq":
|
|
180
201
|
if (ast[2] === null)
|
|
181
|
-
return sql`${
|
|
182
|
-
return
|
|
202
|
+
return sql`${lookup(ast[1])} IS NOT NULL`;
|
|
203
|
+
return binop("<>", ast[1], ast[2]);
|
|
183
204
|
case "$lt":
|
|
184
|
-
return
|
|
205
|
+
return binop("<", ast[1], ast[2]);
|
|
185
206
|
case "$lte":
|
|
186
|
-
return
|
|
207
|
+
return binop("<=", ast[1], ast[2]);
|
|
187
208
|
case "$gt":
|
|
188
|
-
return
|
|
209
|
+
return binop(">", ast[1], ast[2]);
|
|
189
210
|
case "$gte":
|
|
190
|
-
return
|
|
211
|
+
return binop(">=", ast[1], ast[2]);
|
|
191
212
|
case "$re":
|
|
192
|
-
return
|
|
213
|
+
return binop("~", ast[1], ast[2]);
|
|
193
214
|
case "$ire":
|
|
194
|
-
return
|
|
215
|
+
return binop("~*", ast[1], ast[2]);
|
|
195
216
|
case "$in":
|
|
196
|
-
return sql`${
|
|
217
|
+
return sql`${lookup(ast[1])} IN (${join(ast[2])})`;
|
|
197
218
|
case "$nin":
|
|
198
|
-
return sql`${
|
|
219
|
+
return sql`${lookup(ast[1])} NOT IN (${join(ast[2])})`;
|
|
199
220
|
case "$cts":
|
|
200
|
-
return sql`${
|
|
221
|
+
return sql`${lookup(ast[1])} @> ${ast[2]}`;
|
|
201
222
|
case "$ctd":
|
|
202
|
-
return sql`${
|
|
223
|
+
return sql`${lookup(ast[1])} <@ ${ast[2]}`;
|
|
203
224
|
case "$ovl":
|
|
204
225
|
switch (getColumnType(ast[1])) {
|
|
205
226
|
case "jsonb":
|
|
206
|
-
|
|
227
|
+
case "any":
|
|
228
|
+
return sql`${lookup(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
|
|
207
229
|
case "array":
|
|
208
|
-
return sql`${
|
|
230
|
+
return sql`${lookup(ast[1])} && ${ast[2]}`;
|
|
209
231
|
default:
|
|
210
232
|
throw Error("pg.getSql_ovl_unknown_column_type");
|
|
211
233
|
}
|
|
@@ -216,11 +238,11 @@ function getSql(filter, getLookupSql, getColumnType = defaultColumnType) {
|
|
|
216
238
|
case "$not":
|
|
217
239
|
return sql`NOT (${getNodeSql(ast[1])})`;
|
|
218
240
|
case "$any":
|
|
219
|
-
return sql`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${
|
|
241
|
+
return sql`(SELECT bool_or(${getNodeSql(ast[3])}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
220
242
|
case "$all":
|
|
221
|
-
return sql`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${
|
|
243
|
+
return sql`(SELECT bool_and(${getNodeSql(ast[3])}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
222
244
|
case "$has":
|
|
223
|
-
return sql`(SELECT bool_or(${join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${
|
|
245
|
+
return sql`(SELECT bool_or(${join(ast[3].map((node) => getNodeSql(node)), `) AND bool_or(`)}) FROM UNNEST(${lookup(ast[1])}) ${lookup(ast[2])})`;
|
|
224
246
|
default:
|
|
225
247
|
throw Error("pg.getSql_unknown_operator: " + ast[0]);
|
|
226
248
|
}
|
|
@@ -239,7 +261,7 @@ const getJsonBuildValue = (value) => {
|
|
|
239
261
|
return value;
|
|
240
262
|
if (typeof value === "string")
|
|
241
263
|
return sql`${value}::text`;
|
|
242
|
-
return sql`${stripAttributes(value)}::jsonb`;
|
|
264
|
+
return sql`${JSON.stringify(stripAttributes(value))}::jsonb`;
|
|
243
265
|
};
|
|
244
266
|
const getSelectCols = (table) => {
|
|
245
267
|
return sql`to_jsonb("${raw(table)}")`;
|
|
@@ -249,13 +271,13 @@ const getInsert = (row, options) => {
|
|
|
249
271
|
const vals = [];
|
|
250
272
|
Object.entries(row).filter(([name]) => name !== options.verCol && name[0] !== "$").concat([[options.verCol, nowTimestamp]]).forEach(([col, val]) => {
|
|
251
273
|
cols.push(sql`"${raw(col)}"`);
|
|
252
|
-
vals.push(val);
|
|
274
|
+
vals.push(val instanceof Sql || typeof val !== "object" || !val ? val : sql`${JSON.stringify(stripAttributes(val))}::jsonb`);
|
|
253
275
|
});
|
|
254
276
|
return { cols: join(cols, ", "), vals: join(vals, ", ") };
|
|
255
277
|
};
|
|
256
278
|
const getUpdates = (row, options) => {
|
|
257
279
|
return join(Object.entries(row).filter(([name]) => name !== options.idCol && name[0] !== "$").map(([name, value]) => {
|
|
258
|
-
return sql`"${raw(name)}" = ${typeof value
|
|
280
|
+
return sql`"${raw(name)}" = ${value instanceof Sql || typeof value !== "object" || !value ? value : !value.$put ? sql`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : sql`${JSON.stringify(stripAttributes(value))}::jsonb`}`;
|
|
259
281
|
}).concat(sql`"${raw(options.verCol)}" = ${nowTimestamp}`), ", ");
|
|
260
282
|
};
|
|
261
283
|
function getJsonUpdate(_a, col, path) {
|
|
@@ -273,10 +295,15 @@ function getJsonUpdate(_a, col, path) {
|
|
|
273
295
|
function stripAttributes(object) {
|
|
274
296
|
if (typeof object !== "object" || !object)
|
|
275
297
|
return object;
|
|
276
|
-
if (Array.isArray(object))
|
|
277
|
-
return
|
|
278
|
-
|
|
279
|
-
return
|
|
298
|
+
if (Array.isArray(object)) {
|
|
299
|
+
return object.map((item) => stripAttributes(item));
|
|
300
|
+
}
|
|
301
|
+
return Object.entries(object).reduce((out, [key, val]) => {
|
|
302
|
+
if (key === "$put")
|
|
303
|
+
return out;
|
|
304
|
+
out[key] = stripAttributes(val);
|
|
305
|
+
return out;
|
|
306
|
+
}, {});
|
|
280
307
|
}
|
|
281
308
|
const getIdMeta = ({ idCol }) => getJsonBuildObject({
|
|
282
309
|
$key: sql`"${raw(idCol)}"`,
|
|
@@ -291,16 +318,21 @@ function getArgSql(_c, options) {
|
|
|
291
318
|
var _d = _c, { $first, $last, $after, $before, $since, $until, $all, $cursor: _ } = _d, rest = __objRest(_d, ["$first", "$last", "$after", "$before", "$since", "$until", "$all", "$cursor"]);
|
|
292
319
|
const _a = rest, { $order } = _a, filter = __objRest(_a, ["$order"]);
|
|
293
320
|
const { prefix, idCol } = options;
|
|
294
|
-
const lookup = (prop) => {
|
|
321
|
+
const lookup = (prop, type) => {
|
|
295
322
|
const [prefix2, ...suffix] = encodePath(prop);
|
|
296
|
-
|
|
323
|
+
const op = type === "text" ? sql`#>>` : sql`#>`;
|
|
324
|
+
return suffix.length ? sql`"${raw(prefix2)}" ${op} ${suffix}` : sql`"${raw(prefix2)}"`;
|
|
325
|
+
};
|
|
326
|
+
const getType = (prop) => {
|
|
327
|
+
const [_prefix, ...suffix] = encodePath(prop);
|
|
328
|
+
return suffix.length ? "jsonb" : "any";
|
|
297
329
|
};
|
|
298
330
|
const meta = (key2) => getArgMeta(key2, prefix, idCol);
|
|
299
331
|
const hasRangeArg = $before || $after || $since || $until || $first || $last || $all || $order;
|
|
300
332
|
let key;
|
|
301
333
|
const where = [];
|
|
302
334
|
if (!isEmpty(filter)) {
|
|
303
|
-
where.push(getSql(filter, lookup));
|
|
335
|
+
where.push(getSql(filter, lookup, getType));
|
|
304
336
|
key = sql`${JSON.stringify(filter)}::jsonb`;
|
|
305
337
|
}
|
|
306
338
|
if (!hasRangeArg)
|
|
@@ -486,7 +518,7 @@ class Db {
|
|
|
486
518
|
promises.push(getByIds());
|
|
487
519
|
await Promise.all(promises);
|
|
488
520
|
log("dbRead", rootQuery, results);
|
|
489
|
-
return slice(finalize(results,
|
|
521
|
+
return slice(finalize(results, wrap(query, prefix)), rootQuery).known || [];
|
|
490
522
|
}
|
|
491
523
|
async write(rootChange, tableOptions) {
|
|
492
524
|
const sqls = [];
|
|
@@ -529,13 +561,23 @@ const pg = ({ table, idCol, verCol, links, connection }) => (store) => {
|
|
|
529
561
|
links: links || {}
|
|
530
562
|
};
|
|
531
563
|
const defaultDb = new Db(connection);
|
|
532
|
-
function read(query, options) {
|
|
564
|
+
function read(query, options, next) {
|
|
533
565
|
const _a = options, { transactionDb = defaultDb } = _a, readOpts = __objRest(_a, ["transactionDb"]);
|
|
534
|
-
|
|
566
|
+
const readPromise = transactionDb.read(query, tableOpts, readOpts);
|
|
567
|
+
const remainingQuery = remove(query, prefix);
|
|
568
|
+
const nextPromise = next(remainingQuery);
|
|
569
|
+
return Promise.all([readPromise, nextPromise]).then(([readRes, nextRes]) => {
|
|
570
|
+
return merge(readRes, nextRes);
|
|
571
|
+
});
|
|
535
572
|
}
|
|
536
|
-
function write(change, options) {
|
|
573
|
+
function write(change, options, next) {
|
|
537
574
|
const _a = options, { transactionDb = defaultDb } = _a, writeOpts = __objRest(_a, ["transactionDb"]);
|
|
538
|
-
|
|
575
|
+
const writePromise = transactionDb.write(change, tableOpts, writeOpts);
|
|
576
|
+
const remainingChange = remove(change, prefix);
|
|
577
|
+
const nextPromise = next(remainingChange);
|
|
578
|
+
return Promise.all([writePromise, nextPromise]).then(([writeRes, nextRes]) => {
|
|
579
|
+
return merge(writeRes, nextRes);
|
|
580
|
+
});
|
|
539
581
|
}
|
|
540
582
|
};
|
|
541
583
|
export { pg };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@graffy/pg",
|
|
3
3
|
"description": "The standard Postgres module for Graffy. Each instance this module mounts a Postgres table as a Graffy subtree.",
|
|
4
4
|
"author": "aravind (https://github.com/aravindet)",
|
|
5
|
-
"version": "0.15.
|
|
5
|
+
"version": "0.15.11-alpha.1",
|
|
6
6
|
"main": "./index.cjs",
|
|
7
7
|
"exports": {
|
|
8
8
|
"import": "./index.mjs",
|
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
},
|
|
17
17
|
"license": "Apache-2.0",
|
|
18
18
|
"dependencies": {
|
|
19
|
+
"@graffy/common": "0.15.11-alpha.1",
|
|
19
20
|
"pg": "^8.7.1",
|
|
20
|
-
"@graffy/common": "0.15.9",
|
|
21
21
|
"debug": "^4.3.2",
|
|
22
22
|
"sql-template-tag": "^4.0.0"
|
|
23
23
|
}
|
package/types/filter/getSql.d.ts
CHANGED