@graffy/pg 0.16.3-alpha.1 → 0.16.3-alpha.3

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/index.cjs CHANGED
@@ -206,24 +206,32 @@ const getJsonBuildValue = (value) => {
206
206
  return sql`${value}::text`;
207
207
  return sql`${JSON.stringify(stripAttributes(value))}::jsonb`;
208
208
  };
209
- const lookup = (prop) => {
209
+ const lookup = (prop, options) => {
210
210
  const [prefix, ...suffix] = prop.split(".");
211
- return suffix.length ? sql`"${raw(prefix)}" #> ${suffix}` : sql`"${raw(prefix)}"`;
211
+ if (!suffix.length)
212
+ return sql`"${raw(prefix)}"`;
213
+ const { types: types2 } = options.schema;
214
+ if (types2[prefix] === "jsonb") {
215
+ return sql`"${raw(prefix)}" #> ${suffix}`;
216
+ } else if (types2[prefix] === "cube" && suffix.length === 1) {
217
+ return sql`"${raw(prefix)}" ~> ${parseInt(suffix[0])}`;
218
+ } else {
219
+ throw Error(`pg.cannot_lookup ${prop}`);
220
+ }
212
221
  };
213
222
  const lookupNumeric = (prop) => {
214
223
  const [prefix, ...suffix] = prop.split(".");
215
- return suffix.length ? sql`CASE WHEN "${raw(prefix)}" #> ${suffix} = 'null'::jsonb THEN 0 ELSE ("${raw(
216
- prefix
217
- )}" #> ${suffix})::numeric END` : sql`"${raw(prefix)}"`;
224
+ return suffix.length ? sql`CASE WHEN "${raw(prefix)}" #> ${suffix} = 'null'::jsonb
225
+ THEN 0 ELSE ("${raw(prefix)}" #> ${suffix})::numeric END` : sql`"${raw(prefix)}"`;
218
226
  };
219
227
  const aggSql = {
220
228
  $sum: (prop) => sql`sum((${lookupNumeric(prop)})::numeric)`,
221
- $card: (prop) => sql`count(distinct(${lookup(prop)}))`,
229
+ $card: (prop, options) => sql`count(distinct(${lookup(prop, options)}))`,
222
230
  $avg: (prop) => sql`avg((${lookupNumeric(prop)})::numeric)`,
223
231
  $max: (prop) => sql`max((${lookupNumeric(prop)})::numeric)`,
224
232
  $min: (prop) => sql`min((${lookupNumeric(prop)})::numeric)`
225
233
  };
226
- const getSelectCols = (table, projection = null) => {
234
+ const getSelectCols = (options, projection = null) => {
227
235
  if (!projection)
228
236
  return sql`*`;
229
237
  const sqls = [];
@@ -233,7 +241,7 @@ const getSelectCols = (table, projection = null) => {
233
241
  } else if (aggSql[key]) {
234
242
  const subSqls = [];
235
243
  for (const prop in projection[key]) {
236
- subSqls.push(sql`${prop}::text, ${aggSql[key](prop)}`);
244
+ subSqls.push(sql`${prop}::text, ${aggSql[key](prop, options)}`);
237
245
  }
238
246
  sqls.push(
239
247
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
@@ -317,14 +325,17 @@ function stripAttributes(object) {
317
325
  if (Array.isArray(object)) {
318
326
  return object.map((item) => stripAttributes(item));
319
327
  }
320
- return Object.entries(object).reduce((out, [key, val]) => {
321
- if (key === "$put" || val === null)
328
+ return Object.entries(object).reduce(
329
+ (out, [key, val]) => {
330
+ if (key === "$put" || val === null)
331
+ return out;
332
+ if (out === null)
333
+ out = {};
334
+ out[key] = stripAttributes(val);
322
335
  return out;
323
- if (out === null)
324
- out = {};
325
- out[key] = stripAttributes(val);
326
- return out;
327
- }, null);
336
+ },
337
+ null
338
+ );
328
339
  }
329
340
  const opSql = {
330
341
  $and: "AND",
@@ -431,11 +442,16 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
431
442
  if (!common.isEmpty(filter))
432
443
  where.push(getSql(filter, options));
433
444
  if (!hasRangeArg)
434
- return { meta: meta(baseKey), where, limit: 1 };
435
- const groupCols = Array.isArray($group) && $group.length && $group.map(lookup);
445
+ return {
446
+ meta: meta(baseKey),
447
+ where,
448
+ limit: 1,
449
+ ensureSingleRow: $group === true
450
+ };
451
+ const groupCols = Array.isArray($group) && $group.length && $group.map((prop) => lookup(prop, options));
436
452
  const group = groupCols ? join(groupCols, ", ") : void 0;
437
453
  const orderCols = ($order || [idCol]).map(
438
- (orderItem) => orderItem[0] === "!" ? sql`-(${lookup(orderItem.slice(1))})::float8` : lookup(orderItem)
454
+ (orderItem) => orderItem[0] === "!" ? sql`-(${lookup(orderItem.slice(1), options)})::float8` : lookup(orderItem, options)
439
455
  );
440
456
  Object.entries({ $after, $before, $since, $until }).forEach(
441
457
  ([name, value]) => {
@@ -445,7 +461,7 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
445
461
  );
446
462
  const order = !$group && join(
447
463
  ($order || [idCol]).map(
448
- (orderItem) => orderItem[0] === "!" ? sql`${lookup(orderItem.slice(1))} ${$last ? sql`ASC` : sql`DESC`}` : sql`${lookup(orderItem)} ${$last ? sql`DESC` : sql`ASC`}`
464
+ (orderItem) => orderItem[0] === "!" ? sql`${lookup(orderItem.slice(1), options)} ${$last ? sql`ASC` : sql`DESC`}` : sql`${lookup(orderItem, options)} ${$last ? sql`DESC` : sql`ASC`}`
449
465
  ),
450
466
  ", "
451
467
  );
@@ -458,7 +474,8 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
458
474
  where,
459
475
  order,
460
476
  group,
461
- limit: $first || $last
477
+ limit: $first || $last,
478
+ ensureSingleRow: true
462
479
  };
463
480
  }
464
481
  function getBoundCond(orderCols, bound, kind) {
@@ -492,12 +509,26 @@ function getBoundCond(orderCols, bound, kind) {
492
509
  }
493
510
  const MAX_LIMIT = 4096;
494
511
  function selectByArgs(args, projection, options) {
495
- const { table } = options;
496
- const { where, order, group, limit, meta } = getArgSql(args, options);
512
+ const { table, idCol } = options;
513
+ const { where, order, group, limit, meta, ensureSingleRow } = getArgSql(
514
+ args,
515
+ options
516
+ );
497
517
  const clampedLimit = Math.min(MAX_LIMIT, limit || MAX_LIMIT);
518
+ if (!ensureSingleRow) {
519
+ return sql`
520
+ SELECT
521
+ ${getSelectCols(options, projection)}, ${meta}
522
+ FROM "${raw(table)}" WHERE "${raw(idCol)}" = (
523
+ SELECT "${raw(idCol)}" FROM "${raw(table)}"
524
+ ${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
525
+ LIMIT 2
526
+ )
527
+ `;
528
+ }
498
529
  return sql`
499
530
  SELECT
500
- ${getSelectCols(table, projection)}, ${meta}
531
+ ${getSelectCols(options, projection)}, ${meta}
501
532
  FROM "${raw(table)}"
502
533
  ${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
503
534
  ${group ? sql`GROUP BY ${group}` : empty}
@@ -509,7 +540,7 @@ function selectByIds(ids, projection, options) {
509
540
  const { table, idCol } = options;
510
541
  return sql`
511
542
  SELECT
512
- ${getSelectCols(table, projection)}, ${getIdMeta(options)}
543
+ ${getSelectCols(options, projection)}, ${getIdMeta(options)}
513
544
  FROM "${raw(table)}"
514
545
  WHERE "${raw(idCol)}" IN (${join(ids)})
515
546
  `;
@@ -530,7 +561,7 @@ function getSingleSql(arg, options) {
530
561
  SELECT "${raw(idCol)}"
531
562
  FROM "${raw(table)}"
532
563
  WHERE ${join(where, " AND ")}
533
- LIMIT 1
564
+ LIMIT 2
534
565
  )`,
535
566
  meta
536
567
  };
@@ -542,7 +573,7 @@ function patch(object, arg, options) {
542
573
  return sql`
543
574
  UPDATE "${raw(table)}" SET ${getUpdates(row, options)}
544
575
  WHERE ${where}
545
- RETURNING ${getSelectCols()}, ${meta}`;
576
+ RETURNING ${getSelectCols(options)}, ${meta}`;
546
577
  }
547
578
  function put(object, arg, options) {
548
579
  const { idCol, table } = options;
@@ -560,7 +591,7 @@ function put(object, arg, options) {
560
591
  return sql`
561
592
  INSERT INTO "${raw(table)}" (${cols}) VALUES (${vals})
562
593
  ON CONFLICT (${conflictTarget}) DO UPDATE SET (${cols}) = (${vals})
563
- RETURNING ${getSelectCols()}, ${meta}`;
594
+ RETURNING ${getSelectCols(options)}, ${meta}`;
564
595
  }
565
596
  function del(arg, options) {
566
597
  const { table } = options;
package/index.mjs CHANGED
@@ -204,24 +204,32 @@ const getJsonBuildValue = (value) => {
204
204
  return sql`${value}::text`;
205
205
  return sql`${JSON.stringify(stripAttributes(value))}::jsonb`;
206
206
  };
207
- const lookup = (prop) => {
207
+ const lookup = (prop, options) => {
208
208
  const [prefix, ...suffix] = prop.split(".");
209
- return suffix.length ? sql`"${raw(prefix)}" #> ${suffix}` : sql`"${raw(prefix)}"`;
209
+ if (!suffix.length)
210
+ return sql`"${raw(prefix)}"`;
211
+ const { types: types2 } = options.schema;
212
+ if (types2[prefix] === "jsonb") {
213
+ return sql`"${raw(prefix)}" #> ${suffix}`;
214
+ } else if (types2[prefix] === "cube" && suffix.length === 1) {
215
+ return sql`"${raw(prefix)}" ~> ${parseInt(suffix[0])}`;
216
+ } else {
217
+ throw Error(`pg.cannot_lookup ${prop}`);
218
+ }
210
219
  };
211
220
  const lookupNumeric = (prop) => {
212
221
  const [prefix, ...suffix] = prop.split(".");
213
- return suffix.length ? sql`CASE WHEN "${raw(prefix)}" #> ${suffix} = 'null'::jsonb THEN 0 ELSE ("${raw(
214
- prefix
215
- )}" #> ${suffix})::numeric END` : sql`"${raw(prefix)}"`;
222
+ return suffix.length ? sql`CASE WHEN "${raw(prefix)}" #> ${suffix} = 'null'::jsonb
223
+ THEN 0 ELSE ("${raw(prefix)}" #> ${suffix})::numeric END` : sql`"${raw(prefix)}"`;
216
224
  };
217
225
  const aggSql = {
218
226
  $sum: (prop) => sql`sum((${lookupNumeric(prop)})::numeric)`,
219
- $card: (prop) => sql`count(distinct(${lookup(prop)}))`,
227
+ $card: (prop, options) => sql`count(distinct(${lookup(prop, options)}))`,
220
228
  $avg: (prop) => sql`avg((${lookupNumeric(prop)})::numeric)`,
221
229
  $max: (prop) => sql`max((${lookupNumeric(prop)})::numeric)`,
222
230
  $min: (prop) => sql`min((${lookupNumeric(prop)})::numeric)`
223
231
  };
224
- const getSelectCols = (table, projection = null) => {
232
+ const getSelectCols = (options, projection = null) => {
225
233
  if (!projection)
226
234
  return sql`*`;
227
235
  const sqls = [];
@@ -231,7 +239,7 @@ const getSelectCols = (table, projection = null) => {
231
239
  } else if (aggSql[key]) {
232
240
  const subSqls = [];
233
241
  for (const prop in projection[key]) {
234
- subSqls.push(sql`${prop}::text, ${aggSql[key](prop)}`);
242
+ subSqls.push(sql`${prop}::text, ${aggSql[key](prop, options)}`);
235
243
  }
236
244
  sqls.push(
237
245
  sql`jsonb_build_object(${join(subSqls, ", ")}) AS "${raw(key)}"`
@@ -315,14 +323,17 @@ function stripAttributes(object) {
315
323
  if (Array.isArray(object)) {
316
324
  return object.map((item) => stripAttributes(item));
317
325
  }
318
- return Object.entries(object).reduce((out, [key, val]) => {
319
- if (key === "$put" || val === null)
326
+ return Object.entries(object).reduce(
327
+ (out, [key, val]) => {
328
+ if (key === "$put" || val === null)
329
+ return out;
330
+ if (out === null)
331
+ out = {};
332
+ out[key] = stripAttributes(val);
320
333
  return out;
321
- if (out === null)
322
- out = {};
323
- out[key] = stripAttributes(val);
324
- return out;
325
- }, null);
334
+ },
335
+ null
336
+ );
326
337
  }
327
338
  const opSql = {
328
339
  $and: "AND",
@@ -429,11 +440,16 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
429
440
  if (!isEmpty(filter))
430
441
  where.push(getSql(filter, options));
431
442
  if (!hasRangeArg)
432
- return { meta: meta(baseKey), where, limit: 1 };
433
- const groupCols = Array.isArray($group) && $group.length && $group.map(lookup);
443
+ return {
444
+ meta: meta(baseKey),
445
+ where,
446
+ limit: 1,
447
+ ensureSingleRow: $group === true
448
+ };
449
+ const groupCols = Array.isArray($group) && $group.length && $group.map((prop) => lookup(prop, options));
434
450
  const group = groupCols ? join(groupCols, ", ") : void 0;
435
451
  const orderCols = ($order || [idCol]).map(
436
- (orderItem) => orderItem[0] === "!" ? sql`-(${lookup(orderItem.slice(1))})::float8` : lookup(orderItem)
452
+ (orderItem) => orderItem[0] === "!" ? sql`-(${lookup(orderItem.slice(1), options)})::float8` : lookup(orderItem, options)
437
453
  );
438
454
  Object.entries({ $after, $before, $since, $until }).forEach(
439
455
  ([name, value]) => {
@@ -443,7 +459,7 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
443
459
  );
444
460
  const order = !$group && join(
445
461
  ($order || [idCol]).map(
446
- (orderItem) => orderItem[0] === "!" ? sql`${lookup(orderItem.slice(1))} ${$last ? sql`ASC` : sql`DESC`}` : sql`${lookup(orderItem)} ${$last ? sql`DESC` : sql`ASC`}`
462
+ (orderItem) => orderItem[0] === "!" ? sql`${lookup(orderItem.slice(1), options)} ${$last ? sql`ASC` : sql`DESC`}` : sql`${lookup(orderItem, options)} ${$last ? sql`DESC` : sql`ASC`}`
447
463
  ),
448
464
  ", "
449
465
  );
@@ -456,7 +472,8 @@ function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $curs
456
472
  where,
457
473
  order,
458
474
  group,
459
- limit: $first || $last
475
+ limit: $first || $last,
476
+ ensureSingleRow: true
460
477
  };
461
478
  }
462
479
  function getBoundCond(orderCols, bound, kind) {
@@ -490,12 +507,26 @@ function getBoundCond(orderCols, bound, kind) {
490
507
  }
491
508
  const MAX_LIMIT = 4096;
492
509
  function selectByArgs(args, projection, options) {
493
- const { table } = options;
494
- const { where, order, group, limit, meta } = getArgSql(args, options);
510
+ const { table, idCol } = options;
511
+ const { where, order, group, limit, meta, ensureSingleRow } = getArgSql(
512
+ args,
513
+ options
514
+ );
495
515
  const clampedLimit = Math.min(MAX_LIMIT, limit || MAX_LIMIT);
516
+ if (!ensureSingleRow) {
517
+ return sql`
518
+ SELECT
519
+ ${getSelectCols(options, projection)}, ${meta}
520
+ FROM "${raw(table)}" WHERE "${raw(idCol)}" = (
521
+ SELECT "${raw(idCol)}" FROM "${raw(table)}"
522
+ ${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
523
+ LIMIT 2
524
+ )
525
+ `;
526
+ }
496
527
  return sql`
497
528
  SELECT
498
- ${getSelectCols(table, projection)}, ${meta}
529
+ ${getSelectCols(options, projection)}, ${meta}
499
530
  FROM "${raw(table)}"
500
531
  ${where.length ? sql`WHERE ${join(where, " AND ")}` : empty}
501
532
  ${group ? sql`GROUP BY ${group}` : empty}
@@ -507,7 +538,7 @@ function selectByIds(ids, projection, options) {
507
538
  const { table, idCol } = options;
508
539
  return sql`
509
540
  SELECT
510
- ${getSelectCols(table, projection)}, ${getIdMeta(options)}
541
+ ${getSelectCols(options, projection)}, ${getIdMeta(options)}
511
542
  FROM "${raw(table)}"
512
543
  WHERE "${raw(idCol)}" IN (${join(ids)})
513
544
  `;
@@ -528,7 +559,7 @@ function getSingleSql(arg, options) {
528
559
  SELECT "${raw(idCol)}"
529
560
  FROM "${raw(table)}"
530
561
  WHERE ${join(where, " AND ")}
531
- LIMIT 1
562
+ LIMIT 2
532
563
  )`,
533
564
  meta
534
565
  };
@@ -540,7 +571,7 @@ function patch(object, arg, options) {
540
571
  return sql`
541
572
  UPDATE "${raw(table)}" SET ${getUpdates(row, options)}
542
573
  WHERE ${where}
543
- RETURNING ${getSelectCols()}, ${meta}`;
574
+ RETURNING ${getSelectCols(options)}, ${meta}`;
544
575
  }
545
576
  function put(object, arg, options) {
546
577
  const { idCol, table } = options;
@@ -558,7 +589,7 @@ function put(object, arg, options) {
558
589
  return sql`
559
590
  INSERT INTO "${raw(table)}" (${cols}) VALUES (${vals})
560
591
  ON CONFLICT (${conflictTarget}) DO UPDATE SET (${cols}) = (${vals})
561
- RETURNING ${getSelectCols()}, ${meta}`;
592
+ RETURNING ${getSelectCols(options)}, ${meta}`;
562
593
  }
563
594
  function del(arg, options) {
564
595
  const { table } = options;
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.16.3-alpha.1",
5
+ "version": "0.16.3-alpha.3",
6
6
  "main": "./index.cjs",
7
7
  "exports": {
8
8
  "import": "./index.mjs",
@@ -16,7 +16,7 @@
16
16
  },
17
17
  "license": "Apache-2.0",
18
18
  "dependencies": {
19
- "@graffy/common": "0.16.3-alpha.1",
19
+ "@graffy/common": "0.16.3-alpha.3",
20
20
  "debug": "^4.3.3"
21
21
  },
22
22
  "peerDependencies": {
@@ -1,8 +1,8 @@
1
1
  export function cubeLiteralSql(value: any): Sql;
2
2
  export function getJsonBuildTrusted(variadic: any): Sql;
3
- export function lookup(prop: any): Sql;
3
+ export function lookup(prop: any, options: any): Sql;
4
4
  export function lookupNumeric(prop: any): Sql;
5
- export function getSelectCols(table: any, projection?: any): Sql;
5
+ export function getSelectCols(options: any, projection?: any): Sql;
6
6
  export function getInsert(row: any, options: any): {
7
7
  cols: Sql;
8
8
  vals: Sql;
@@ -5,7 +5,7 @@
5
5
  @param {{prefix: string, idCol: string, verDefault: string}} options
6
6
 
7
7
  @typedef { import('sql-template-tag').Sql } Sql
8
- @return {{ meta: Sql, where: Sql[], order?: Sql, group?: Sql, limit: number }}
8
+ @return {{ meta: Sql, where: Sql[], order?: Sql, group?: Sql, limit: number, ensureSingleRow: boolean }}
9
9
  */
10
10
  export default function getArgSql({ $first, $last, $after, $before, $since, $until, $all, $cursor: _, ...rest }: object, options: {
11
11
  prefix: string;
@@ -17,6 +17,7 @@ export default function getArgSql({ $first, $last, $after, $before, $since, $unt
17
17
  order?: Sql;
18
18
  group?: Sql;
19
19
  limit: number;
20
+ ensureSingleRow: boolean;
20
21
  };
21
22
  /**
22
23
  * Uses the args object (typically passed in the $key attribute)