@_linked/core 2.0.0 → 2.2.0-next.20260313111019
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/CHANGELOG.md +33 -171
- package/README.md +197 -45
- package/lib/cjs/expressions/Expr.d.ts +58 -0
- package/lib/cjs/expressions/Expr.js +217 -0
- package/lib/cjs/expressions/Expr.js.map +1 -0
- package/lib/cjs/expressions/ExpressionMethods.d.ts +81 -0
- package/lib/cjs/expressions/ExpressionMethods.js +3 -0
- package/lib/cjs/expressions/ExpressionMethods.js.map +1 -0
- package/lib/cjs/expressions/ExpressionNode.d.ts +95 -0
- package/lib/cjs/expressions/ExpressionNode.js +349 -0
- package/lib/cjs/expressions/ExpressionNode.js.map +1 -0
- package/lib/cjs/index.d.ts +4 -0
- package/lib/cjs/index.js +6 -1
- package/lib/cjs/index.js.map +1 -1
- package/lib/cjs/queries/DeleteBuilder.d.ts +17 -20
- package/lib/cjs/queries/DeleteBuilder.js +46 -19
- package/lib/cjs/queries/DeleteBuilder.js.map +1 -1
- package/lib/cjs/queries/DeleteQuery.d.ts +2 -2
- package/lib/cjs/queries/DeleteQuery.js.map +1 -1
- package/lib/cjs/queries/FieldSet.d.ts +3 -0
- package/lib/cjs/queries/FieldSet.js +16 -0
- package/lib/cjs/queries/FieldSet.js.map +1 -1
- package/lib/cjs/queries/IRCanonicalize.d.ts +10 -3
- package/lib/cjs/queries/IRCanonicalize.js +10 -1
- package/lib/cjs/queries/IRCanonicalize.js.map +1 -1
- package/lib/cjs/queries/IRDesugar.d.ts +30 -2
- package/lib/cjs/queries/IRDesugar.js +29 -9
- package/lib/cjs/queries/IRDesugar.js.map +1 -1
- package/lib/cjs/queries/IRLower.d.ts +19 -2
- package/lib/cjs/queries/IRLower.js +104 -20
- package/lib/cjs/queries/IRLower.js.map +1 -1
- package/lib/cjs/queries/IRMutation.d.ts +31 -1
- package/lib/cjs/queries/IRMutation.js +68 -15
- package/lib/cjs/queries/IRMutation.js.map +1 -1
- package/lib/cjs/queries/IntermediateRepresentation.d.ts +33 -4
- package/lib/cjs/queries/MutationQuery.js +16 -2
- package/lib/cjs/queries/MutationQuery.js.map +1 -1
- package/lib/cjs/queries/QueryBuilder.d.ts +14 -1
- package/lib/cjs/queries/QueryBuilder.js +59 -2
- package/lib/cjs/queries/QueryBuilder.js.map +1 -1
- package/lib/cjs/queries/QueryFactory.d.ts +2 -1
- package/lib/cjs/queries/QueryFactory.js.map +1 -1
- package/lib/cjs/queries/SelectQuery.d.ts +6 -2
- package/lib/cjs/queries/SelectQuery.js +51 -7
- package/lib/cjs/queries/SelectQuery.js.map +1 -1
- package/lib/cjs/queries/UpdateBuilder.d.ts +22 -13
- package/lib/cjs/queries/UpdateBuilder.js +60 -11
- package/lib/cjs/queries/UpdateBuilder.js.map +1 -1
- package/lib/cjs/queries/UpdateQuery.d.ts +2 -2
- package/lib/cjs/shapes/Shape.d.ts +8 -2
- package/lib/cjs/shapes/Shape.js +9 -13
- package/lib/cjs/shapes/Shape.js.map +1 -1
- package/lib/cjs/sparql/SparqlStore.js +15 -0
- package/lib/cjs/sparql/SparqlStore.js.map +1 -1
- package/lib/cjs/sparql/irToAlgebra.d.ts +34 -3
- package/lib/cjs/sparql/irToAlgebra.js +380 -31
- package/lib/cjs/sparql/irToAlgebra.js.map +1 -1
- package/lib/cjs/test-helpers/query-fixtures.d.ts +96 -208
- package/lib/cjs/test-helpers/query-fixtures.js +96 -19
- package/lib/cjs/test-helpers/query-fixtures.js.map +1 -1
- package/lib/esm/expressions/Expr.d.ts +58 -0
- package/lib/esm/expressions/Expr.js +214 -0
- package/lib/esm/expressions/Expr.js.map +1 -0
- package/lib/esm/expressions/ExpressionMethods.d.ts +81 -0
- package/lib/esm/expressions/ExpressionMethods.js +2 -0
- package/lib/esm/expressions/ExpressionMethods.js.map +1 -0
- package/lib/esm/expressions/ExpressionNode.d.ts +95 -0
- package/lib/esm/expressions/ExpressionNode.js +341 -0
- package/lib/esm/expressions/ExpressionNode.js.map +1 -0
- package/lib/esm/index.d.ts +4 -0
- package/lib/esm/index.js +3 -0
- package/lib/esm/index.js.map +1 -1
- package/lib/esm/queries/DeleteBuilder.d.ts +17 -20
- package/lib/esm/queries/DeleteBuilder.js +46 -19
- package/lib/esm/queries/DeleteBuilder.js.map +1 -1
- package/lib/esm/queries/DeleteQuery.d.ts +2 -2
- package/lib/esm/queries/DeleteQuery.js.map +1 -1
- package/lib/esm/queries/FieldSet.d.ts +3 -0
- package/lib/esm/queries/FieldSet.js +16 -0
- package/lib/esm/queries/FieldSet.js.map +1 -1
- package/lib/esm/queries/IRCanonicalize.d.ts +10 -3
- package/lib/esm/queries/IRCanonicalize.js +10 -1
- package/lib/esm/queries/IRCanonicalize.js.map +1 -1
- package/lib/esm/queries/IRDesugar.d.ts +30 -2
- package/lib/esm/queries/IRDesugar.js +21 -2
- package/lib/esm/queries/IRDesugar.js.map +1 -1
- package/lib/esm/queries/IRLower.d.ts +19 -2
- package/lib/esm/queries/IRLower.js +101 -19
- package/lib/esm/queries/IRLower.js.map +1 -1
- package/lib/esm/queries/IRMutation.d.ts +31 -1
- package/lib/esm/queries/IRMutation.js +63 -14
- package/lib/esm/queries/IRMutation.js.map +1 -1
- package/lib/esm/queries/IntermediateRepresentation.d.ts +33 -4
- package/lib/esm/queries/MutationQuery.js +16 -2
- package/lib/esm/queries/MutationQuery.js.map +1 -1
- package/lib/esm/queries/QueryBuilder.d.ts +14 -1
- package/lib/esm/queries/QueryBuilder.js +59 -2
- package/lib/esm/queries/QueryBuilder.js.map +1 -1
- package/lib/esm/queries/QueryFactory.d.ts +2 -1
- package/lib/esm/queries/QueryFactory.js.map +1 -1
- package/lib/esm/queries/SelectQuery.d.ts +6 -2
- package/lib/esm/queries/SelectQuery.js +51 -7
- package/lib/esm/queries/SelectQuery.js.map +1 -1
- package/lib/esm/queries/UpdateBuilder.d.ts +22 -13
- package/lib/esm/queries/UpdateBuilder.js +60 -11
- package/lib/esm/queries/UpdateBuilder.js.map +1 -1
- package/lib/esm/queries/UpdateQuery.d.ts +2 -2
- package/lib/esm/shapes/Shape.d.ts +8 -2
- package/lib/esm/shapes/Shape.js +9 -13
- package/lib/esm/shapes/Shape.js.map +1 -1
- package/lib/esm/sparql/SparqlStore.js +16 -1
- package/lib/esm/sparql/SparqlStore.js.map +1 -1
- package/lib/esm/sparql/irToAlgebra.d.ts +34 -3
- package/lib/esm/sparql/irToAlgebra.js +374 -31
- package/lib/esm/sparql/irToAlgebra.js.map +1 -1
- package/lib/esm/test-helpers/query-fixtures.d.ts +96 -208
- package/lib/esm/test-helpers/query-fixtures.js +96 -19
- package/lib/esm/test-helpers/query-fixtures.js.map +1 -1
- package/package.json +3 -3
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IRSelectQuery, IRCreateMutation, IRUpdateMutation, IRDeleteMutation } from '../queries/IntermediateRepresentation.js';
|
|
1
|
+
import { IRSelectQuery, IRCreateMutation, IRUpdateMutation, IRDeleteMutation, IRDeleteAllMutation, IRDeleteWhereMutation, IRUpdateWhereMutation } from '../queries/IntermediateRepresentation.js';
|
|
2
2
|
import { SparqlSelectPlan, SparqlInsertDataPlan, SparqlDeleteInsertPlan } from './SparqlAlgebra.js';
|
|
3
3
|
import { SparqlOptions } from './sparqlUtils.js';
|
|
4
4
|
/**
|
|
@@ -17,9 +17,29 @@ export declare function updateToAlgebra(query: IRUpdateMutation, options?: Sparq
|
|
|
17
17
|
* Converts an IRDeleteMutation to a SparqlDeleteInsertPlan (DELETE + WHERE).
|
|
18
18
|
*/
|
|
19
19
|
export declare function deleteToAlgebra(query: IRDeleteMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
|
|
20
|
+
/**
|
|
21
|
+
* Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
|
|
22
|
+
*
|
|
23
|
+
* Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
|
|
24
|
+
* WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
|
|
25
|
+
*/
|
|
26
|
+
export declare function deleteAllToAlgebra(query: IRDeleteAllMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
|
|
27
|
+
/**
|
|
28
|
+
* Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
|
|
29
|
+
*
|
|
30
|
+
* Like deleteAllToAlgebra but adds filter conditions from the where clause.
|
|
31
|
+
*/
|
|
32
|
+
export declare function deleteWhereToAlgebra(query: IRDeleteWhereMutation, _options?: SparqlOptions): SparqlDeleteInsertPlan;
|
|
33
|
+
/**
|
|
34
|
+
* Converts an IRUpdateWhereMutation to a SparqlDeleteInsertPlan.
|
|
35
|
+
*
|
|
36
|
+
* Like updateToAlgebra but uses a variable subject (?a0) instead of a
|
|
37
|
+
* hardcoded entity IRI, adds a type triple, and optionally includes
|
|
38
|
+
* filter conditions from the where clause.
|
|
39
|
+
*/
|
|
40
|
+
export declare function updateWhereToAlgebra(query: IRUpdateWhereMutation, options?: SparqlOptions): SparqlDeleteInsertPlan;
|
|
20
41
|
/**
|
|
21
42
|
* Converts an IRSelectQuery to a SPARQL string.
|
|
22
|
-
* Stub: will be implemented when algebraToString is available.
|
|
23
43
|
*/
|
|
24
44
|
export declare function selectToSparql(query: IRSelectQuery, options?: SparqlOptions): string;
|
|
25
45
|
/**
|
|
@@ -34,6 +54,17 @@ export declare function createToSparql(query: IRCreateMutation, options?: Sparql
|
|
|
34
54
|
export declare function updateToSparql(query: IRUpdateMutation, options?: SparqlOptions): string;
|
|
35
55
|
/**
|
|
36
56
|
* Converts an IRDeleteMutation to a SPARQL string.
|
|
37
|
-
* Stub: will be implemented when algebraToString is available.
|
|
38
57
|
*/
|
|
39
58
|
export declare function deleteToSparql(query: IRDeleteMutation, options?: SparqlOptions): string;
|
|
59
|
+
/**
|
|
60
|
+
* Converts an IRDeleteAllMutation to a SPARQL string.
|
|
61
|
+
*/
|
|
62
|
+
export declare function deleteAllToSparql(query: IRDeleteAllMutation, options?: SparqlOptions): string;
|
|
63
|
+
/**
|
|
64
|
+
* Converts an IRDeleteWhereMutation to a SPARQL string.
|
|
65
|
+
*/
|
|
66
|
+
export declare function deleteWhereToSparql(query: IRDeleteWhereMutation, options?: SparqlOptions): string;
|
|
67
|
+
/**
|
|
68
|
+
* Converts an IRUpdateWhereMutation to a SPARQL string.
|
|
69
|
+
*/
|
|
70
|
+
export declare function updateWhereToSparql(query: IRUpdateWhereMutation, options?: SparqlOptions): string;
|
|
@@ -4,12 +4,36 @@ exports.selectToAlgebra = selectToAlgebra;
|
|
|
4
4
|
exports.createToAlgebra = createToAlgebra;
|
|
5
5
|
exports.updateToAlgebra = updateToAlgebra;
|
|
6
6
|
exports.deleteToAlgebra = deleteToAlgebra;
|
|
7
|
+
exports.deleteAllToAlgebra = deleteAllToAlgebra;
|
|
8
|
+
exports.deleteWhereToAlgebra = deleteWhereToAlgebra;
|
|
9
|
+
exports.updateWhereToAlgebra = updateWhereToAlgebra;
|
|
7
10
|
exports.selectToSparql = selectToSparql;
|
|
8
11
|
exports.createToSparql = createToSparql;
|
|
9
12
|
exports.updateToSparql = updateToSparql;
|
|
10
13
|
exports.deleteToSparql = deleteToSparql;
|
|
14
|
+
exports.deleteAllToSparql = deleteAllToSparql;
|
|
15
|
+
exports.deleteWhereToSparql = deleteWhereToSparql;
|
|
16
|
+
exports.updateWhereToSparql = updateWhereToSparql;
|
|
11
17
|
const sparqlUtils_js_1 = require("./sparqlUtils.js");
|
|
12
18
|
const algebraToString_js_1 = require("./algebraToString.js");
|
|
19
|
+
// Lazy-loaded to avoid circular dependency:
|
|
20
|
+
// irToAlgebra → ShapeClass → Shape → CreateBuilder → … → SHACL → Shape (circular)
|
|
21
|
+
let _getShapeClass;
|
|
22
|
+
function lazyGetShapeClass(id) {
|
|
23
|
+
if (!_getShapeClass) {
|
|
24
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
25
|
+
_getShapeClass = require('../utils/ShapeClass.js').getShapeClass;
|
|
26
|
+
}
|
|
27
|
+
return _getShapeClass(id);
|
|
28
|
+
}
|
|
29
|
+
let _shacl;
|
|
30
|
+
function lazyShacl() {
|
|
31
|
+
if (!_shacl) {
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
33
|
+
_shacl = require('../ontologies/shacl.js').shacl;
|
|
34
|
+
}
|
|
35
|
+
return _shacl;
|
|
36
|
+
}
|
|
13
37
|
const rdf_js_1 = require("../ontologies/rdf.js");
|
|
14
38
|
const xsd_js_1 = require("../ontologies/xsd.js");
|
|
15
39
|
// ---------------------------------------------------------------------------
|
|
@@ -53,6 +77,16 @@ function propertySuffix(propertyUri) {
|
|
|
53
77
|
function sanitizeVarName(name) {
|
|
54
78
|
return name.replace(/[^A-Za-z0-9_]/g, '_');
|
|
55
79
|
}
|
|
80
|
+
const IR_EXPRESSION_KINDS = new Set([
|
|
81
|
+
'literal_expr', 'property_expr', 'binary_expr', 'logical_expr',
|
|
82
|
+
'not_expr', 'function_expr', 'aggregate_expr', 'reference_expr',
|
|
83
|
+
'alias_expr', 'context_property_expr', 'exists_expr',
|
|
84
|
+
]);
|
|
85
|
+
function isIRExpression(value) {
|
|
86
|
+
return !!value && typeof value === 'object' && 'kind' in value &&
|
|
87
|
+
typeof value.kind === 'string' &&
|
|
88
|
+
IR_EXPRESSION_KINDS.has(value.kind);
|
|
89
|
+
}
|
|
56
90
|
/**
|
|
57
91
|
* Wrap a single node in a LeftJoin, making `right` optional relative to `left`.
|
|
58
92
|
*/
|
|
@@ -238,6 +272,23 @@ function selectToAlgebra(query, _options) {
|
|
|
238
272
|
};
|
|
239
273
|
}
|
|
240
274
|
}
|
|
275
|
+
// 5b. MINUS patterns — wrap algebra in SparqlMinus for each minus pattern
|
|
276
|
+
for (const pattern of query.patterns) {
|
|
277
|
+
if (pattern.kind === 'minus') {
|
|
278
|
+
let minusAlgebra = convertExistsPattern(pattern.pattern, registry);
|
|
279
|
+
if (pattern.filter) {
|
|
280
|
+
const minusPropertyTriples = [];
|
|
281
|
+
processExpressionForProperties(pattern.filter, registry, minusPropertyTriples);
|
|
282
|
+
// Add property triples into the MINUS block
|
|
283
|
+
if (minusPropertyTriples.length > 0) {
|
|
284
|
+
minusAlgebra = joinNodes(minusAlgebra, { type: 'bgp', triples: minusPropertyTriples });
|
|
285
|
+
}
|
|
286
|
+
const filterExpr = convertExpression(pattern.filter, registry, minusPropertyTriples);
|
|
287
|
+
minusAlgebra = { type: 'filter', expression: filterExpr, inner: minusAlgebra };
|
|
288
|
+
}
|
|
289
|
+
algebra = { type: 'minus', left: algebra, right: minusAlgebra };
|
|
290
|
+
}
|
|
291
|
+
}
|
|
241
292
|
// 6. SubjectId → Filter / SubjectIds → VALUES
|
|
242
293
|
if (query.subjectIds && query.subjectIds.length > 0) {
|
|
243
294
|
// Multiple subjects: use VALUES clause for efficient filtering
|
|
@@ -403,6 +454,10 @@ function processPattern(pattern, registry, traverseTriples, optionalPropertyTrip
|
|
|
403
454
|
processPattern(pattern.pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks);
|
|
404
455
|
break;
|
|
405
456
|
}
|
|
457
|
+
case 'minus': {
|
|
458
|
+
// MINUS patterns are handled separately in selectToAlgebra — skip in processPattern.
|
|
459
|
+
break;
|
|
460
|
+
}
|
|
406
461
|
}
|
|
407
462
|
}
|
|
408
463
|
// ---------------------------------------------------------------------------
|
|
@@ -614,6 +669,9 @@ function convertExistsPattern(pattern, registry) {
|
|
|
614
669
|
case 'exists': {
|
|
615
670
|
return convertExistsPattern(pattern.pattern, registry);
|
|
616
671
|
}
|
|
672
|
+
case 'minus': {
|
|
673
|
+
return convertExistsPattern(pattern.pattern, registry);
|
|
674
|
+
}
|
|
617
675
|
default:
|
|
618
676
|
throw new Error(`Unsupported pattern kind in EXISTS: ${pattern.kind}`);
|
|
619
677
|
}
|
|
@@ -733,15 +791,19 @@ function createToAlgebra(query, options) {
|
|
|
733
791
|
triples,
|
|
734
792
|
};
|
|
735
793
|
}
|
|
794
|
+
// ---------------------------------------------------------------------------
|
|
795
|
+
// Shared update field processing
|
|
796
|
+
// ---------------------------------------------------------------------------
|
|
736
797
|
/**
|
|
737
|
-
*
|
|
798
|
+
* Processes IRNodeData fields into DELETE/INSERT/WHERE triples.
|
|
799
|
+
* Shared between updateToAlgebra (IRI subject) and updateWhereToAlgebra (variable subject).
|
|
738
800
|
*/
|
|
739
|
-
function
|
|
740
|
-
const subjectTerm = iriTerm(query.id);
|
|
801
|
+
function processUpdateFields(data, subjectTerm, options) {
|
|
741
802
|
const deletePatterns = [];
|
|
742
803
|
const insertPatterns = [];
|
|
743
|
-
const
|
|
744
|
-
|
|
804
|
+
const oldValueTriples = [];
|
|
805
|
+
const extends_ = [];
|
|
806
|
+
for (const field of data.fields) {
|
|
745
807
|
const propertyTerm = iriTerm(field.property);
|
|
746
808
|
const suffix = propertySuffix(field.property);
|
|
747
809
|
// Check for set modification ({add, remove})
|
|
@@ -753,19 +815,16 @@ function updateToAlgebra(query, options) {
|
|
|
753
815
|
!('shape' in field.value) &&
|
|
754
816
|
('add' in field.value || 'remove' in field.value)) {
|
|
755
817
|
const setMod = field.value;
|
|
756
|
-
// Remove specific values
|
|
757
818
|
if (setMod.remove) {
|
|
758
819
|
for (const removeItem of setMod.remove) {
|
|
759
820
|
const removeTerm = iriTerm(removeItem.id);
|
|
760
821
|
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
|
|
761
|
-
|
|
822
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
|
|
762
823
|
}
|
|
763
824
|
}
|
|
764
|
-
// Add new values
|
|
765
825
|
if (setMod.add) {
|
|
766
826
|
for (const addItem of setMod.add) {
|
|
767
827
|
if (addItem && typeof addItem === 'object' && 'shape' in addItem && 'fields' in addItem) {
|
|
768
|
-
// Nested create in add
|
|
769
828
|
const nested = generateNodeDataTriples(addItem, options);
|
|
770
829
|
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
771
830
|
insertPatterns.push(...nested.triples);
|
|
@@ -784,14 +843,14 @@ function updateToAlgebra(query, options) {
|
|
|
784
843
|
if (field.value === undefined || field.value === null) {
|
|
785
844
|
const oldVar = varTerm(`old_${suffix}`);
|
|
786
845
|
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
787
|
-
|
|
846
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
788
847
|
continue;
|
|
789
848
|
}
|
|
790
849
|
// Array overwrite — delete old values + insert new ones
|
|
791
850
|
if (Array.isArray(field.value)) {
|
|
792
851
|
const oldVar = varTerm(`old_${suffix}`);
|
|
793
852
|
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
794
|
-
|
|
853
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
795
854
|
for (const item of field.value) {
|
|
796
855
|
if (item && typeof item === 'object' && 'shape' in item && 'fields' in item) {
|
|
797
856
|
const nested = generateNodeDataTriples(item, options);
|
|
@@ -811,49 +870,109 @@ function updateToAlgebra(query, options) {
|
|
|
811
870
|
if (typeof field.value === 'object' && 'shape' in field.value && 'fields' in field.value) {
|
|
812
871
|
const oldVar = varTerm(`old_${suffix}`);
|
|
813
872
|
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
814
|
-
|
|
873
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
815
874
|
const nested = generateNodeDataTriples(field.value, options);
|
|
816
875
|
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
817
876
|
insertPatterns.push(...nested.triples);
|
|
818
877
|
continue;
|
|
819
878
|
}
|
|
879
|
+
// IRExpression — computed value update (e.g. p.age.plus(1))
|
|
880
|
+
if (isIRExpression(field.value)) {
|
|
881
|
+
const expr = field.value;
|
|
882
|
+
const oldVar = varTerm(`old_${suffix}`);
|
|
883
|
+
const computedVarName = `computed_${suffix}`;
|
|
884
|
+
const computedVar = varTerm(computedVarName);
|
|
885
|
+
// DELETE old value
|
|
886
|
+
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
887
|
+
// WHERE: OPTIONAL for old value
|
|
888
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
889
|
+
// Discover additional property references in the expression and add OPTIONAL triples
|
|
890
|
+
const registry = new VariableRegistry();
|
|
891
|
+
const mutationSubjectAlias = '__mutation_subject__';
|
|
892
|
+
// Pre-register the subject variable mapping for the field being updated
|
|
893
|
+
registry.set(mutationSubjectAlias, field.property, `old_${suffix}`);
|
|
894
|
+
const additionalOptionals = [];
|
|
895
|
+
processExpressionForProperties(expr, registry, additionalOptionals);
|
|
896
|
+
// Add any additional property OPTIONAL triples (for refs to other properties)
|
|
897
|
+
for (const triple of additionalOptionals) {
|
|
898
|
+
// Rewrite the subject from the placeholder variable to the actual subject term
|
|
899
|
+
if (triple.subject.kind === 'variable' && triple.subject.name === mutationSubjectAlias) {
|
|
900
|
+
oldValueTriples.push(tripleOf(subjectTerm, triple.predicate, triple.object));
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
oldValueTriples.push(triple);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
// Convert IRExpression to SparqlExpression
|
|
907
|
+
const sparqlExpr = convertExpression(expr, registry, additionalOptionals);
|
|
908
|
+
// BIND computed expression
|
|
909
|
+
extends_.push({ variable: computedVarName, expression: sparqlExpr });
|
|
910
|
+
// INSERT computed value
|
|
911
|
+
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, computedVar));
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
820
914
|
// Simple value update — delete old + insert new
|
|
821
915
|
const oldVar = varTerm(`old_${suffix}`);
|
|
822
916
|
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
823
|
-
|
|
917
|
+
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
824
918
|
const terms = fieldValueToTerms(field.value, options);
|
|
825
919
|
for (const term of terms) {
|
|
826
920
|
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
827
921
|
}
|
|
828
922
|
}
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
923
|
+
return { deletePatterns, insertPatterns, oldValueTriples, extends: extends_ };
|
|
924
|
+
}
|
|
925
|
+
/**
|
|
926
|
+
* Wraps old-value triples in OPTIONAL (LEFT JOIN) so UPDATE succeeds
|
|
927
|
+
* even when the old value doesn't exist.
|
|
928
|
+
*/
|
|
929
|
+
function wrapOldValueOptionals(base, oldValueTriples) {
|
|
930
|
+
let algebra = base;
|
|
931
|
+
if (oldValueTriples.length === 0) {
|
|
932
|
+
return algebra;
|
|
834
933
|
}
|
|
835
|
-
|
|
836
|
-
|
|
934
|
+
for (const triple of oldValueTriples) {
|
|
935
|
+
algebra = {
|
|
837
936
|
type: 'left_join',
|
|
838
|
-
left:
|
|
839
|
-
right: { type: 'bgp', triples:
|
|
937
|
+
left: algebra,
|
|
938
|
+
right: { type: 'bgp', triples: [triple] },
|
|
840
939
|
};
|
|
841
940
|
}
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
941
|
+
return algebra;
|
|
942
|
+
}
|
|
943
|
+
/**
|
|
944
|
+
* Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
|
|
945
|
+
*/
|
|
946
|
+
function updateToAlgebra(query, options) {
|
|
947
|
+
const subjectTerm = iriTerm(query.id);
|
|
948
|
+
const result = processUpdateFields(query.data, subjectTerm, options);
|
|
949
|
+
let whereAlgebra = wrapOldValueOptionals({ type: 'bgp', triples: [] }, result.oldValueTriples);
|
|
950
|
+
// Add traversal OPTIONAL patterns (for multi-segment expression refs)
|
|
951
|
+
// These must come BEFORE expression BINDs since the BINDs reference traversal variables.
|
|
952
|
+
if (query.traversalPatterns) {
|
|
953
|
+
for (const trav of query.traversalPatterns) {
|
|
954
|
+
const fromTerm = trav.from === '__mutation_subject__' ? subjectTerm : varTerm(trav.from);
|
|
955
|
+
const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
|
|
846
956
|
whereAlgebra = {
|
|
847
957
|
type: 'left_join',
|
|
848
958
|
left: whereAlgebra,
|
|
849
|
-
right: { type: 'bgp', triples: [
|
|
959
|
+
right: { type: 'bgp', triples: [traversalTriple] },
|
|
850
960
|
};
|
|
851
961
|
}
|
|
852
962
|
}
|
|
963
|
+
// Add BIND expressions for computed fields
|
|
964
|
+
for (const ext of result.extends) {
|
|
965
|
+
whereAlgebra = {
|
|
966
|
+
type: 'extend',
|
|
967
|
+
inner: whereAlgebra,
|
|
968
|
+
variable: ext.variable,
|
|
969
|
+
expression: ext.expression,
|
|
970
|
+
};
|
|
971
|
+
}
|
|
853
972
|
return {
|
|
854
973
|
type: 'delete_insert',
|
|
855
|
-
deletePatterns,
|
|
856
|
-
insertPatterns,
|
|
974
|
+
deletePatterns: result.deletePatterns,
|
|
975
|
+
insertPatterns: result.insertPatterns,
|
|
857
976
|
whereAlgebra,
|
|
858
977
|
};
|
|
859
978
|
}
|
|
@@ -894,11 +1013,221 @@ function deleteToAlgebra(query, _options) {
|
|
|
894
1013
|
};
|
|
895
1014
|
}
|
|
896
1015
|
// ---------------------------------------------------------------------------
|
|
1016
|
+
// Blank node tree walking for schema-aware delete cleanup
|
|
1017
|
+
// ---------------------------------------------------------------------------
|
|
1018
|
+
/**
|
|
1019
|
+
* Checks whether a PropertyShape points to blank nodes (sh:BlankNode or
|
|
1020
|
+
* sh:BlankNodeOrIRI). Returns true when the property's range *may* include
|
|
1021
|
+
* blank node values that should be cleaned up on delete.
|
|
1022
|
+
*/
|
|
1023
|
+
function isBlankNodeProperty(prop) {
|
|
1024
|
+
var _a;
|
|
1025
|
+
const nk = (_a = prop.nodeKind) === null || _a === void 0 ? void 0 : _a.id;
|
|
1026
|
+
if (!nk)
|
|
1027
|
+
return false;
|
|
1028
|
+
const s = lazyShacl();
|
|
1029
|
+
return nk === s.BlankNode.id || nk === s.BlankNodeOrIRI.id;
|
|
1030
|
+
}
|
|
1031
|
+
/**
|
|
1032
|
+
* Recursively builds DELETE + WHERE patterns for blank-node-typed properties.
|
|
1033
|
+
*
|
|
1034
|
+
* For each blank-node property on the shape:
|
|
1035
|
+
* - DELETE: `?bnVar ?pN ?oN .` (wildcard all triples on the blank node)
|
|
1036
|
+
* - WHERE: `OPTIONAL { ?parent <property> ?bnVar . FILTER(isBlank(?bnVar)) . ?bnVar ?pN ?oN . }`
|
|
1037
|
+
*
|
|
1038
|
+
* Recurses into the property's valueShape to handle nested blank nodes
|
|
1039
|
+
* (e.g. Person → Address (blank) → GeoPoint (blank)).
|
|
1040
|
+
*/
|
|
1041
|
+
function walkBlankNodeTree(shapeId, parentVar, depth, deletePatterns) {
|
|
1042
|
+
var _a;
|
|
1043
|
+
const shapeClass = lazyGetShapeClass(shapeId);
|
|
1044
|
+
if (!(shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape))
|
|
1045
|
+
return null;
|
|
1046
|
+
let optionals = null;
|
|
1047
|
+
const props = shapeClass.shape.getPropertyShapes(true);
|
|
1048
|
+
for (const prop of props) {
|
|
1049
|
+
if (!isBlankNodeProperty(prop))
|
|
1050
|
+
continue;
|
|
1051
|
+
const bnVar = `bn${depth}`;
|
|
1052
|
+
const pVar = `p${depth}`;
|
|
1053
|
+
const oVar = `o${depth}`;
|
|
1054
|
+
// DELETE pattern: wildcard all triples on the blank node
|
|
1055
|
+
deletePatterns.push(tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar)));
|
|
1056
|
+
// WHERE: parent --<property>--> ?bnVar
|
|
1057
|
+
const traverseTriple = tripleOf(varTerm(parentVar), iriTerm(prop.path[0].id), varTerm(bnVar));
|
|
1058
|
+
// FILTER(isBlank(?bnVar))
|
|
1059
|
+
const isBlankFilter = {
|
|
1060
|
+
kind: 'function_expr',
|
|
1061
|
+
name: 'isBlank',
|
|
1062
|
+
args: [{ kind: 'variable_expr', name: bnVar }],
|
|
1063
|
+
};
|
|
1064
|
+
// ?bnVar ?pN ?oN
|
|
1065
|
+
const wildcardTriple = tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar));
|
|
1066
|
+
// Build inner pattern: traverse + filter + wildcard
|
|
1067
|
+
let innerPattern = {
|
|
1068
|
+
type: 'bgp',
|
|
1069
|
+
triples: [traverseTriple, wildcardTriple],
|
|
1070
|
+
};
|
|
1071
|
+
innerPattern = { type: 'filter', expression: isBlankFilter, inner: innerPattern };
|
|
1072
|
+
// Recurse into valueShape for nested blank nodes
|
|
1073
|
+
if ((_a = prop.valueShape) === null || _a === void 0 ? void 0 : _a.id) {
|
|
1074
|
+
const nestedOptional = walkBlankNodeTree(prop.valueShape.id, bnVar, depth + 1, deletePatterns);
|
|
1075
|
+
if (nestedOptional) {
|
|
1076
|
+
innerPattern = { type: 'left_join', left: innerPattern, right: nestedOptional };
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
// Wrap in OPTIONAL (left_join)
|
|
1080
|
+
if (optionals) {
|
|
1081
|
+
optionals = { type: 'left_join', left: optionals, right: innerPattern };
|
|
1082
|
+
}
|
|
1083
|
+
else {
|
|
1084
|
+
optionals = innerPattern;
|
|
1085
|
+
}
|
|
1086
|
+
depth++;
|
|
1087
|
+
}
|
|
1088
|
+
return optionals;
|
|
1089
|
+
}
|
|
1090
|
+
/**
|
|
1091
|
+
* Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
|
|
1092
|
+
*
|
|
1093
|
+
* Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
|
|
1094
|
+
* WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
|
|
1095
|
+
*/
|
|
1096
|
+
function deleteAllToAlgebra(query, _options) {
|
|
1097
|
+
const subjectVar = 'a0';
|
|
1098
|
+
// DELETE patterns: root wildcard
|
|
1099
|
+
const deletePatterns = [
|
|
1100
|
+
tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
|
|
1101
|
+
];
|
|
1102
|
+
// WHERE: type triple + root wildcard
|
|
1103
|
+
const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(query.shape));
|
|
1104
|
+
const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
|
|
1105
|
+
let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
|
|
1106
|
+
// Walk blank node tree for cleanup
|
|
1107
|
+
const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
|
|
1108
|
+
if (blankNodeOptional) {
|
|
1109
|
+
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
|
|
1110
|
+
}
|
|
1111
|
+
return {
|
|
1112
|
+
type: 'delete_insert',
|
|
1113
|
+
deletePatterns,
|
|
1114
|
+
insertPatterns: [],
|
|
1115
|
+
whereAlgebra,
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
/**
|
|
1119
|
+
* Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
|
|
1120
|
+
*
|
|
1121
|
+
* Like deleteAllToAlgebra but adds filter conditions from the where clause.
|
|
1122
|
+
*/
|
|
1123
|
+
function deleteWhereToAlgebra(query, _options) {
|
|
1124
|
+
const subjectVar = 'a0';
|
|
1125
|
+
const registry = new VariableRegistry();
|
|
1126
|
+
// DELETE patterns: root wildcard
|
|
1127
|
+
const deletePatterns = [
|
|
1128
|
+
tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
|
|
1129
|
+
];
|
|
1130
|
+
// WHERE: type triple + root wildcard
|
|
1131
|
+
const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(query.shape));
|
|
1132
|
+
const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
|
|
1133
|
+
let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
|
|
1134
|
+
// Process where patterns (traversals from the where clause)
|
|
1135
|
+
const traverseTriples = [];
|
|
1136
|
+
const optionalPropertyTriples = [];
|
|
1137
|
+
for (const pattern of query.wherePatterns) {
|
|
1138
|
+
processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
|
|
1139
|
+
}
|
|
1140
|
+
// Add traverse triples to required BGP
|
|
1141
|
+
if (traverseTriples.length > 0) {
|
|
1142
|
+
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
|
|
1143
|
+
}
|
|
1144
|
+
// Process expression to discover property triples
|
|
1145
|
+
processExpressionForProperties(query.where, registry, optionalPropertyTriples);
|
|
1146
|
+
// Add optional property triples
|
|
1147
|
+
for (const triple of optionalPropertyTriples) {
|
|
1148
|
+
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
|
|
1149
|
+
}
|
|
1150
|
+
// Convert and add filter expression
|
|
1151
|
+
const filterExpr = convertExpression(query.where, registry, []);
|
|
1152
|
+
whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
|
|
1153
|
+
// Walk blank node tree for cleanup
|
|
1154
|
+
const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
|
|
1155
|
+
if (blankNodeOptional) {
|
|
1156
|
+
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
|
|
1157
|
+
}
|
|
1158
|
+
return {
|
|
1159
|
+
type: 'delete_insert',
|
|
1160
|
+
deletePatterns,
|
|
1161
|
+
insertPatterns: [],
|
|
1162
|
+
whereAlgebra,
|
|
1163
|
+
};
|
|
1164
|
+
}
|
|
1165
|
+
/**
|
|
1166
|
+
* Converts an IRUpdateWhereMutation to a SparqlDeleteInsertPlan.
|
|
1167
|
+
*
|
|
1168
|
+
* Like updateToAlgebra but uses a variable subject (?a0) instead of a
|
|
1169
|
+
* hardcoded entity IRI, adds a type triple, and optionally includes
|
|
1170
|
+
* filter conditions from the where clause.
|
|
1171
|
+
*/
|
|
1172
|
+
function updateWhereToAlgebra(query, options) {
|
|
1173
|
+
const subjectTerm = varTerm('a0');
|
|
1174
|
+
const result = processUpdateFields(query.data, subjectTerm, options);
|
|
1175
|
+
// WHERE: type triple is always required
|
|
1176
|
+
const typeTriple = tripleOf(subjectTerm, iriTerm(RDF_TYPE), iriTerm(query.data.shape));
|
|
1177
|
+
let whereAlgebra = { type: 'bgp', triples: [typeTriple] };
|
|
1178
|
+
// Process where filter conditions (if any)
|
|
1179
|
+
if (query.where && query.wherePatterns) {
|
|
1180
|
+
const registry = new VariableRegistry();
|
|
1181
|
+
const traverseTriples = [];
|
|
1182
|
+
const optionalPropertyTriples = [];
|
|
1183
|
+
for (const pattern of query.wherePatterns) {
|
|
1184
|
+
processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
|
|
1185
|
+
}
|
|
1186
|
+
if (traverseTriples.length > 0) {
|
|
1187
|
+
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
|
|
1188
|
+
}
|
|
1189
|
+
processExpressionForProperties(query.where, registry, optionalPropertyTriples);
|
|
1190
|
+
for (const triple of optionalPropertyTriples) {
|
|
1191
|
+
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
|
|
1192
|
+
}
|
|
1193
|
+
const filterExpr = convertExpression(query.where, registry, []);
|
|
1194
|
+
whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
|
|
1195
|
+
}
|
|
1196
|
+
whereAlgebra = wrapOldValueOptionals(whereAlgebra, result.oldValueTriples);
|
|
1197
|
+
// Add traversal OPTIONAL patterns (for multi-segment expression refs)
|
|
1198
|
+
// These must come BEFORE expression BINDs since the BINDs reference traversal variables.
|
|
1199
|
+
if (query.traversalPatterns) {
|
|
1200
|
+
for (const trav of query.traversalPatterns) {
|
|
1201
|
+
const fromTerm = trav.from === '__mutation_subject__' ? varTerm('a0') : varTerm(trav.from);
|
|
1202
|
+
const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
|
|
1203
|
+
whereAlgebra = {
|
|
1204
|
+
type: 'left_join',
|
|
1205
|
+
left: whereAlgebra,
|
|
1206
|
+
right: { type: 'bgp', triples: [traversalTriple] },
|
|
1207
|
+
};
|
|
1208
|
+
}
|
|
1209
|
+
}
|
|
1210
|
+
// Add BIND expressions for computed fields
|
|
1211
|
+
for (const ext of result.extends) {
|
|
1212
|
+
whereAlgebra = {
|
|
1213
|
+
type: 'extend',
|
|
1214
|
+
inner: whereAlgebra,
|
|
1215
|
+
variable: ext.variable,
|
|
1216
|
+
expression: ext.expression,
|
|
1217
|
+
};
|
|
1218
|
+
}
|
|
1219
|
+
return {
|
|
1220
|
+
type: 'delete_insert',
|
|
1221
|
+
deletePatterns: result.deletePatterns,
|
|
1222
|
+
insertPatterns: result.insertPatterns,
|
|
1223
|
+
whereAlgebra,
|
|
1224
|
+
};
|
|
1225
|
+
}
|
|
1226
|
+
// ---------------------------------------------------------------------------
|
|
897
1227
|
// Convenience wrappers: IR → algebra → SPARQL string in one call
|
|
898
1228
|
// ---------------------------------------------------------------------------
|
|
899
1229
|
/**
|
|
900
1230
|
* Converts an IRSelectQuery to a SPARQL string.
|
|
901
|
-
* Stub: will be implemented when algebraToString is available.
|
|
902
1231
|
*/
|
|
903
1232
|
function selectToSparql(query, options) {
|
|
904
1233
|
const plan = selectToAlgebra(query, options);
|
|
@@ -922,10 +1251,30 @@ function updateToSparql(query, options) {
|
|
|
922
1251
|
}
|
|
923
1252
|
/**
|
|
924
1253
|
* Converts an IRDeleteMutation to a SPARQL string.
|
|
925
|
-
* Stub: will be implemented when algebraToString is available.
|
|
926
1254
|
*/
|
|
927
1255
|
function deleteToSparql(query, options) {
|
|
928
1256
|
const plan = deleteToAlgebra(query, options);
|
|
929
1257
|
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
930
1258
|
}
|
|
1259
|
+
/**
|
|
1260
|
+
* Converts an IRDeleteAllMutation to a SPARQL string.
|
|
1261
|
+
*/
|
|
1262
|
+
function deleteAllToSparql(query, options) {
|
|
1263
|
+
const plan = deleteAllToAlgebra(query, options);
|
|
1264
|
+
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
1265
|
+
}
|
|
1266
|
+
/**
|
|
1267
|
+
* Converts an IRDeleteWhereMutation to a SPARQL string.
|
|
1268
|
+
*/
|
|
1269
|
+
function deleteWhereToSparql(query, options) {
|
|
1270
|
+
const plan = deleteWhereToAlgebra(query, options);
|
|
1271
|
+
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
1272
|
+
}
|
|
1273
|
+
/**
|
|
1274
|
+
* Converts an IRUpdateWhereMutation to a SPARQL string.
|
|
1275
|
+
*/
|
|
1276
|
+
function updateWhereToSparql(query, options) {
|
|
1277
|
+
const plan = updateWhereToAlgebra(query, options);
|
|
1278
|
+
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
1279
|
+
}
|
|
931
1280
|
//# sourceMappingURL=irToAlgebra.js.map
|