@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 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 "jsonb";
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 lhs(string) {
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"]`${lhs(ast[1])} IS NULL`;
186
- return sql__default["default"]`${lhs(ast[1])} = ${ast[2]}`;
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"]`${lhs(ast[1])} IS NOT NULL`;
190
- return sql__default["default"]`${lhs(ast[1])} <> ${ast[2]}`;
210
+ return sql__default["default"]`${lookup(ast[1])} IS NOT NULL`;
211
+ return binop("<>", ast[1], ast[2]);
191
212
  case "$lt":
192
- return sql__default["default"]`${lhs(ast[1])} < ${ast[2]}`;
213
+ return binop("<", ast[1], ast[2]);
193
214
  case "$lte":
194
- return sql__default["default"]`${lhs(ast[1])} <= ${ast[2]}`;
215
+ return binop("<=", ast[1], ast[2]);
195
216
  case "$gt":
196
- return sql__default["default"]`${lhs(ast[1])} > ${ast[2]}`;
217
+ return binop(">", ast[1], ast[2]);
197
218
  case "$gte":
198
- return sql__default["default"]`${lhs(ast[1])} >= ${ast[2]}`;
219
+ return binop(">=", ast[1], ast[2]);
199
220
  case "$re":
200
- return sql__default["default"]`${lhs(ast[1])} ~ ${ast[2]}`;
221
+ return binop("~", ast[1], ast[2]);
201
222
  case "$ire":
202
- return sql__default["default"]`${lhs(ast[1])} ~* ${ast[2]}`;
223
+ return binop("~*", ast[1], ast[2]);
203
224
  case "$in":
204
- return sql__default["default"]`${lhs(ast[1])} IN (${sql.join(ast[2])})`;
225
+ return sql__default["default"]`${lookup(ast[1])} IN (${sql.join(ast[2])})`;
205
226
  case "$nin":
206
- return sql__default["default"]`${lhs(ast[1])} NOT IN (${sql.join(ast[2])})`;
227
+ return sql__default["default"]`${lookup(ast[1])} NOT IN (${sql.join(ast[2])})`;
207
228
  case "$cts":
208
- return sql__default["default"]`${lhs(ast[1])} @> ${ast[2]}`;
229
+ return sql__default["default"]`${lookup(ast[1])} @> ${ast[2]}`;
209
230
  case "$ctd":
210
- return sql__default["default"]`${lhs(ast[1])} <@ ${ast[2]}`;
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
- return sql__default["default"]`${lhs(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
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"]`${lhs(ast[1])} && ${ast[2]}`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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 === "object" && value && !value.$put ? sql__default["default"]`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : stripAttributes(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 JSON.stringify(object);
286
- const _a = object, { $put } = _a, rest = __objRest(_a, ["$put"]);
287
- return JSON.stringify(rest);
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
- return suffix.length ? sql__default["default"]`"${sql.raw(prefix2)}" #> '{"${suffix.join('","')}"}'` : sql__default["default"]`"${sql.raw(prefix2)}"`;
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, rootQuery), rootQuery).known || [];
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
- return transactionDb.read(query, tableOpts, readOpts);
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
- return transactionDb.write(change, tableOpts, writeOpts);
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 { isEmpty, encodePath, isPlainObject, unwrap, decodeArgs, slice, finalize, isRange, decodeGraph, mergeObject, merge, encodeGraph, wrapObject } from "@graffy/common";
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 "jsonb";
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 lhs(string) {
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`${lhs(ast[1])} IS NULL`;
178
- return sql`${lhs(ast[1])} = ${ast[2]}`;
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`${lhs(ast[1])} IS NOT NULL`;
182
- return sql`${lhs(ast[1])} <> ${ast[2]}`;
202
+ return sql`${lookup(ast[1])} IS NOT NULL`;
203
+ return binop("<>", ast[1], ast[2]);
183
204
  case "$lt":
184
- return sql`${lhs(ast[1])} < ${ast[2]}`;
205
+ return binop("<", ast[1], ast[2]);
185
206
  case "$lte":
186
- return sql`${lhs(ast[1])} <= ${ast[2]}`;
207
+ return binop("<=", ast[1], ast[2]);
187
208
  case "$gt":
188
- return sql`${lhs(ast[1])} > ${ast[2]}`;
209
+ return binop(">", ast[1], ast[2]);
189
210
  case "$gte":
190
- return sql`${lhs(ast[1])} >= ${ast[2]}`;
211
+ return binop(">=", ast[1], ast[2]);
191
212
  case "$re":
192
- return sql`${lhs(ast[1])} ~ ${ast[2]}`;
213
+ return binop("~", ast[1], ast[2]);
193
214
  case "$ire":
194
- return sql`${lhs(ast[1])} ~* ${ast[2]}`;
215
+ return binop("~*", ast[1], ast[2]);
195
216
  case "$in":
196
- return sql`${lhs(ast[1])} IN (${join(ast[2])})`;
217
+ return sql`${lookup(ast[1])} IN (${join(ast[2])})`;
197
218
  case "$nin":
198
- return sql`${lhs(ast[1])} NOT IN (${join(ast[2])})`;
219
+ return sql`${lookup(ast[1])} NOT IN (${join(ast[2])})`;
199
220
  case "$cts":
200
- return sql`${lhs(ast[1])} @> ${ast[2]}`;
221
+ return sql`${lookup(ast[1])} @> ${ast[2]}`;
201
222
  case "$ctd":
202
- return sql`${lhs(ast[1])} <@ ${ast[2]}`;
223
+ return sql`${lookup(ast[1])} <@ ${ast[2]}`;
203
224
  case "$ovl":
204
225
  switch (getColumnType(ast[1])) {
205
226
  case "jsonb":
206
- return sql`${lhs(ast[1])} ?| ${Array.isArray(ast[2]) ? ast[2] : Object.keys(ast[2])}`;
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`${lhs(ast[1])} && ${ast[2]}`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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(${lhs(ast[1])}) ${lhs(ast[2])})`;
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 === "object" && value && !value.$put ? sql`jsonb_strip_nulls(${getJsonUpdate(value, name, [])})` : stripAttributes(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 JSON.stringify(object);
278
- const _a = object, { $put } = _a, rest = __objRest(_a, ["$put"]);
279
- return JSON.stringify(rest);
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
- return suffix.length ? sql`"${raw(prefix2)}" #> '{"${suffix.join('","')}"}'` : sql`"${raw(prefix2)}"`;
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, rootQuery), rootQuery).known || [];
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
- return transactionDb.read(query, tableOpts, readOpts);
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
- return transactionDb.write(change, tableOpts, writeOpts);
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.9",
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
  }
@@ -1,3 +1,3 @@
1
1
  export default function getSql(filter: any, getLookupSql: any, getColumnType?: typeof defaultColumnType): any;
2
- declare function defaultColumnType(): string;
2
+ declare function defaultColumnType(_: any): string;
3
3
  export {};