@undefineds.co/xpod 0.2.43 → 0.2.44

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.
@@ -12,7 +12,10 @@
12
12
  Object.defineProperty(exports, "__esModule", { value: true });
13
13
  exports.PgQuintStore = void 0;
14
14
  const pglite_1 = require("@electric-sql/pglite");
15
+ const node_crypto_1 = require("node:crypto");
15
16
  const BaseQuintStore_1 = require("./BaseQuintStore");
17
+ const serialization_1 = require("./serialization");
18
+ const value_types_1 = require("./value-types");
16
19
  const PostgresPoolManager_1 = require("../database/PostgresPoolManager");
17
20
  /**
18
21
  * PostgreSQL 兼容的分隔符
@@ -31,6 +34,9 @@ function toPgSafe(str) {
31
34
  function fromPgSafe(str) {
32
35
  return str.replace(new RegExp(PG_SEP, 'g'), '\u0000');
33
36
  }
37
+ function digestObject(value) {
38
+ return (0, node_crypto_1.createHash)('sha256').update(toPgSafe(value)).digest('hex');
39
+ }
34
40
  /**
35
41
  * PGLite 执行器
36
42
  */
@@ -208,43 +214,45 @@ class PgQuintStore extends BaseQuintStore_1.BaseQuintStore {
208
214
  // PostgreSQL 建表语法
209
215
  await this.executor.exec(`
210
216
  CREATE TABLE IF NOT EXISTS quints (
217
+ object_kind TEXT,
218
+ object_key TEXT,
219
+ object_text TEXT,
220
+ object_digest TEXT,
211
221
  graph TEXT NOT NULL,
212
222
  subject TEXT NOT NULL,
213
223
  predicate TEXT NOT NULL,
214
224
  object TEXT NOT NULL,
215
- vector TEXT,
216
- PRIMARY KEY (graph, subject, predicate, object)
225
+ vector TEXT
217
226
  )
218
227
  `);
228
+ await this.ensureTypedObjectSchema();
219
229
  const indexes = [
220
- 'CREATE INDEX IF NOT EXISTS idx_spog ON quints (subject, predicate, object, graph)',
221
- 'CREATE INDEX IF NOT EXISTS idx_ogsp ON quints (object, graph, subject, predicate)',
222
- 'CREATE INDEX IF NOT EXISTS idx_gspo ON quints (graph, subject, predicate, object)',
223
- 'CREATE INDEX IF NOT EXISTS idx_sopg ON quints (subject, object, predicate, graph)',
224
- 'CREATE INDEX IF NOT EXISTS idx_pogs ON quints (predicate, object, graph, subject)',
225
- 'CREATE INDEX IF NOT EXISTS idx_gpos ON quints (graph, predicate, object, subject)',
230
+ 'CREATE INDEX IF NOT EXISTS idx_quints_graph ON quints (graph)',
231
+ 'CREATE INDEX IF NOT EXISTS idx_quints_subject ON quints (subject)',
232
+ 'CREATE INDEX IF NOT EXISTS idx_quints_predicate ON quints (predicate)',
233
+ 'CREATE INDEX IF NOT EXISTS idx_quints_object_key ON quints (object_kind, object_key)',
234
+ 'CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_key ON quints (predicate, object_kind, object_key)',
235
+ 'CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_digest ON quints (predicate, object_kind, object_digest)',
236
+ 'CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_key ON quints (graph, subject, predicate, object_kind, object_key) WHERE object_key IS NOT NULL',
237
+ 'CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_digest ON quints (graph, subject, predicate, object_kind, object_digest) WHERE object_digest IS NOT NULL',
238
+ 'CREATE INDEX IF NOT EXISTS idx_quints_gsp ON quints (graph, subject, predicate)',
239
+ 'CREATE INDEX IF NOT EXISTS idx_quints_sp ON quints (subject, predicate)',
240
+ 'CREATE INDEX IF NOT EXISTS idx_quints_gp ON quints (graph, predicate)',
226
241
  ];
227
242
  for (const indexSql of indexes) {
228
243
  await this.executor.exec(indexSql);
229
244
  }
230
245
  }
231
246
  /**
232
- * 重写 put 方法,使用 PostgreSQL ON CONFLICT
247
+ * 重写 put 方法,避免长文本对象进入 PostgreSQL btree 唯一键
233
248
  */
234
249
  async put(quint) {
235
250
  this.ensureOpen();
236
- const row = this.quintToRow(quint);
237
- // PostgreSQL UPSERT 语法
238
- const sql = `
239
- INSERT INTO quints (graph, subject, predicate, object, vector)
240
- VALUES ($1, $2, $3, $4, $5)
241
- ON CONFLICT (graph, subject, predicate, object)
242
- DO UPDATE SET vector = EXCLUDED.vector
243
- `;
244
- await this.executor.execute(sql, [row.graph, row.subject, row.predicate, row.object, row.vector]);
251
+ const row = this.quintToPgRow(quint);
252
+ await this.executor.executeInTransaction(this.writeStatementsForRow(row));
245
253
  }
246
254
  /**
247
- * 重写 multiPut 方法,使用 PostgreSQL 的 ON CONFLICT
255
+ * 重写 multiPut 方法,使用同一事务保持批量写入幂等
248
256
  */
249
257
  async multiPut(quintList) {
250
258
  console.log(`[PgQuintStore.multiPut] Starting: ${quintList.length} quints`);
@@ -253,22 +261,582 @@ class PgQuintStore extends BaseQuintStore_1.BaseQuintStore {
253
261
  console.log(`[PgQuintStore.multiPut] Empty list, skipping`);
254
262
  return;
255
263
  }
264
+ const statements = quintList.flatMap(quint => {
265
+ const row = this.quintToPgRow(quint);
266
+ return this.writeStatementsForRow(row);
267
+ });
268
+ console.log(`[PgQuintStore.multiPut] Executing ${statements.length} statements in transaction`);
269
+ const start = Date.now();
270
+ await this.executor.executeInTransaction(statements);
271
+ console.log(`[PgQuintStore.multiPut] Completed in ${Date.now() - start}ms`);
272
+ }
273
+ async multiDel(quintList) {
274
+ this.ensureOpen();
275
+ if (quintList.length === 0)
276
+ return;
256
277
  const statements = quintList.map(quint => {
257
- const row = this.quintToRow(quint);
278
+ const row = this.quintToPgRow(quint);
258
279
  return {
259
280
  sql: `
260
- INSERT INTO quints (graph, subject, predicate, object, vector)
261
- VALUES ($1, $2, $3, $4, $5)
262
- ON CONFLICT (graph, subject, predicate, object)
263
- DO UPDATE SET vector = EXCLUDED.vector
281
+ DELETE FROM quints
282
+ WHERE graph = $1
283
+ AND subject = $2
284
+ AND predicate = $3
285
+ AND object = $4
264
286
  `,
265
- params: [row.graph, row.subject, row.predicate, row.object, row.vector],
287
+ params: [row.graph, row.subject, row.predicate, row.object],
266
288
  };
267
289
  });
268
- console.log(`[PgQuintStore.multiPut] Executing ${statements.length} statements in transaction`);
269
- const start = Date.now();
270
290
  await this.executor.executeInTransaction(statements);
271
- console.log(`[PgQuintStore.multiPut] Completed in ${Date.now() - start}ms`);
291
+ }
292
+ async getAttributes(subjects, predicates, graph) {
293
+ this.ensureOpen();
294
+ if (subjects.length === 0 || predicates.length === 0) {
295
+ return new Map();
296
+ }
297
+ const subjectPlaceholders = subjects.map(() => '?').join(', ');
298
+ const predicatePlaceholders = predicates.map(() => '?').join(', ');
299
+ let sql = `
300
+ SELECT subject, predicate, object
301
+ FROM quints
302
+ WHERE subject IN (${subjectPlaceholders})
303
+ AND predicate IN (${predicatePlaceholders})
304
+ `;
305
+ const params = [...subjects, ...predicates];
306
+ if (graph && graph.termType !== 'DefaultGraph') {
307
+ const graphValue = (0, serialization_1.termToId)(graph);
308
+ sql += ` AND graph = ?`;
309
+ params.push(graphValue);
310
+ }
311
+ const rows = await this.executor.query(sql, params);
312
+ const result = new Map();
313
+ for (const row of rows) {
314
+ if (!result.has(row.subject)) {
315
+ result.set(row.subject, new Map());
316
+ }
317
+ const predicateMap = result.get(row.subject);
318
+ if (!predicateMap.has(row.predicate)) {
319
+ predicateMap.set(row.predicate, []);
320
+ }
321
+ predicateMap.get(row.predicate).push(this.deserializeObject(row.object));
322
+ }
323
+ return result;
324
+ }
325
+ buildWhereClause(pattern) {
326
+ const conditions = [];
327
+ const params = [];
328
+ const predicate = this.extractExactPredicate(pattern.predicate);
329
+ this.addPgCondition(conditions, params, 'graph', pattern.graph);
330
+ this.addPgCondition(conditions, params, 'subject', pattern.subject);
331
+ this.addPgCondition(conditions, params, 'predicate', pattern.predicate);
332
+ if (pattern.object) {
333
+ this.addObjectConditions(conditions, params, undefined, pattern.object, predicate);
334
+ }
335
+ const whereClause = conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '';
336
+ return { whereClause, params };
337
+ }
338
+ addPgCondition(conditions, params, column, match) {
339
+ if (!match)
340
+ return;
341
+ if (typeof match === 'object' && 'termType' in match) {
342
+ const value = (0, serialization_1.termToId)(match);
343
+ conditions.push(`${column} = ?`);
344
+ params.push(value);
345
+ return;
346
+ }
347
+ const ops = match;
348
+ if (ops.$eq !== undefined) {
349
+ const value = this.serializeOpValue(ops.$eq, false, '$eq');
350
+ conditions.push(`${column} = ?`);
351
+ params.push(String(value));
352
+ return;
353
+ }
354
+ super.addConditions(conditions, params, column, match, false);
355
+ }
356
+ addAliasedConditions(conditions, params, alias, pattern) {
357
+ this.addAliasedPgCondition(conditions, params, alias, 'graph', pattern.graph, false);
358
+ this.addAliasedPgCondition(conditions, params, alias, 'subject', pattern.subject, false);
359
+ this.addAliasedPgCondition(conditions, params, alias, 'predicate', pattern.predicate, false);
360
+ this.addAliasedObjectConditions(conditions, params, alias, pattern);
361
+ }
362
+ buildCompoundQuery(compound, options) {
363
+ const { patterns, joinOn, select } = compound;
364
+ const params = [];
365
+ let selectClause = `q0.${joinOn} as join_value`;
366
+ if (select) {
367
+ for (const s of select) {
368
+ selectClause += `, q${s.pattern}.${s.field} as ${s.alias}`;
369
+ }
370
+ }
371
+ else {
372
+ for (let i = 0; i < patterns.length; i++) {
373
+ selectClause += `, q${i}.object as p${i}_object`;
374
+ selectClause += `, q${i}.predicate as p${i}_predicate`;
375
+ }
376
+ }
377
+ let fromClause = 'quints q0';
378
+ for (let i = 1; i < patterns.length; i++) {
379
+ fromClause += ` JOIN quints q${i} ON q0.${joinOn} = q${i}.${joinOn}`;
380
+ fromClause += ` AND q0.graph = q${i}.graph`;
381
+ }
382
+ const whereParts = [];
383
+ for (let i = 0; i < patterns.length; i++) {
384
+ const pattern = patterns[i];
385
+ const alias = `q${i}`;
386
+ this.addAliasedConditions(whereParts, params, alias, pattern);
387
+ }
388
+ let sql = `SELECT ${selectClause} FROM ${fromClause}`;
389
+ if (whereParts.length > 0) {
390
+ sql += ` WHERE ${whereParts.join(' AND ')}`;
391
+ }
392
+ if (options?.limit) {
393
+ sql += ` LIMIT ?`;
394
+ params.push(options.limit);
395
+ }
396
+ if (options?.offset) {
397
+ sql += ` OFFSET ?`;
398
+ params.push(options.offset);
399
+ }
400
+ return { sql, params };
401
+ }
402
+ buildSelectQuery(pattern, options) {
403
+ const { whereClause, params } = this.buildWhereClause(pattern);
404
+ let sql = `SELECT * FROM quints${whereClause}`;
405
+ if (options?.order && options.order.length > 0) {
406
+ const orderCols = options.order.map(field => {
407
+ if (field === 'object') {
408
+ const objectType = this.resolveObjectDataTypeForPattern(pattern);
409
+ if (objectType === 'longText') {
410
+ throw new Error('ORDER BY object is not supported for longText predicates');
411
+ }
412
+ return 'object_key';
413
+ }
414
+ return field;
415
+ }).join(', ');
416
+ sql += ` ORDER BY ${orderCols}`;
417
+ if (options.reverse) {
418
+ sql += ' DESC';
419
+ }
420
+ }
421
+ if (options?.limit) {
422
+ sql += ` LIMIT ?`;
423
+ params.push(options.limit);
424
+ }
425
+ if (options?.offset) {
426
+ sql += ` OFFSET ?`;
427
+ params.push(options.offset);
428
+ }
429
+ return { sql, params };
430
+ }
431
+ async ensureTypedObjectSchema() {
432
+ const statements = [
433
+ 'ALTER TABLE quints ADD COLUMN IF NOT EXISTS object_kind TEXT',
434
+ 'ALTER TABLE quints ADD COLUMN IF NOT EXISTS object_key TEXT',
435
+ 'ALTER TABLE quints ADD COLUMN IF NOT EXISTS object_text TEXT',
436
+ 'ALTER TABLE quints ADD COLUMN IF NOT EXISTS object_digest TEXT',
437
+ ];
438
+ for (const statement of statements) {
439
+ await this.executor.exec(statement);
440
+ }
441
+ // The old Postgres schema indexed complete RDF terms. Long literals can
442
+ // exceed the btree tuple size and surface as 500s while creating containers.
443
+ const obsoleteIndexes = [
444
+ 'idx_spog',
445
+ 'idx_ogsp',
446
+ 'idx_gspo',
447
+ 'idx_sopg',
448
+ 'idx_pogs',
449
+ 'idx_gpos',
450
+ 'idx_pg_spog',
451
+ 'idx_pg_ogsp',
452
+ 'idx_pg_gspo',
453
+ 'idx_pg_sopg',
454
+ 'idx_pg_pogs',
455
+ 'idx_pg_gpos',
456
+ 'idx_pg_graph_prefix',
457
+ 'idx_quints_predicate_object_text',
458
+ 'idx_quints_quint_hash',
459
+ 'idx_quints_graph_hash',
460
+ 'idx_quints_subject_hash',
461
+ 'idx_quints_predicate_hash',
462
+ 'idx_quints_object_hash',
463
+ 'idx_quints_gsp_hash',
464
+ 'idx_quints_sp_hash',
465
+ 'idx_quints_gp_hash',
466
+ ];
467
+ for (const indexName of obsoleteIndexes) {
468
+ await this.executor.exec(`DROP INDEX IF EXISTS ${indexName}`);
469
+ }
470
+ await this.executor.exec('ALTER TABLE quints DROP CONSTRAINT IF EXISTS quints_pkey');
471
+ const obsoleteColumns = [
472
+ 'quint_hash',
473
+ 'graph_hash',
474
+ 'subject_hash',
475
+ 'predicate_hash',
476
+ 'object_hash',
477
+ ];
478
+ for (const columnName of obsoleteColumns) {
479
+ await this.executor.exec(`ALTER TABLE quints DROP COLUMN IF EXISTS ${columnName}`);
480
+ }
481
+ await this.backfillMissingObjectIndexFields();
482
+ await this.executor.exec(`
483
+ UPDATE quints
484
+ SET object_kind = 'text'
485
+ WHERE object_kind = 'shortText'
486
+ `);
487
+ }
488
+ async backfillMissingObjectIndexFields() {
489
+ const rows = await this.executor.query(`
490
+ SELECT graph, subject, predicate, object
491
+ FROM quints
492
+ WHERE object_kind IS NULL
493
+ OR (object_key IS NULL AND object_digest IS NULL)
494
+ `);
495
+ for (const row of rows) {
496
+ const objectIndex = this.objectIndexForSerialized(row.predicate, row.object);
497
+ await this.executor.execute(`
498
+ UPDATE quints
499
+ SET object_kind = $1,
500
+ object_key = $2,
501
+ object_text = $3,
502
+ object_digest = $4
503
+ WHERE graph = $5
504
+ AND subject = $6
505
+ AND predicate = $7
506
+ AND object = $8
507
+ `, [
508
+ objectIndex.objectKind,
509
+ objectIndex.objectKey,
510
+ objectIndex.objectText,
511
+ this.objectDigestForIndex(row.object, objectIndex),
512
+ row.graph,
513
+ row.subject,
514
+ row.predicate,
515
+ row.object,
516
+ ]);
517
+ }
518
+ }
519
+ quintToPgRow(quint) {
520
+ const row = this.quintToRow(quint);
521
+ const objectIndex = this.objectIndexForTerm(row.predicate, quint.object);
522
+ return {
523
+ ...row,
524
+ objectKind: objectIndex.objectKind,
525
+ objectKey: objectIndex.objectKey,
526
+ objectText: objectIndex.objectText,
527
+ objectDigest: this.objectDigestForIndex(row.object, objectIndex),
528
+ };
529
+ }
530
+ objectIndexForTerm(predicate, object) {
531
+ return (0, value_types_1.objectIndexFieldsFromTerm)(object, {
532
+ predicate,
533
+ predicateObjectDataTypes: this.options.predicateObjectDataTypes,
534
+ textMaxBytes: this.options.textMaxBytes,
535
+ });
536
+ }
537
+ objectIndexForSerialized(predicate, object) {
538
+ return (0, value_types_1.objectIndexFieldsFromSerialized)(object, {
539
+ predicate,
540
+ predicateObjectDataTypes: this.options.predicateObjectDataTypes,
541
+ textMaxBytes: this.options.textMaxBytes,
542
+ });
543
+ }
544
+ objectDigestForIndex(serialized, fields) {
545
+ return fields.objectKey === null ? digestObject(serialized) : null;
546
+ }
547
+ writeStatementsForRow(row) {
548
+ const params = [
549
+ row.objectKind,
550
+ row.objectKey,
551
+ row.objectText,
552
+ row.objectDigest,
553
+ row.graph,
554
+ row.subject,
555
+ row.predicate,
556
+ row.object,
557
+ row.vector,
558
+ ];
559
+ if (row.objectKey !== null) {
560
+ return [{
561
+ sql: `
562
+ INSERT INTO quints (
563
+ object_kind, object_key, object_text, object_digest,
564
+ graph, subject, predicate, object, vector
565
+ )
566
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
567
+ ON CONFLICT (graph, subject, predicate, object_kind, object_key)
568
+ WHERE object_key IS NOT NULL
569
+ DO UPDATE SET
570
+ vector = EXCLUDED.vector,
571
+ object_text = EXCLUDED.object_text,
572
+ object_digest = EXCLUDED.object_digest
573
+ WHERE quints.object = EXCLUDED.object
574
+ `,
575
+ params,
576
+ }];
577
+ }
578
+ return [{
579
+ sql: `
580
+ INSERT INTO quints (
581
+ object_kind, object_key, object_text, object_digest,
582
+ graph, subject, predicate, object, vector
583
+ )
584
+ VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)
585
+ ON CONFLICT (graph, subject, predicate, object_kind, object_digest)
586
+ WHERE object_digest IS NOT NULL
587
+ DO UPDATE SET
588
+ object = CASE WHEN quints.object = EXCLUDED.object THEN EXCLUDED.object ELSE NULL END,
589
+ vector = EXCLUDED.vector,
590
+ object_text = EXCLUDED.object_text
591
+ `,
592
+ params,
593
+ }];
594
+ }
595
+ addObjectConditions(conditions, params, alias, match, predicate) {
596
+ const column = (name) => alias ? `${alias}.${name}` : name;
597
+ if (typeof match === 'object' && 'termType' in match) {
598
+ this.addObjectExactCondition(conditions, params, column, match, predicate);
599
+ return;
600
+ }
601
+ const ops = match;
602
+ if (ops.$eq !== undefined) {
603
+ this.addObjectExactValueCondition(conditions, params, column, ops.$eq, '$eq', predicate);
604
+ }
605
+ if (ops.$ne !== undefined) {
606
+ this.addObjectExactValueCondition(conditions, params, column, ops.$ne, '$ne', predicate);
607
+ }
608
+ if (ops.$gt !== undefined) {
609
+ this.addObjectComparableCondition(conditions, params, column, '>', ops.$gt, '$gt', predicate);
610
+ }
611
+ if (ops.$gte !== undefined) {
612
+ this.addObjectComparableCondition(conditions, params, column, '>=', ops.$gte, '$gte', predicate);
613
+ }
614
+ if (ops.$lt !== undefined) {
615
+ this.addObjectComparableCondition(conditions, params, column, '<', ops.$lt, '$lt', predicate);
616
+ }
617
+ if (ops.$lte !== undefined) {
618
+ this.addObjectComparableCondition(conditions, params, column, '<=', ops.$lte, '$lte', predicate);
619
+ }
620
+ if (ops.$in !== undefined && ops.$in.length > 0) {
621
+ const predicates = ops.$in.map((value) => this.objectPredicateForOperatorValue(value, '$in', predicate));
622
+ const placeholders = predicates.map((item) => {
623
+ if (item.fields.objectKey === null) {
624
+ return `(${column('object_digest')} = ? AND ${column('object')} = ?)`;
625
+ }
626
+ return `(${column('object_kind')} = ? AND ${column('object_key')} = ?)`;
627
+ }).join(' OR ');
628
+ conditions.push(`(${placeholders})`);
629
+ for (const item of predicates) {
630
+ if (item.fields.objectKey === null) {
631
+ params.push(this.objectDigestForIndex(item.serialized, item.fields), item.serialized);
632
+ }
633
+ else {
634
+ params.push(item.fields.objectKind, item.fields.objectKey);
635
+ }
636
+ }
637
+ }
638
+ if (ops.$notIn !== undefined && ops.$notIn.length > 0) {
639
+ const values = ops.$notIn.map((value) => this.objectPredicateForOperatorValue(value, '$notIn', predicate).serialized);
640
+ for (const value of values) {
641
+ conditions.push(`${column('object')} != ?`);
642
+ params.push(value);
643
+ }
644
+ }
645
+ if (ops.$startsWith !== undefined) {
646
+ const fields = this.objectFieldsForPrefix(ops.$startsWith, predicate);
647
+ this.assertComparableObject(fields, '$startsWith');
648
+ conditions.push(`${column('object_kind')} = ?`);
649
+ params.push(fields.objectKind);
650
+ conditions.push(`${column('object_key')} >= ? AND ${column('object_key')} < ?`);
651
+ params.push(ops.$startsWith, ops.$startsWith + '\uffff');
652
+ }
653
+ if (ops.$endsWith !== undefined) {
654
+ this.addObjectTextCondition(conditions, params, column, 'LIKE', `%${ops.$endsWith}`, predicate);
655
+ }
656
+ if (ops.$contains !== undefined) {
657
+ this.addObjectTextCondition(conditions, params, column, 'LIKE', `%${ops.$contains}%`, predicate);
658
+ }
659
+ if (ops.$regex !== undefined) {
660
+ const pattern = ops.$regex.replace(/\.\*/g, '%').replace(/\./g, '_');
661
+ this.addObjectTextCondition(conditions, params, column, 'LIKE', pattern, predicate);
662
+ }
663
+ if (ops.$isNull === true) {
664
+ conditions.push(`${column('object')} IS NULL`);
665
+ }
666
+ if (ops.$isNull === false) {
667
+ conditions.push(`${column('object')} IS NOT NULL`);
668
+ }
669
+ }
670
+ addObjectExactCondition(conditions, params, column, object, predicate) {
671
+ const serialized = (0, serialization_1.serializeObject)(object);
672
+ const fields = this.objectFieldsForTerm(object, predicate);
673
+ this.addObjectExactSerializedCondition(conditions, params, column, serialized, fields);
674
+ }
675
+ addObjectExactValueCondition(conditions, params, column, value, op, predicate) {
676
+ const item = this.objectPredicateForOperatorValue(value, op, predicate);
677
+ if (op === '$ne') {
678
+ conditions.push(`${column('object')} != ?`);
679
+ params.push(item.serialized);
680
+ return;
681
+ }
682
+ this.addObjectExactSerializedCondition(conditions, params, column, item.serialized, item.fields);
683
+ }
684
+ addObjectExactSerializedCondition(conditions, params, column, serialized, fields) {
685
+ if (fields.objectKey !== null) {
686
+ conditions.push(`${column('object_kind')} = ?`);
687
+ params.push(fields.objectKind);
688
+ conditions.push(`${column('object_key')} = ?`);
689
+ params.push(fields.objectKey);
690
+ return;
691
+ }
692
+ conditions.push(`${column('object_digest')} = ?`);
693
+ params.push(this.objectDigestForIndex(serialized, fields));
694
+ conditions.push(`${column('object')} = ?`);
695
+ params.push(serialized);
696
+ }
697
+ addObjectComparableCondition(conditions, params, column, sqlOperator, value, op, predicate) {
698
+ const item = this.objectPredicateForOperatorValue(value, op, predicate);
699
+ this.assertComparableObject(item.fields, op);
700
+ conditions.push(`${column('object_kind')} = ?`);
701
+ params.push(item.fields.objectKind);
702
+ conditions.push(`${column('object_key')} ${sqlOperator} ?`);
703
+ params.push(item.serialized);
704
+ }
705
+ addObjectTextCondition(conditions, params, column, sqlOperator, value, predicate) {
706
+ const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
707
+ if (declaredType) {
708
+ if (!['text', 'longText', 'literal'].includes(declaredType)) {
709
+ throw new Error(`Object text search is not supported for ${declaredType}`);
710
+ }
711
+ conditions.push(`${column('object_kind')} = ?`);
712
+ params.push(declaredType);
713
+ }
714
+ conditions.push(`${column('object_text')} ${sqlOperator} ?`);
715
+ params.push(value);
716
+ }
717
+ objectPredicateForOperatorValue(value, op, predicate) {
718
+ const serialized = this.serializeOpValue(value, true, op);
719
+ return {
720
+ serialized,
721
+ fields: this.objectFieldsForSerialized(serialized, predicate),
722
+ };
723
+ }
724
+ objectFieldsForTerm(object, predicate) {
725
+ return (0, value_types_1.objectIndexFieldsFromTerm)(object, {
726
+ predicate,
727
+ predicateObjectDataTypes: this.options.predicateObjectDataTypes,
728
+ textMaxBytes: this.options.textMaxBytes,
729
+ });
730
+ }
731
+ objectFieldsForSerialized(serialized, predicate) {
732
+ return (0, value_types_1.objectIndexFieldsFromSerialized)(serialized, {
733
+ predicate,
734
+ predicateObjectDataTypes: this.options.predicateObjectDataTypes,
735
+ textMaxBytes: this.options.textMaxBytes,
736
+ });
737
+ }
738
+ objectFieldsForPrefix(prefix, predicate) {
739
+ const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
740
+ if (declaredType) {
741
+ if (declaredType === 'longText') {
742
+ return { objectKind: 'longText', objectKey: null, objectText: prefix };
743
+ }
744
+ return { objectKind: declaredType, objectKey: prefix, objectText: null };
745
+ }
746
+ if ((0, serialization_1.isSerializedNumericLiteral)(prefix)) {
747
+ return { objectKind: 'numeric', objectKey: prefix, objectText: null };
748
+ }
749
+ if ((0, serialization_1.isSerializedDateTimeLiteral)(prefix)) {
750
+ return { objectKind: 'dateTime', objectKey: prefix, objectText: null };
751
+ }
752
+ if (prefix.startsWith('"')) {
753
+ return { objectKind: 'text', objectKey: prefix, objectText: null };
754
+ }
755
+ if (prefix.startsWith('_:')) {
756
+ return { objectKind: 'blankNode', objectKey: prefix, objectText: null };
757
+ }
758
+ return { objectKind: 'iri', objectKey: prefix, objectText: null };
759
+ }
760
+ assertComparableObject(fields, op) {
761
+ if (fields.objectKey !== null && fields.objectKind !== 'longText') {
762
+ return;
763
+ }
764
+ throw new Error(`Object ${op} is not supported for ${fields.objectKind}; declare/use a comparable data type instead of longText`);
765
+ }
766
+ addAliasedObjectConditions(conditions, params, alias, pattern) {
767
+ if (!pattern.object)
768
+ return;
769
+ this.addObjectConditions(conditions, params, alias, pattern.object, this.extractExactPredicate(pattern.predicate));
770
+ }
771
+ extractExactPredicate(match) {
772
+ if (!match)
773
+ return undefined;
774
+ if (typeof match === 'object' && 'termType' in match) {
775
+ return (0, serialization_1.termToId)(match);
776
+ }
777
+ const ops = match;
778
+ if (ops.$eq !== undefined) {
779
+ return String(this.serializeOpValue(ops.$eq, false, '$eq'));
780
+ }
781
+ return undefined;
782
+ }
783
+ resolveObjectDataTypeForPattern(pattern) {
784
+ const predicate = this.extractExactPredicate(pattern.predicate);
785
+ if (predicate) {
786
+ return (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
787
+ }
788
+ if (pattern.object && typeof pattern.object === 'object' && 'termType' in pattern.object) {
789
+ return this.objectFieldsForTerm(pattern.object, predicate).objectKind;
790
+ }
791
+ return undefined;
792
+ }
793
+ addAliasedPgCondition(conditions, params, alias, column, match, isObject) {
794
+ if (!match)
795
+ return;
796
+ if (isObject) {
797
+ this.addObjectConditions(conditions, params, alias, match, undefined);
798
+ return;
799
+ }
800
+ if (typeof match === 'object' && 'termType' in match) {
801
+ const value = (0, serialization_1.termToId)(match);
802
+ conditions.push(`${alias}.${column} = ?`);
803
+ params.push(value);
804
+ return;
805
+ }
806
+ if (match.$eq !== undefined) {
807
+ const value = this.serializeOpValue(match.$eq, false, '$eq');
808
+ conditions.push(`${alias}.${column} = ?`);
809
+ params.push(value);
810
+ return;
811
+ }
812
+ this.addFallbackAliasedCondition(conditions, params, alias, column, match, isObject);
813
+ }
814
+ addFallbackAliasedCondition(conditions, params, alias, column, match, isObject) {
815
+ if (isObject) {
816
+ this.addObjectConditions(conditions, params, alias, match, undefined);
817
+ return;
818
+ }
819
+ if (match.$gt !== undefined) {
820
+ conditions.push(`${alias}.${column} > ?`);
821
+ params.push(this.serializeOpValue(match.$gt, false, '$gt'));
822
+ }
823
+ if (match.$gte !== undefined) {
824
+ conditions.push(`${alias}.${column} >= ?`);
825
+ params.push(this.serializeOpValue(match.$gte, false, '$gte'));
826
+ }
827
+ if (match.$lt !== undefined) {
828
+ conditions.push(`${alias}.${column} < ?`);
829
+ params.push(this.serializeOpValue(match.$lt, false, '$lt'));
830
+ }
831
+ if (match.$lte !== undefined) {
832
+ conditions.push(`${alias}.${column} <= ?`);
833
+ params.push(this.serializeOpValue(match.$lte, false, '$lte'));
834
+ }
835
+ if (match.$in !== undefined && match.$in.length > 0) {
836
+ const placeholders = match.$in.map(() => '?').join(', ');
837
+ conditions.push(`${alias}.${column} IN (${placeholders})`);
838
+ params.push(...match.$in.map((value) => this.serializeOpValue(value, false, '$in')));
839
+ }
272
840
  }
273
841
  }
274
842
  exports.PgQuintStore = PgQuintStore;