@undefineds.co/xpod 0.3.14 → 0.3.16
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/config/local.json +5 -5
- package/config/xpod.json +24 -10
- package/dist/cli/commands/auth.d.ts +1 -0
- package/dist/cli/commands/auth.js +117 -37
- package/dist/cli/commands/auth.js.map +1 -1
- package/dist/cli/commands/login.js +16 -23
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/logs.d.ts +2 -0
- package/dist/cli/commands/logs.js +20 -5
- package/dist/cli/commands/logs.js.map +1 -1
- package/dist/cli/commands/obj.d.ts +44 -0
- package/dist/cli/commands/obj.js +1059 -0
- package/dist/cli/commands/obj.js.map +1 -0
- package/dist/cli/commands/rdf.d.ts +14 -0
- package/dist/cli/commands/rdf.js +235 -0
- package/dist/cli/commands/rdf.js.map +1 -0
- package/dist/cli/commands/resource.d.ts +31 -0
- package/dist/cli/commands/resource.js +191 -0
- package/dist/cli/commands/resource.js.map +1 -0
- package/dist/cli/commands/secret.d.ts +36 -0
- package/dist/cli/commands/secret.js +285 -0
- package/dist/cli/commands/secret.js.map +1 -0
- package/dist/cli/commands/server.d.ts +11 -0
- package/dist/cli/commands/server.js +168 -0
- package/dist/cli/commands/server.js.map +1 -0
- package/dist/cli/commands/start.d.ts +1 -0
- package/dist/cli/commands/start.js +5 -0
- package/dist/cli/commands/start.js.map +1 -1
- package/dist/cli/commands/status.d.ts +1 -0
- package/dist/cli/commands/status.js +21 -6
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/stop.d.ts +3 -0
- package/dist/cli/commands/stop.js +40 -6
- package/dist/cli/commands/stop.js.map +1 -1
- package/dist/cli/index.js +23 -8
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/lib/auth-context.d.ts +24 -0
- package/dist/cli/lib/auth-context.js +70 -0
- package/dist/cli/lib/auth-context.js.map +1 -0
- package/dist/cli/lib/output.d.ts +23 -0
- package/dist/cli/lib/output.js +63 -0
- package/dist/cli/lib/output.js.map +1 -0
- package/dist/cli/lib/resource.d.ts +29 -0
- package/dist/cli/lib/resource.js +114 -0
- package/dist/cli/lib/resource.js.map +1 -0
- package/dist/components/context.jsonld +6 -0
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.d.ts +11 -10
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js +13 -24
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectIdentityProviderHandler.jsonld +4 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.d.ts +8 -4
- package/dist/identity/oidc/AutoDetectOidcHandler.js +10 -6
- package/dist/identity/oidc/AutoDetectOidcHandler.js.map +1 -1
- package/dist/identity/oidc/AutoDetectOidcHandler.jsonld +3 -3
- package/dist/storage/accessors/MixDataAccessor.js +3 -0
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.d.ts +26 -1
- package/dist/storage/quint/SqliteQuintStore.js +551 -318
- package/dist/storage/quint/SqliteQuintStore.js.map +1 -1
- package/dist/storage/quint/SqliteQuintStore.jsonld +102 -2
- package/dist/storage/quint/schema.d.ts +76 -0
- package/dist/storage/quint/schema.js +13 -7
- package/dist/storage/quint/schema.js.map +1 -1
- package/dist/storage/rdf/RdfLocalQueryEngine.d.ts +4 -1
- package/dist/storage/rdf/RdfLocalQueryEngine.js +77 -8
- package/dist/storage/rdf/RdfLocalQueryEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.d.ts +5 -0
- package/dist/storage/rdf/SolidRdfEngine.js +31 -3
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.jsonld +34 -0
- package/dist/storage/sparql/ComunicaQuintEngine.js +16 -3
- package/dist/storage/sparql/ComunicaQuintEngine.js.map +1 -1
- package/package.json +1 -1
- package/dist/cli/commands/config.d.ts +0 -42
- package/dist/cli/commands/config.js +0 -289
- package/dist/cli/commands/config.js.map +0 -1
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SqliteQuintStore = void 0;
|
|
7
|
+
const node_crypto_1 = require("node:crypto");
|
|
7
8
|
const node_fs_1 = require("node:fs");
|
|
8
9
|
const node_path_1 = require("node:path");
|
|
9
10
|
const drizzle_orm_1 = require("drizzle-orm");
|
|
@@ -13,7 +14,11 @@ const schema_1 = require("./schema");
|
|
|
13
14
|
const serialization_1 = require("./serialization");
|
|
14
15
|
const SqliteRuntime_1 = require("../SqliteRuntime");
|
|
15
16
|
const types_1 = require("./types");
|
|
17
|
+
const value_types_1 = require("./value-types");
|
|
16
18
|
const SQLITE_UNBOUNDED_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
19
|
+
function digestObject(value) {
|
|
20
|
+
return (0, node_crypto_1.createHash)('sha256').update(value).digest('hex');
|
|
21
|
+
}
|
|
17
22
|
class SqliteQuintStore {
|
|
18
23
|
constructor(options) {
|
|
19
24
|
this.sqlite = null;
|
|
@@ -47,20 +52,34 @@ class SqliteQuintStore {
|
|
|
47
52
|
// Create table and indexes
|
|
48
53
|
this.sqlite.exec(`
|
|
49
54
|
CREATE TABLE IF NOT EXISTS quints (
|
|
55
|
+
object_kind TEXT,
|
|
56
|
+
object_key TEXT,
|
|
57
|
+
object_text TEXT,
|
|
58
|
+
object_digest TEXT,
|
|
50
59
|
graph TEXT NOT NULL,
|
|
51
60
|
subject TEXT NOT NULL,
|
|
52
61
|
predicate TEXT NOT NULL,
|
|
53
62
|
object TEXT NOT NULL,
|
|
54
|
-
vector TEXT
|
|
55
|
-
PRIMARY KEY (graph, subject, predicate, object)
|
|
63
|
+
vector TEXT
|
|
56
64
|
);
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
CREATE INDEX IF NOT EXISTS
|
|
61
|
-
CREATE INDEX IF NOT EXISTS
|
|
62
|
-
CREATE INDEX IF NOT EXISTS
|
|
63
|
-
CREATE INDEX IF NOT EXISTS
|
|
65
|
+
`);
|
|
66
|
+
this.ensureTypedObjectSchema();
|
|
67
|
+
this.sqlite.exec(`
|
|
68
|
+
CREATE INDEX IF NOT EXISTS idx_quints_graph ON quints (graph);
|
|
69
|
+
CREATE INDEX IF NOT EXISTS idx_quints_subject ON quints (subject);
|
|
70
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate ON quints (predicate);
|
|
71
|
+
CREATE INDEX IF NOT EXISTS idx_quints_object_key ON quints (object_kind, object_key);
|
|
72
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_key ON quints (predicate, object_kind, object_key);
|
|
73
|
+
CREATE INDEX IF NOT EXISTS idx_quints_predicate_object_digest ON quints (predicate, object_kind, object_digest);
|
|
74
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_key
|
|
75
|
+
ON quints (graph, subject, predicate, object_kind, object_key)
|
|
76
|
+
WHERE object_key IS NOT NULL;
|
|
77
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_quints_gspo_digest
|
|
78
|
+
ON quints (graph, subject, predicate, object_kind, object_digest)
|
|
79
|
+
WHERE object_digest IS NOT NULL;
|
|
80
|
+
CREATE INDEX IF NOT EXISTS idx_quints_gsp ON quints (graph, subject, predicate);
|
|
81
|
+
CREATE INDEX IF NOT EXISTS idx_quints_sp ON quints (subject, predicate);
|
|
82
|
+
CREATE INDEX IF NOT EXISTS idx_quints_gp ON quints (graph, predicate);
|
|
64
83
|
`);
|
|
65
84
|
}
|
|
66
85
|
async close() {
|
|
@@ -75,29 +94,8 @@ class SqliteQuintStore {
|
|
|
75
94
|
// ============================================
|
|
76
95
|
async get(pattern, options) {
|
|
77
96
|
this.ensureOpen();
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
if (conditions.length > 0) {
|
|
81
|
-
query = query.where((0, drizzle_orm_1.and)(...conditions));
|
|
82
|
-
}
|
|
83
|
-
// ORDER BY 支持
|
|
84
|
-
if (options?.order && options.order.length > 0) {
|
|
85
|
-
// 使用 sql 模板构建 ORDER BY
|
|
86
|
-
const orderCol = options.order[0]; // 目前只支持单列排序
|
|
87
|
-
const direction = options.reverse ? 'DESC' : 'ASC';
|
|
88
|
-
query = query.orderBy(drizzle_orm_1.sql.raw(`${orderCol} ${direction}`));
|
|
89
|
-
}
|
|
90
|
-
if (options?.limit !== undefined) {
|
|
91
|
-
query = query.limit(options.limit);
|
|
92
|
-
}
|
|
93
|
-
if (options?.offset !== undefined) {
|
|
94
|
-
// SQLite requires LIMIT when using OFFSET
|
|
95
|
-
if (options?.limit === undefined) {
|
|
96
|
-
query = query.limit(SQLITE_UNBOUNDED_LIMIT);
|
|
97
|
-
}
|
|
98
|
-
query = query.offset(options.offset);
|
|
99
|
-
}
|
|
100
|
-
const rows = await query;
|
|
97
|
+
const { sql: query, params } = this.buildSelectQuery(pattern, options);
|
|
98
|
+
const rows = this.sqlite.prepare(query).all(...params);
|
|
101
99
|
return rows.map((row) => this.rowToQuint(row));
|
|
102
100
|
}
|
|
103
101
|
match(subject, predicate, object, graph) {
|
|
@@ -117,13 +115,9 @@ class SqliteQuintStore {
|
|
|
117
115
|
}
|
|
118
116
|
async count(pattern) {
|
|
119
117
|
this.ensureOpen();
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
query = query.where((0, drizzle_orm_1.and)(...conditions));
|
|
124
|
-
}
|
|
125
|
-
const result = await query;
|
|
126
|
-
return result[0]?.count ?? 0;
|
|
118
|
+
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
119
|
+
const result = this.sqlite.prepare(`SELECT COUNT(*) AS count FROM quints${whereClause}`).get(...params);
|
|
120
|
+
return Number(result?.count ?? 0);
|
|
127
121
|
}
|
|
128
122
|
/**
|
|
129
123
|
* Compound query - multiple patterns JOINed by a common field
|
|
@@ -284,137 +278,12 @@ class SqliteQuintStore {
|
|
|
284
278
|
*/
|
|
285
279
|
buildConditionsForAlias(pattern, alias, params) {
|
|
286
280
|
const conditions = [];
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
*/
|
|
294
|
-
const serializeOpValue = (value, isObject, filterOp) => {
|
|
295
|
-
if (typeof value === 'object' && 'termType' in value) {
|
|
296
|
-
// It's a Term - use full serialization
|
|
297
|
-
return isObject ? (0, serialization_1.serializeObject)(value) : (0, serialization_1.termToId)(value);
|
|
298
|
-
}
|
|
299
|
-
if (typeof value === 'number') {
|
|
300
|
-
if (isObject) {
|
|
301
|
-
// For exact match operations, use full serialization (includes datatype and original value)
|
|
302
|
-
if (filterOp === '$eq' || filterOp === '$ne' || filterOp === '$in' || filterOp === '$notIn') {
|
|
303
|
-
const lit = n3_1.DataFactory.literal(String(value), n3_1.DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#integer'));
|
|
304
|
-
return (0, serialization_1.serializeObject)(lit);
|
|
305
|
-
}
|
|
306
|
-
// For range comparisons, use fpstring
|
|
307
|
-
const fpValue = `N${serialization_1.SEP}${(0, serialization_1.fpEncode)(value)}`;
|
|
308
|
-
// $gt and $lte need max suffix to compare correctly
|
|
309
|
-
if (filterOp === '$gt' || filterOp === '$lte') {
|
|
310
|
-
return fpValue + serialization_1.SEP + '\uffff';
|
|
311
|
-
}
|
|
312
|
-
// $lt and $gte use prefix only
|
|
313
|
-
return fpValue;
|
|
314
|
-
}
|
|
315
|
-
return value;
|
|
316
|
-
}
|
|
317
|
-
if (isObject) {
|
|
318
|
-
if ((0, serialization_1.isSerializedObjectValue)(value)) {
|
|
319
|
-
return value;
|
|
320
|
-
}
|
|
321
|
-
return `"${value}"`;
|
|
322
|
-
}
|
|
323
|
-
return value;
|
|
324
|
-
};
|
|
325
|
-
const addCondition = (column, match, isObject = false) => {
|
|
326
|
-
if (!match)
|
|
327
|
-
return;
|
|
328
|
-
const fullColumn = `${alias}.${column}`;
|
|
329
|
-
if ((0, types_1.isTerm)(match)) {
|
|
330
|
-
conditions.push(`${fullColumn} = ?`);
|
|
331
|
-
params.push(isObject ? (0, serialization_1.serializeObject)(match) : (0, serialization_1.termToId)(match));
|
|
332
|
-
}
|
|
333
|
-
else {
|
|
334
|
-
const ops = match;
|
|
335
|
-
if (ops.$eq !== undefined) {
|
|
336
|
-
conditions.push(`${fullColumn} = ?`);
|
|
337
|
-
params.push(serializeOpValue(ops.$eq, isObject, '$eq'));
|
|
338
|
-
}
|
|
339
|
-
if (ops.$ne !== undefined) {
|
|
340
|
-
conditions.push(`${fullColumn} != ?`);
|
|
341
|
-
params.push(serializeOpValue(ops.$ne, isObject, '$ne'));
|
|
342
|
-
}
|
|
343
|
-
if (ops.$gt !== undefined) {
|
|
344
|
-
conditions.push(`${fullColumn} > ?`);
|
|
345
|
-
params.push(serializeOpValue(ops.$gt, isObject, '$gt'));
|
|
346
|
-
}
|
|
347
|
-
if (ops.$gte !== undefined) {
|
|
348
|
-
conditions.push(`${fullColumn} >= ?`);
|
|
349
|
-
params.push(serializeOpValue(ops.$gte, isObject, '$gte'));
|
|
350
|
-
}
|
|
351
|
-
if (ops.$lt !== undefined) {
|
|
352
|
-
conditions.push(`${fullColumn} < ?`);
|
|
353
|
-
params.push(serializeOpValue(ops.$lt, isObject, '$lt'));
|
|
354
|
-
}
|
|
355
|
-
if (ops.$lte !== undefined) {
|
|
356
|
-
conditions.push(`${fullColumn} <= ?`);
|
|
357
|
-
params.push(serializeOpValue(ops.$lte, isObject, '$lte'));
|
|
358
|
-
}
|
|
359
|
-
if (ops.$in !== undefined && ops.$in.length > 0) {
|
|
360
|
-
const placeholders = ops.$in.map(() => '?').join(', ');
|
|
361
|
-
conditions.push(`${fullColumn} IN (${placeholders})`);
|
|
362
|
-
params.push(...ops.$in.map(v => serializeOpValue(v, isObject, '$in')));
|
|
363
|
-
}
|
|
364
|
-
if (ops.$notIn !== undefined && ops.$notIn.length > 0) {
|
|
365
|
-
const placeholders = ops.$notIn.map(() => '?').join(', ');
|
|
366
|
-
conditions.push(`${fullColumn} NOT IN (${placeholders})`);
|
|
367
|
-
params.push(...ops.$notIn.map(v => serializeOpValue(v, isObject, '$notIn')));
|
|
368
|
-
}
|
|
369
|
-
if (ops.$startsWith !== undefined) {
|
|
370
|
-
conditions.push(`${fullColumn} >= ?`);
|
|
371
|
-
conditions.push(`${fullColumn} < ?`);
|
|
372
|
-
params.push(ops.$startsWith);
|
|
373
|
-
params.push(ops.$startsWith + '\uffff');
|
|
374
|
-
}
|
|
375
|
-
if (ops.$endsWith !== undefined) {
|
|
376
|
-
conditions.push(`${fullColumn} LIKE ?`);
|
|
377
|
-
params.push(`%${ops.$endsWith}`);
|
|
378
|
-
}
|
|
379
|
-
if (ops.$contains !== undefined) {
|
|
380
|
-
conditions.push(`${fullColumn} LIKE ?`);
|
|
381
|
-
params.push(`%${ops.$contains}%`);
|
|
382
|
-
}
|
|
383
|
-
if (isObject && ops.$strStartsWith !== undefined) {
|
|
384
|
-
const lexical = this.objectLexicalSql(fullColumn);
|
|
385
|
-
conditions.push(`${lexical} >= ?`);
|
|
386
|
-
conditions.push(`${lexical} < ?`);
|
|
387
|
-
params.push(ops.$strStartsWith);
|
|
388
|
-
params.push(ops.$strStartsWith + '\uffff');
|
|
389
|
-
}
|
|
390
|
-
if (isObject && ops.$strEndsWith !== undefined) {
|
|
391
|
-
conditions.push(`${this.objectLexicalSql(fullColumn)} LIKE ?`);
|
|
392
|
-
params.push(`%${ops.$strEndsWith}`);
|
|
393
|
-
}
|
|
394
|
-
if (isObject && ops.$strContains !== undefined) {
|
|
395
|
-
conditions.push(`${this.objectLexicalSql(fullColumn)} LIKE ?`);
|
|
396
|
-
params.push(`%${ops.$strContains}%`);
|
|
397
|
-
}
|
|
398
|
-
if (isObject && ops.$strRegex !== undefined) {
|
|
399
|
-
conditions.push(`${this.objectLexicalSql(fullColumn)} GLOB ?`);
|
|
400
|
-
params.push(ops.$strRegex.replace(/\.\*/g, '*').replace(/\./g, '?'));
|
|
401
|
-
}
|
|
402
|
-
if (isObject && ops.$language !== undefined) {
|
|
403
|
-
conditions.push(`lower(${fullColumn}) LIKE ?`);
|
|
404
|
-
params.push(ops.$language === '*' ? '%"@%' : `%"@${ops.$language.toLowerCase()}`);
|
|
405
|
-
}
|
|
406
|
-
if (ops.$isNull === true) {
|
|
407
|
-
conditions.push(`${fullColumn} IS NULL`);
|
|
408
|
-
}
|
|
409
|
-
if (ops.$isNull === false) {
|
|
410
|
-
conditions.push(`${fullColumn} IS NOT NULL`);
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
addCondition('graph', pattern.graph);
|
|
415
|
-
addCondition('subject', pattern.subject);
|
|
416
|
-
addCondition('predicate', pattern.predicate);
|
|
417
|
-
addCondition('object', pattern.object, true);
|
|
281
|
+
this.addTermConditions(conditions, params, `${alias}.graph`, pattern.graph, false);
|
|
282
|
+
this.addTermConditions(conditions, params, `${alias}.subject`, pattern.subject, false);
|
|
283
|
+
this.addTermConditions(conditions, params, `${alias}.predicate`, pattern.predicate, false);
|
|
284
|
+
if (pattern.object) {
|
|
285
|
+
this.addObjectConditions(conditions, params, alias, pattern.object, this.extractExactPredicate(pattern.predicate));
|
|
286
|
+
}
|
|
418
287
|
return conditions;
|
|
419
288
|
}
|
|
420
289
|
// ============================================
|
|
@@ -423,43 +292,26 @@ class SqliteQuintStore {
|
|
|
423
292
|
async put(quint) {
|
|
424
293
|
this.ensureOpen();
|
|
425
294
|
const row = this.quintToRow(quint);
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
.onConflictDoUpdate({
|
|
429
|
-
target: [schema_1.quints.graph, schema_1.quints.subject, schema_1.quints.predicate, schema_1.quints.object],
|
|
430
|
-
set: { vector: row.vector },
|
|
431
|
-
});
|
|
295
|
+
const statement = this.writeStatementForRow(row);
|
|
296
|
+
this.sqlite.prepare(statement.sql).run(...statement.params);
|
|
432
297
|
}
|
|
433
298
|
async multiPut(quintList) {
|
|
434
299
|
this.ensureOpen();
|
|
435
300
|
if (quintList.length === 0)
|
|
436
301
|
return;
|
|
437
|
-
const rows = quintList.map(q => this.quintToRow(q));
|
|
302
|
+
const rows = quintList.map(q => this.writeStatementForRow(this.quintToRow(q)));
|
|
438
303
|
// Use transaction for batch insert
|
|
439
304
|
this.sqlite.transaction(() => {
|
|
440
305
|
for (const row of rows) {
|
|
441
|
-
this.
|
|
442
|
-
.values(row)
|
|
443
|
-
.onConflictDoUpdate({
|
|
444
|
-
target: [schema_1.quints.graph, schema_1.quints.subject, schema_1.quints.predicate, schema_1.quints.object],
|
|
445
|
-
set: { vector: row.vector },
|
|
446
|
-
})
|
|
447
|
-
.run();
|
|
306
|
+
this.sqlite.prepare(row.sql).run(...row.params);
|
|
448
307
|
}
|
|
449
308
|
})();
|
|
450
309
|
}
|
|
451
310
|
async updateEmbedding(pattern, embedding) {
|
|
452
311
|
this.ensureOpen();
|
|
453
|
-
const
|
|
312
|
+
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
454
313
|
const vectorJson = JSON.stringify(embedding);
|
|
455
|
-
|
|
456
|
-
const result = await this.db.update(schema_1.quints)
|
|
457
|
-
.set({ vector: vectorJson });
|
|
458
|
-
return result.changes;
|
|
459
|
-
}
|
|
460
|
-
const result = await this.db.update(schema_1.quints)
|
|
461
|
-
.set({ vector: vectorJson })
|
|
462
|
-
.where((0, drizzle_orm_1.and)(...conditions));
|
|
314
|
+
const result = this.sqlite.prepare(`UPDATE quints SET vector = ?${whereClause}`).run(vectorJson, ...params);
|
|
463
315
|
return result.changes;
|
|
464
316
|
}
|
|
465
317
|
// ============================================
|
|
@@ -467,13 +319,8 @@ class SqliteQuintStore {
|
|
|
467
319
|
// ============================================
|
|
468
320
|
async del(pattern) {
|
|
469
321
|
this.ensureOpen();
|
|
470
|
-
const
|
|
471
|
-
|
|
472
|
-
// Delete all - dangerous!
|
|
473
|
-
const result = await this.db.delete(schema_1.quints);
|
|
474
|
-
return result.changes;
|
|
475
|
-
}
|
|
476
|
-
const result = await this.db.delete(schema_1.quints).where((0, drizzle_orm_1.and)(...conditions));
|
|
322
|
+
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
323
|
+
const result = this.sqlite.prepare(`DELETE FROM quints${whereClause}`).run(...params);
|
|
477
324
|
return result.changes;
|
|
478
325
|
}
|
|
479
326
|
async multiDel(quintList) {
|
|
@@ -486,9 +333,13 @@ class SqliteQuintStore {
|
|
|
486
333
|
const s = (0, serialization_1.termToId)(quint.subject);
|
|
487
334
|
const p = (0, serialization_1.termToId)(quint.predicate);
|
|
488
335
|
const o = (0, serialization_1.serializeObject)(quint.object);
|
|
489
|
-
this.
|
|
490
|
-
|
|
491
|
-
|
|
336
|
+
this.sqlite.prepare(`
|
|
337
|
+
DELETE FROM quints
|
|
338
|
+
WHERE graph = ?
|
|
339
|
+
AND subject = ?
|
|
340
|
+
AND predicate = ?
|
|
341
|
+
AND object = ?
|
|
342
|
+
`).run(g, s, p, o);
|
|
492
343
|
}
|
|
493
344
|
})();
|
|
494
345
|
}
|
|
@@ -608,142 +459,524 @@ class SqliteQuintStore {
|
|
|
608
459
|
throw new Error('Store not open. Call open() first.');
|
|
609
460
|
}
|
|
610
461
|
}
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
462
|
+
ensureTypedObjectSchema() {
|
|
463
|
+
this.addColumnIfMissing('object_kind', 'TEXT');
|
|
464
|
+
this.addColumnIfMissing('object_key', 'TEXT');
|
|
465
|
+
this.addColumnIfMissing('object_text', 'TEXT');
|
|
466
|
+
this.addColumnIfMissing('object_digest', 'TEXT');
|
|
467
|
+
for (const indexName of ['idx_spog', 'idx_ogsp', 'idx_gspo', 'idx_sopg', 'idx_pogs', 'idx_gpos']) {
|
|
468
|
+
this.sqlite.exec(`DROP INDEX IF EXISTS ${indexName}`);
|
|
469
|
+
}
|
|
470
|
+
this.backfillMissingObjectIndexFields();
|
|
471
|
+
this.sqlite.prepare(`
|
|
472
|
+
UPDATE quints
|
|
473
|
+
SET object_kind = 'text'
|
|
474
|
+
WHERE object_kind = 'shortText'
|
|
475
|
+
`).run();
|
|
476
|
+
}
|
|
477
|
+
addColumnIfMissing(name, definition) {
|
|
478
|
+
const columns = new Set(this.sqlite.prepare('PRAGMA table_info(quints)').all().map(row => row.name));
|
|
479
|
+
if (!columns.has(name)) {
|
|
480
|
+
this.sqlite.exec(`ALTER TABLE quints ADD COLUMN ${name} ${definition}`);
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
backfillMissingObjectIndexFields() {
|
|
484
|
+
const rows = this.sqlite.prepare(`
|
|
485
|
+
SELECT graph, subject, predicate, object
|
|
486
|
+
FROM quints
|
|
487
|
+
WHERE object_kind IS NULL
|
|
488
|
+
OR (object_key IS NULL AND object_digest IS NULL)
|
|
489
|
+
`).all();
|
|
490
|
+
const update = this.sqlite.prepare(`
|
|
491
|
+
UPDATE quints
|
|
492
|
+
SET object_kind = ?,
|
|
493
|
+
object_key = ?,
|
|
494
|
+
object_text = ?,
|
|
495
|
+
object_digest = ?
|
|
496
|
+
WHERE graph = ?
|
|
497
|
+
AND subject = ?
|
|
498
|
+
AND predicate = ?
|
|
499
|
+
AND object = ?
|
|
500
|
+
`);
|
|
501
|
+
for (const row of rows) {
|
|
502
|
+
const objectIndex = this.objectIndexForSerialized(row.predicate, row.object);
|
|
503
|
+
update.run(objectIndex.objectKind, objectIndex.objectKey, objectIndex.objectText, this.objectDigestForIndex(row.object, objectIndex), row.graph, row.subject, row.predicate, row.object);
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
buildSelectQuery(pattern, options) {
|
|
507
|
+
const { whereClause, params } = this.buildWhereClause(pattern);
|
|
508
|
+
let query = `SELECT * FROM quints${whereClause}`;
|
|
509
|
+
if (options?.order && options.order.length > 0) {
|
|
510
|
+
const orderCols = options.order.map(field => {
|
|
511
|
+
if (field === 'object') {
|
|
512
|
+
const objectType = this.resolveObjectDataTypeForPattern(pattern);
|
|
513
|
+
if (objectType === 'longText') {
|
|
514
|
+
throw new Error('ORDER BY object is not supported for longText predicates');
|
|
636
515
|
}
|
|
637
|
-
return
|
|
516
|
+
return 'object_key';
|
|
638
517
|
}
|
|
639
|
-
return
|
|
518
|
+
return field;
|
|
519
|
+
}).join(', ');
|
|
520
|
+
query += ` ORDER BY ${orderCols}`;
|
|
521
|
+
if (options.reverse) {
|
|
522
|
+
query += ' DESC';
|
|
640
523
|
}
|
|
641
|
-
|
|
642
|
-
|
|
524
|
+
}
|
|
525
|
+
if (options?.limit !== undefined) {
|
|
526
|
+
query += ' LIMIT ?';
|
|
527
|
+
params.push(options.limit);
|
|
528
|
+
}
|
|
529
|
+
if (options?.offset !== undefined) {
|
|
530
|
+
if (options.limit === undefined) {
|
|
531
|
+
query += ' LIMIT ?';
|
|
532
|
+
params.push(SQLITE_UNBOUNDED_LIMIT);
|
|
643
533
|
}
|
|
644
|
-
|
|
534
|
+
query += ' OFFSET ?';
|
|
535
|
+
params.push(options.offset);
|
|
536
|
+
}
|
|
537
|
+
return { sql: query, params };
|
|
538
|
+
}
|
|
539
|
+
buildWhereClause(pattern) {
|
|
540
|
+
const conditions = [];
|
|
541
|
+
const params = [];
|
|
542
|
+
const predicate = this.extractExactPredicate(pattern.predicate);
|
|
543
|
+
this.addTermConditions(conditions, params, 'graph', pattern.graph, false);
|
|
544
|
+
this.addTermConditions(conditions, params, 'subject', pattern.subject, false);
|
|
545
|
+
this.addTermConditions(conditions, params, 'predicate', pattern.predicate, false);
|
|
546
|
+
if (pattern.object) {
|
|
547
|
+
this.addObjectConditions(conditions, params, undefined, pattern.object, predicate);
|
|
548
|
+
}
|
|
549
|
+
return {
|
|
550
|
+
whereClause: conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '',
|
|
551
|
+
params,
|
|
645
552
|
};
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
553
|
+
}
|
|
554
|
+
addTermConditions(conditions, params, column, match, isObject) {
|
|
555
|
+
if (!match)
|
|
556
|
+
return;
|
|
557
|
+
if ((0, types_1.isTerm)(match)) {
|
|
558
|
+
conditions.push(`${column} = ?`);
|
|
559
|
+
params.push(isObject ? (0, serialization_1.serializeObject)(match) : (0, serialization_1.termToId)(match));
|
|
560
|
+
return;
|
|
561
|
+
}
|
|
562
|
+
const ops = match;
|
|
563
|
+
if (ops.$eq !== undefined) {
|
|
564
|
+
conditions.push(`${column} = ?`);
|
|
565
|
+
params.push(this.serializeOpValue(ops.$eq, isObject, '$eq'));
|
|
566
|
+
}
|
|
567
|
+
if (ops.$ne !== undefined) {
|
|
568
|
+
conditions.push(`${column} != ?`);
|
|
569
|
+
params.push(this.serializeOpValue(ops.$ne, isObject, '$ne'));
|
|
570
|
+
}
|
|
571
|
+
if (ops.$gt !== undefined) {
|
|
572
|
+
conditions.push(`${column} > ?`);
|
|
573
|
+
params.push(this.serializeOpValue(ops.$gt, isObject, '$gt'));
|
|
574
|
+
}
|
|
575
|
+
if (ops.$gte !== undefined) {
|
|
576
|
+
conditions.push(`${column} >= ?`);
|
|
577
|
+
params.push(this.serializeOpValue(ops.$gte, isObject, '$gte'));
|
|
578
|
+
}
|
|
579
|
+
if (ops.$lt !== undefined) {
|
|
580
|
+
conditions.push(`${column} < ?`);
|
|
581
|
+
params.push(this.serializeOpValue(ops.$lt, isObject, '$lt'));
|
|
582
|
+
}
|
|
583
|
+
if (ops.$lte !== undefined) {
|
|
584
|
+
conditions.push(`${column} <= ?`);
|
|
585
|
+
params.push(this.serializeOpValue(ops.$lte, isObject, '$lte'));
|
|
586
|
+
}
|
|
587
|
+
if (ops.$in !== undefined && ops.$in.length > 0) {
|
|
588
|
+
const placeholders = ops.$in.map(() => '?').join(', ');
|
|
589
|
+
conditions.push(`${column} IN (${placeholders})`);
|
|
590
|
+
params.push(...ops.$in.map(v => this.serializeOpValue(v, isObject, '$in')));
|
|
591
|
+
}
|
|
592
|
+
if (ops.$notIn !== undefined && ops.$notIn.length > 0) {
|
|
593
|
+
const placeholders = ops.$notIn.map(() => '?').join(', ');
|
|
594
|
+
conditions.push(`${column} NOT IN (${placeholders})`);
|
|
595
|
+
params.push(...ops.$notIn.map(v => this.serializeOpValue(v, isObject, '$notIn')));
|
|
596
|
+
}
|
|
597
|
+
if (ops.$startsWith !== undefined) {
|
|
598
|
+
conditions.push(`${column} >= ? AND ${column} < ?`);
|
|
599
|
+
params.push(ops.$startsWith, ops.$startsWith + '\uffff');
|
|
600
|
+
}
|
|
601
|
+
if (ops.$endsWith !== undefined) {
|
|
602
|
+
conditions.push(`${column} LIKE ?`);
|
|
603
|
+
params.push(`%${ops.$endsWith}`);
|
|
604
|
+
}
|
|
605
|
+
if (ops.$contains !== undefined) {
|
|
606
|
+
conditions.push(`${column} LIKE ?`);
|
|
607
|
+
params.push(`%${ops.$contains}%`);
|
|
608
|
+
}
|
|
609
|
+
if (ops.$regex !== undefined) {
|
|
610
|
+
conditions.push(`${column} GLOB ?`);
|
|
611
|
+
params.push(ops.$regex.replace(/\.\*/g, '*').replace(/\./g, '?'));
|
|
612
|
+
}
|
|
613
|
+
if (ops.$isNull === true) {
|
|
614
|
+
conditions.push(`${column} IS NULL`);
|
|
615
|
+
}
|
|
616
|
+
if (ops.$isNull === false) {
|
|
617
|
+
conditions.push(`${column} IS NOT NULL`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
serializeOpValue(value, isObject, filterOp) {
|
|
621
|
+
if (typeof value === 'object' && 'termType' in value) {
|
|
622
|
+
return isObject ? (0, serialization_1.serializeObject)(value) : (0, serialization_1.termToId)(value);
|
|
623
|
+
}
|
|
624
|
+
if (typeof value === 'number') {
|
|
625
|
+
if (isObject) {
|
|
626
|
+
if (filterOp === '$eq' || filterOp === '$ne' || filterOp === '$in' || filterOp === '$notIn') {
|
|
627
|
+
const lit = n3_1.DataFactory.literal(String(value), n3_1.DataFactory.namedNode('http://www.w3.org/2001/XMLSchema#integer'));
|
|
628
|
+
return (0, serialization_1.serializeObject)(lit);
|
|
707
629
|
}
|
|
708
|
-
|
|
709
|
-
|
|
630
|
+
const fpValue = `N${serialization_1.SEP}${(0, serialization_1.fpEncode)(value)}`;
|
|
631
|
+
if (filterOp === '$gt' || filterOp === '$lte') {
|
|
632
|
+
return fpValue + serialization_1.SEP + '\uffff';
|
|
710
633
|
}
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
634
|
+
return fpValue;
|
|
635
|
+
}
|
|
636
|
+
return value;
|
|
637
|
+
}
|
|
638
|
+
if (isObject && !(0, serialization_1.isSerializedObjectValue)(value)) {
|
|
639
|
+
return `"${value}"`;
|
|
640
|
+
}
|
|
641
|
+
return value;
|
|
642
|
+
}
|
|
643
|
+
addObjectConditions(conditions, params, alias, match, predicate) {
|
|
644
|
+
const column = (name) => alias ? `${alias}.${name}` : name;
|
|
645
|
+
if (typeof match === 'object' && 'termType' in match) {
|
|
646
|
+
this.addObjectExactCondition(conditions, params, column, match, predicate);
|
|
647
|
+
return;
|
|
648
|
+
}
|
|
649
|
+
const ops = match;
|
|
650
|
+
if (ops.$eq !== undefined) {
|
|
651
|
+
this.addObjectExactValueCondition(conditions, params, column, ops.$eq, '$eq', predicate);
|
|
652
|
+
}
|
|
653
|
+
if (ops.$ne !== undefined) {
|
|
654
|
+
this.addObjectExactValueCondition(conditions, params, column, ops.$ne, '$ne', predicate);
|
|
655
|
+
}
|
|
656
|
+
if (ops.$gt !== undefined) {
|
|
657
|
+
this.addObjectComparableCondition(conditions, params, column, '>', ops.$gt, '$gt', predicate);
|
|
658
|
+
}
|
|
659
|
+
if (ops.$gte !== undefined) {
|
|
660
|
+
this.addObjectComparableCondition(conditions, params, column, '>=', ops.$gte, '$gte', predicate);
|
|
661
|
+
}
|
|
662
|
+
if (ops.$lt !== undefined) {
|
|
663
|
+
this.addObjectComparableCondition(conditions, params, column, '<', ops.$lt, '$lt', predicate);
|
|
664
|
+
}
|
|
665
|
+
if (ops.$lte !== undefined) {
|
|
666
|
+
this.addObjectComparableCondition(conditions, params, column, '<=', ops.$lte, '$lte', predicate);
|
|
667
|
+
}
|
|
668
|
+
if (ops.$in !== undefined && ops.$in.length > 0) {
|
|
669
|
+
const predicates = ops.$in.map((value) => this.objectPredicateForOperatorValue(value, '$in', predicate));
|
|
670
|
+
const placeholders = predicates.map((item) => {
|
|
671
|
+
if (item.fields.objectKey === null) {
|
|
672
|
+
return `(${column('object_kind')} = ? AND ${column('object_digest')} = ? AND ${column('object')} = ?)`;
|
|
715
673
|
}
|
|
716
|
-
|
|
717
|
-
|
|
674
|
+
return `(${column('object_kind')} = ? AND ${column('object_key')} = ?)`;
|
|
675
|
+
}).join(' OR ');
|
|
676
|
+
conditions.push(`(${placeholders})`);
|
|
677
|
+
for (const item of predicates) {
|
|
678
|
+
if (item.fields.objectKey === null) {
|
|
679
|
+
params.push(item.fields.objectKind, this.objectDigestForIndex(item.serialized, item.fields), item.serialized);
|
|
718
680
|
}
|
|
719
|
-
|
|
720
|
-
|
|
681
|
+
else {
|
|
682
|
+
params.push(item.fields.objectKind, item.fields.objectKey);
|
|
721
683
|
}
|
|
722
684
|
}
|
|
723
|
-
}
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
685
|
+
}
|
|
686
|
+
if (ops.$notIn !== undefined && ops.$notIn.length > 0) {
|
|
687
|
+
for (const value of ops.$notIn) {
|
|
688
|
+
conditions.push(`${column('object')} != ?`);
|
|
689
|
+
params.push(this.objectPredicateForOperatorValue(value, '$notIn', predicate).serialized);
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
if (ops.$startsWith !== undefined) {
|
|
693
|
+
const fields = this.objectFieldsForPrefix(ops.$startsWith, predicate);
|
|
694
|
+
this.assertComparableObject(fields, '$startsWith');
|
|
695
|
+
conditions.push(`${column('object_kind')} = ?`);
|
|
696
|
+
params.push(fields.objectKind);
|
|
697
|
+
conditions.push(`${column('object_key')} >= ? AND ${column('object_key')} < ?`);
|
|
698
|
+
params.push(ops.$startsWith, ops.$startsWith + '\uffff');
|
|
699
|
+
}
|
|
700
|
+
if (ops.$endsWith !== undefined) {
|
|
701
|
+
this.addObjectTextCondition(conditions, params, column, 'LIKE', `%${ops.$endsWith}`, predicate);
|
|
702
|
+
}
|
|
703
|
+
if (ops.$contains !== undefined) {
|
|
704
|
+
this.addObjectTextCondition(conditions, params, column, 'LIKE', `%${ops.$contains}%`, predicate);
|
|
705
|
+
}
|
|
706
|
+
if (ops.$regex !== undefined) {
|
|
707
|
+
this.addObjectTextCondition(conditions, params, column, 'GLOB', ops.$regex.replace(/\.\*/g, '*').replace(/\./g, '?'), predicate);
|
|
708
|
+
}
|
|
709
|
+
if (ops.$strStartsWith !== undefined) {
|
|
710
|
+
this.addObjectLexicalStringCondition(conditions, params, column, 'startsWith', ops.$strStartsWith);
|
|
711
|
+
}
|
|
712
|
+
if (ops.$strEndsWith !== undefined) {
|
|
713
|
+
this.addObjectLexicalStringCondition(conditions, params, column, 'endsWith', ops.$strEndsWith);
|
|
714
|
+
}
|
|
715
|
+
if (ops.$strContains !== undefined) {
|
|
716
|
+
this.addObjectLexicalStringCondition(conditions, params, column, 'contains', ops.$strContains);
|
|
717
|
+
}
|
|
718
|
+
if (ops.$strRegex !== undefined) {
|
|
719
|
+
this.addObjectLexicalStringCondition(conditions, params, column, 'regex', ops.$strRegex);
|
|
720
|
+
}
|
|
721
|
+
if (ops.$language !== undefined) {
|
|
722
|
+
this.addObjectLanguageCondition(conditions, params, column, ops.$language);
|
|
723
|
+
}
|
|
724
|
+
if (ops.$isNull === true) {
|
|
725
|
+
conditions.push(`${column('object')} IS NULL`);
|
|
726
|
+
}
|
|
727
|
+
if (ops.$isNull === false) {
|
|
728
|
+
conditions.push(`${column('object')} IS NOT NULL`);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
addObjectExactCondition(conditions, params, column, object, predicate) {
|
|
732
|
+
const serialized = (0, serialization_1.serializeObject)(object);
|
|
733
|
+
const fields = this.objectIndexForTerm(this.predicateForIndex(predicate), object);
|
|
734
|
+
this.addObjectExactSerializedCondition(conditions, params, column, serialized, fields);
|
|
735
|
+
}
|
|
736
|
+
addObjectExactValueCondition(conditions, params, column, value, op, predicate) {
|
|
737
|
+
const item = this.objectPredicateForOperatorValue(value, op, predicate);
|
|
738
|
+
if (op === '$ne') {
|
|
739
|
+
conditions.push(`${column('object')} != ?`);
|
|
740
|
+
params.push(item.serialized);
|
|
741
|
+
return;
|
|
742
|
+
}
|
|
743
|
+
this.addObjectExactSerializedCondition(conditions, params, column, item.serialized, item.fields);
|
|
744
|
+
}
|
|
745
|
+
addObjectExactSerializedCondition(conditions, params, column, serialized, fields) {
|
|
746
|
+
if (fields.objectKey !== null) {
|
|
747
|
+
conditions.push(`${column('object_kind')} = ?`);
|
|
748
|
+
params.push(fields.objectKind);
|
|
749
|
+
conditions.push(`${column('object_key')} = ?`);
|
|
750
|
+
params.push(fields.objectKey);
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
conditions.push(`${column('object_kind')} = ?`);
|
|
754
|
+
params.push(fields.objectKind);
|
|
755
|
+
conditions.push(`${column('object_digest')} = ?`);
|
|
756
|
+
params.push(this.objectDigestForIndex(serialized, fields));
|
|
757
|
+
conditions.push(`${column('object')} = ?`);
|
|
758
|
+
params.push(serialized);
|
|
759
|
+
}
|
|
760
|
+
addObjectComparableCondition(conditions, params, column, sqlOperator, value, op, predicate) {
|
|
761
|
+
const item = this.objectPredicateForOperatorValue(value, op, predicate);
|
|
762
|
+
this.assertComparableObject(item.fields, op);
|
|
763
|
+
conditions.push(`${column('object_kind')} = ?`);
|
|
764
|
+
params.push(item.fields.objectKind);
|
|
765
|
+
conditions.push(`${column('object_key')} ${sqlOperator} ?`);
|
|
766
|
+
params.push(item.serialized);
|
|
767
|
+
}
|
|
768
|
+
addObjectTextCondition(conditions, params, column, sqlOperator, value, predicate) {
|
|
769
|
+
const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
770
|
+
if (declaredType) {
|
|
771
|
+
if (!['text', 'longText', 'literal'].includes(declaredType)) {
|
|
772
|
+
throw new Error(`Object text search is not supported for ${declaredType}`);
|
|
773
|
+
}
|
|
774
|
+
conditions.push(`${column('object_kind')} = ?`);
|
|
775
|
+
params.push(declaredType);
|
|
776
|
+
}
|
|
777
|
+
conditions.push(`${column('object_text')} ${sqlOperator} ?`);
|
|
778
|
+
params.push(value);
|
|
779
|
+
}
|
|
780
|
+
addObjectLexicalStringCondition(conditions, params, column, op, value) {
|
|
781
|
+
const lexical = this.objectLexicalSql(column);
|
|
782
|
+
if (op === 'startsWith') {
|
|
783
|
+
conditions.push(`(${lexical} >= ? AND ${lexical} < ?)`);
|
|
784
|
+
params.push(value, value + '\uffff');
|
|
785
|
+
return;
|
|
786
|
+
}
|
|
787
|
+
if (op === 'endsWith') {
|
|
788
|
+
conditions.push(`${lexical} LIKE ?`);
|
|
789
|
+
params.push(`%${value}`);
|
|
790
|
+
return;
|
|
791
|
+
}
|
|
792
|
+
if (op === 'contains') {
|
|
793
|
+
conditions.push(`${lexical} LIKE ?`);
|
|
794
|
+
params.push(`%${value}%`);
|
|
795
|
+
return;
|
|
796
|
+
}
|
|
797
|
+
conditions.push(`${lexical} GLOB ?`);
|
|
798
|
+
params.push(value.replace(/\.\*/g, '*').replace(/\./g, '?'));
|
|
799
|
+
}
|
|
800
|
+
addObjectLanguageCondition(conditions, params, column, lang) {
|
|
801
|
+
const languageLiteralKinds = `${column('object_kind')} IN ('text', 'longText', 'literal')`;
|
|
802
|
+
if (lang === '*') {
|
|
803
|
+
conditions.push(`(${languageLiteralKinds} AND lower(${column('object')}) LIKE ?)`);
|
|
804
|
+
params.push('%"@%');
|
|
805
|
+
return;
|
|
806
|
+
}
|
|
807
|
+
conditions.push(`(${languageLiteralKinds} AND (lower(${column('object')}) LIKE ? OR lower(${column('object')}) LIKE ?))`);
|
|
808
|
+
params.push(`%"@${lang.toLowerCase()}`, `%"@${lang.toLowerCase()}-%`);
|
|
729
809
|
}
|
|
730
810
|
objectLexicalSql(column) {
|
|
811
|
+
const object = column('object');
|
|
731
812
|
return `CASE
|
|
732
|
-
WHEN ${column}
|
|
733
|
-
WHEN ${column}
|
|
734
|
-
WHEN ${column}
|
|
735
|
-
WHEN ${column
|
|
736
|
-
|
|
737
|
-
ELSE ${column}
|
|
813
|
+
WHEN ${column('object_text')} IS NOT NULL THEN ${column('object_text')}
|
|
814
|
+
WHEN ${column('object_kind')} IN ('iri', 'blankNode') THEN ${column('object_key')}
|
|
815
|
+
WHEN ${column('object_kind')} = 'numeric' THEN substr(${object}, length('N${serialization_1.SEP}') + instr(substr(${object}, length('N${serialization_1.SEP}') + 1), '${serialization_1.SEP}') + instr(substr(${object}, length('N${serialization_1.SEP}') + instr(substr(${object}, length('N${serialization_1.SEP}') + 1), '${serialization_1.SEP}') + 1), '${serialization_1.SEP}') + 1)
|
|
816
|
+
WHEN ${column('object_kind')} = 'dateTime' THEN substr(${object}, length('D${serialization_1.SEP}') + instr(substr(${object}, length('D${serialization_1.SEP}') + 1), '${serialization_1.SEP}') + 1)
|
|
817
|
+
ELSE NULL
|
|
738
818
|
END`;
|
|
739
819
|
}
|
|
820
|
+
objectPredicateForOperatorValue(value, op, predicate) {
|
|
821
|
+
const serialized = this.serializeOpValue(value, true, op);
|
|
822
|
+
return {
|
|
823
|
+
serialized,
|
|
824
|
+
fields: this.objectFieldsForOperatorValue(value, serialized, op, predicate),
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
objectFieldsForOperatorValue(value, serialized, op, predicate) {
|
|
828
|
+
const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
829
|
+
if (declaredType) {
|
|
830
|
+
if (declaredType === 'longText') {
|
|
831
|
+
return { objectKind: 'longText', objectKey: null, objectText: String(value) };
|
|
832
|
+
}
|
|
833
|
+
return { objectKind: declaredType, objectKey: serialized, objectText: null };
|
|
834
|
+
}
|
|
835
|
+
if (typeof value === 'number' && !['$eq', '$ne', '$in', '$notIn'].includes(op)) {
|
|
836
|
+
return { objectKind: 'numeric', objectKey: serialized, objectText: null };
|
|
837
|
+
}
|
|
838
|
+
if (!['$eq', '$ne', '$in', '$notIn'].includes(op)) {
|
|
839
|
+
if ((0, serialization_1.isSerializedNumericLiteral)(serialized)) {
|
|
840
|
+
return { objectKind: 'numeric', objectKey: serialized, objectText: null };
|
|
841
|
+
}
|
|
842
|
+
if ((0, serialization_1.isSerializedDateTimeLiteral)(serialized)) {
|
|
843
|
+
return { objectKind: 'dateTime', objectKey: serialized, objectText: null };
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
return this.objectIndexForSerialized(predicate, serialized);
|
|
847
|
+
}
|
|
848
|
+
objectIndexForTerm(predicate, object) {
|
|
849
|
+
return (0, value_types_1.objectIndexFieldsFromTerm)(object, {
|
|
850
|
+
predicate,
|
|
851
|
+
predicateObjectDataTypes: this.options.predicateObjectDataTypes,
|
|
852
|
+
textMaxBytes: this.options.textMaxBytes,
|
|
853
|
+
});
|
|
854
|
+
}
|
|
855
|
+
objectIndexForSerialized(predicate, object) {
|
|
856
|
+
return (0, value_types_1.objectIndexFieldsFromSerialized)(object, {
|
|
857
|
+
predicate,
|
|
858
|
+
predicateObjectDataTypes: this.options.predicateObjectDataTypes,
|
|
859
|
+
textMaxBytes: this.options.textMaxBytes,
|
|
860
|
+
});
|
|
861
|
+
}
|
|
862
|
+
objectFieldsForPrefix(prefix, predicate) {
|
|
863
|
+
const declaredType = (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
864
|
+
if (declaredType) {
|
|
865
|
+
if (declaredType === 'longText') {
|
|
866
|
+
return { objectKind: 'longText', objectKey: null, objectText: prefix };
|
|
867
|
+
}
|
|
868
|
+
return { objectKind: declaredType, objectKey: prefix, objectText: null };
|
|
869
|
+
}
|
|
870
|
+
if ((0, serialization_1.isSerializedNumericLiteral)(prefix)) {
|
|
871
|
+
return { objectKind: 'numeric', objectKey: prefix, objectText: null };
|
|
872
|
+
}
|
|
873
|
+
if ((0, serialization_1.isSerializedDateTimeLiteral)(prefix)) {
|
|
874
|
+
return { objectKind: 'dateTime', objectKey: prefix, objectText: null };
|
|
875
|
+
}
|
|
876
|
+
if (prefix.startsWith('"')) {
|
|
877
|
+
return { objectKind: 'text', objectKey: prefix, objectText: null };
|
|
878
|
+
}
|
|
879
|
+
if (prefix.startsWith('_:')) {
|
|
880
|
+
return { objectKind: 'blankNode', objectKey: prefix, objectText: null };
|
|
881
|
+
}
|
|
882
|
+
return { objectKind: 'iri', objectKey: prefix, objectText: null };
|
|
883
|
+
}
|
|
884
|
+
objectDigestForIndex(serialized, fields) {
|
|
885
|
+
return fields.objectKey === null ? digestObject(serialized) : null;
|
|
886
|
+
}
|
|
887
|
+
assertComparableObject(fields, op) {
|
|
888
|
+
if (fields.objectKey !== null && fields.objectKind !== 'longText') {
|
|
889
|
+
return;
|
|
890
|
+
}
|
|
891
|
+
throw new Error(`Object ${op} is not supported for ${fields.objectKind}; declare/use a comparable data type instead of longText`);
|
|
892
|
+
}
|
|
893
|
+
extractExactPredicate(match) {
|
|
894
|
+
if (!match)
|
|
895
|
+
return undefined;
|
|
896
|
+
if (typeof match === 'object' && 'termType' in match) {
|
|
897
|
+
return (0, serialization_1.termToId)(match);
|
|
898
|
+
}
|
|
899
|
+
const ops = match;
|
|
900
|
+
if (ops.$eq !== undefined) {
|
|
901
|
+
return String(this.serializeOpValue(ops.$eq, false, '$eq'));
|
|
902
|
+
}
|
|
903
|
+
return undefined;
|
|
904
|
+
}
|
|
905
|
+
resolveObjectDataTypeForPattern(pattern) {
|
|
906
|
+
const predicate = this.extractExactPredicate(pattern.predicate);
|
|
907
|
+
if (predicate) {
|
|
908
|
+
return (0, value_types_1.getPredicateObjectDataType)(predicate, this.options.predicateObjectDataTypes);
|
|
909
|
+
}
|
|
910
|
+
if (pattern.object && typeof pattern.object === 'object' && 'termType' in pattern.object) {
|
|
911
|
+
return this.objectIndexForTerm(predicate, pattern.object).objectKind;
|
|
912
|
+
}
|
|
913
|
+
return undefined;
|
|
914
|
+
}
|
|
915
|
+
predicateForIndex(predicate) {
|
|
916
|
+
return predicate;
|
|
917
|
+
}
|
|
918
|
+
writeStatementForRow(row) {
|
|
919
|
+
const params = [
|
|
920
|
+
row.objectKind,
|
|
921
|
+
row.objectKey,
|
|
922
|
+
row.objectText,
|
|
923
|
+
row.objectDigest,
|
|
924
|
+
row.graph,
|
|
925
|
+
row.subject,
|
|
926
|
+
row.predicate,
|
|
927
|
+
row.object,
|
|
928
|
+
row.vector,
|
|
929
|
+
];
|
|
930
|
+
if (row.objectKey !== null) {
|
|
931
|
+
return {
|
|
932
|
+
sql: `
|
|
933
|
+
INSERT INTO quints (
|
|
934
|
+
object_kind, object_key, object_text, object_digest,
|
|
935
|
+
graph, subject, predicate, object, vector
|
|
936
|
+
)
|
|
937
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
938
|
+
ON CONFLICT (graph, subject, predicate, object_kind, object_key)
|
|
939
|
+
WHERE object_key IS NOT NULL
|
|
940
|
+
DO UPDATE SET
|
|
941
|
+
vector = excluded.vector,
|
|
942
|
+
object_text = excluded.object_text,
|
|
943
|
+
object_digest = excluded.object_digest
|
|
944
|
+
WHERE quints.object = excluded.object
|
|
945
|
+
`,
|
|
946
|
+
params,
|
|
947
|
+
};
|
|
948
|
+
}
|
|
949
|
+
return {
|
|
950
|
+
sql: `
|
|
951
|
+
INSERT INTO quints (
|
|
952
|
+
object_kind, object_key, object_text, object_digest,
|
|
953
|
+
graph, subject, predicate, object, vector
|
|
954
|
+
)
|
|
955
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
956
|
+
ON CONFLICT (graph, subject, predicate, object_kind, object_digest)
|
|
957
|
+
WHERE object_digest IS NOT NULL
|
|
958
|
+
DO UPDATE SET
|
|
959
|
+
vector = excluded.vector,
|
|
960
|
+
object_text = excluded.object_text
|
|
961
|
+
WHERE quints.object = excluded.object
|
|
962
|
+
`,
|
|
963
|
+
params,
|
|
964
|
+
};
|
|
965
|
+
}
|
|
740
966
|
quintToRow(quint) {
|
|
967
|
+
const predicate = (0, serialization_1.termToId)(quint.predicate);
|
|
968
|
+
const object = (0, serialization_1.serializeObject)(quint.object);
|
|
969
|
+
const objectIndex = this.objectIndexForTerm(predicate, quint.object);
|
|
741
970
|
return {
|
|
742
971
|
graph: (0, serialization_1.termToId)(quint.graph),
|
|
743
972
|
subject: (0, serialization_1.termToId)(quint.subject),
|
|
744
|
-
predicate
|
|
745
|
-
object
|
|
973
|
+
predicate,
|
|
974
|
+
object,
|
|
746
975
|
vector: quint.vector ? JSON.stringify(quint.vector) : null,
|
|
976
|
+
objectKind: objectIndex.objectKind,
|
|
977
|
+
objectKey: objectIndex.objectKey,
|
|
978
|
+
objectText: objectIndex.objectText,
|
|
979
|
+
objectDigest: this.objectDigestForIndex(object, objectIndex),
|
|
747
980
|
};
|
|
748
981
|
}
|
|
749
982
|
rowToQuint(row) {
|