@_linked/core 2.12.0 → 2.13.0-next.20260706164859
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 +6 -0
- package/lib/esm/queries/IRPipeline.d.ts +7 -1
- package/lib/esm/queries/IRPipeline.js +20 -6
- package/lib/esm/queries/IRPipeline.js.map +1 -1
- package/lib/esm/queries/queryDispatch.js +12 -2
- package/lib/esm/queries/queryDispatch.js.map +1 -1
- package/package.json +10 -13
- package/lib/cjs/collections/CoreSet.d.ts +0 -47
- package/lib/cjs/collections/CoreSet.js +0 -136
- package/lib/cjs/collections/CoreSet.js.map +0 -1
- package/lib/cjs/collections/ShapeSet.d.ts +0 -6
- package/lib/cjs/collections/ShapeSet.js +0 -20
- package/lib/cjs/collections/ShapeSet.js.map +0 -1
- package/lib/cjs/expressions/Expr.d.ts +0 -29
- package/lib/cjs/expressions/Expr.js +0 -80
- package/lib/cjs/expressions/Expr.js.map +0 -1
- package/lib/cjs/expressions/ExpressionMethods.d.ts +0 -85
- package/lib/cjs/expressions/ExpressionMethods.js +0 -3
- package/lib/cjs/expressions/ExpressionMethods.js.map +0 -1
- package/lib/cjs/expressions/ExpressionNode.d.ts +0 -144
- package/lib/cjs/expressions/ExpressionNode.js +0 -432
- package/lib/cjs/expressions/ExpressionNode.js.map +0 -1
- package/lib/cjs/index.d.ts +0 -29
- package/lib/cjs/index.js +0 -65
- package/lib/cjs/index.js.map +0 -1
- package/lib/cjs/interfaces/ICoreIterable.d.ts +0 -14
- package/lib/cjs/interfaces/ICoreIterable.js +0 -3
- package/lib/cjs/interfaces/ICoreIterable.js.map +0 -1
- package/lib/cjs/interfaces/IDataset.d.ts +0 -26
- package/lib/cjs/interfaces/IDataset.js +0 -3
- package/lib/cjs/interfaces/IDataset.js.map +0 -1
- package/lib/cjs/interfaces/IFileStore.d.ts +0 -16
- package/lib/cjs/interfaces/IFileStore.js +0 -3
- package/lib/cjs/interfaces/IFileStore.js.map +0 -1
- package/lib/cjs/ontologies/linked-core.d.ts +0 -15
- package/lib/cjs/ontologies/linked-core.js +0 -38
- package/lib/cjs/ontologies/linked-core.js.map +0 -1
- package/lib/cjs/ontologies/npm.d.ts +0 -6
- package/lib/cjs/ontologies/npm.js +0 -15
- package/lib/cjs/ontologies/npm.js.map +0 -1
- package/lib/cjs/ontologies/owl.d.ts +0 -14
- package/lib/cjs/ontologies/owl.js +0 -21
- package/lib/cjs/ontologies/owl.js.map +0 -1
- package/lib/cjs/ontologies/rdf.d.ts +0 -13
- package/lib/cjs/ontologies/rdf.js +0 -27
- package/lib/cjs/ontologies/rdf.js.map +0 -1
- package/lib/cjs/ontologies/rdfs.d.ts +0 -16
- package/lib/cjs/ontologies/rdfs.js +0 -33
- package/lib/cjs/ontologies/rdfs.js.map +0 -1
- package/lib/cjs/ontologies/shacl.d.ts +0 -103
- package/lib/cjs/ontologies/shacl.js +0 -152
- package/lib/cjs/ontologies/shacl.js.map +0 -1
- package/lib/cjs/ontologies/xsd.d.ts +0 -19
- package/lib/cjs/ontologies/xsd.js +0 -41
- package/lib/cjs/ontologies/xsd.js.map +0 -1
- package/lib/cjs/package.d.ts +0 -4
- package/lib/cjs/package.js +0 -6
- package/lib/cjs/package.js.map +0 -1
- package/lib/cjs/package.json +0 -9
- package/lib/cjs/paths/PropertyPathExpr.d.ts +0 -38
- package/lib/cjs/paths/PropertyPathExpr.js +0 -221
- package/lib/cjs/paths/PropertyPathExpr.js.map +0 -1
- package/lib/cjs/paths/normalizePropertyPath.d.ts +0 -23
- package/lib/cjs/paths/normalizePropertyPath.js +0 -75
- package/lib/cjs/paths/normalizePropertyPath.js.map +0 -1
- package/lib/cjs/paths/pathExprToSparql.d.ts +0 -14
- package/lib/cjs/paths/pathExprToSparql.js +0 -136
- package/lib/cjs/paths/pathExprToSparql.js.map +0 -1
- package/lib/cjs/queries/ContextRef.d.ts +0 -32
- package/lib/cjs/queries/ContextRef.js +0 -69
- package/lib/cjs/queries/ContextRef.js.map +0 -1
- package/lib/cjs/queries/CreateBuilder.d.ts +0 -59
- package/lib/cjs/queries/CreateBuilder.js +0 -138
- package/lib/cjs/queries/CreateBuilder.js.map +0 -1
- package/lib/cjs/queries/CreateQuery.d.ts +0 -20
- package/lib/cjs/queries/CreateQuery.js +0 -3
- package/lib/cjs/queries/CreateQuery.js.map +0 -1
- package/lib/cjs/queries/DeleteBuilder.d.ts +0 -56
- package/lib/cjs/queries/DeleteBuilder.js +0 -172
- package/lib/cjs/queries/DeleteBuilder.js.map +0 -1
- package/lib/cjs/queries/DeleteQuery.d.ts +0 -37
- package/lib/cjs/queries/DeleteQuery.js +0 -3
- package/lib/cjs/queries/DeleteQuery.js.map +0 -1
- package/lib/cjs/queries/DslJsonExpression.d.ts +0 -68
- package/lib/cjs/queries/DslJsonExpression.js +0 -427
- package/lib/cjs/queries/DslJsonExpression.js.map +0 -1
- package/lib/cjs/queries/FieldSet.d.ts +0 -310
- package/lib/cjs/queries/FieldSet.js +0 -802
- package/lib/cjs/queries/FieldSet.js.map +0 -1
- package/lib/cjs/queries/IRAliasScope.d.ts +0 -20
- package/lib/cjs/queries/IRAliasScope.js +0 -52
- package/lib/cjs/queries/IRAliasScope.js.map +0 -1
- package/lib/cjs/queries/IRCanonicalize.d.ts +0 -19
- package/lib/cjs/queries/IRCanonicalize.js +0 -19
- package/lib/cjs/queries/IRCanonicalize.js.map +0 -1
- package/lib/cjs/queries/IRDesugar.d.ts +0 -123
- package/lib/cjs/queries/IRDesugar.js +0 -261
- package/lib/cjs/queries/IRDesugar.js.map +0 -1
- package/lib/cjs/queries/IRLower.d.ts +0 -25
- package/lib/cjs/queries/IRLower.js +0 -398
- package/lib/cjs/queries/IRLower.js.map +0 -1
- package/lib/cjs/queries/IRMutation.d.ts +0 -53
- package/lib/cjs/queries/IRMutation.js +0 -130
- package/lib/cjs/queries/IRMutation.js.map +0 -1
- package/lib/cjs/queries/IRPipeline.d.ts +0 -8
- package/lib/cjs/queries/IRPipeline.js +0 -25
- package/lib/cjs/queries/IRPipeline.js.map +0 -1
- package/lib/cjs/queries/IRProjection.d.ts +0 -39
- package/lib/cjs/queries/IRProjection.js +0 -105
- package/lib/cjs/queries/IRProjection.js.map +0 -1
- package/lib/cjs/queries/IntermediateRepresentation.d.ts +0 -282
- package/lib/cjs/queries/IntermediateRepresentation.js +0 -3
- package/lib/cjs/queries/IntermediateRepresentation.js.map +0 -1
- package/lib/cjs/queries/MutationQuery.d.ts +0 -50
- package/lib/cjs/queries/MutationQuery.js +0 -334
- package/lib/cjs/queries/MutationQuery.js.map +0 -1
- package/lib/cjs/queries/MutationSerialization.d.ts +0 -102
- package/lib/cjs/queries/MutationSerialization.js +0 -207
- package/lib/cjs/queries/MutationSerialization.js.map +0 -1
- package/lib/cjs/queries/MutationThenable.d.ts +0 -18
- package/lib/cjs/queries/MutationThenable.js +0 -32
- package/lib/cjs/queries/MutationThenable.js.map +0 -1
- package/lib/cjs/queries/PropertyPath.d.ts +0 -38
- package/lib/cjs/queries/PropertyPath.js +0 -82
- package/lib/cjs/queries/PropertyPath.js.map +0 -1
- package/lib/cjs/queries/ProxiedPathBuilder.d.ts +0 -14
- package/lib/cjs/queries/ProxiedPathBuilder.js +0 -29
- package/lib/cjs/queries/ProxiedPathBuilder.js.map +0 -1
- package/lib/cjs/queries/QueryBuilder.d.ts +0 -193
- package/lib/cjs/queries/QueryBuilder.js +0 -519
- package/lib/cjs/queries/QueryBuilder.js.map +0 -1
- package/lib/cjs/queries/QueryBuilderSerialization.d.ts +0 -31
- package/lib/cjs/queries/QueryBuilderSerialization.js +0 -68
- package/lib/cjs/queries/QueryBuilderSerialization.js.map +0 -1
- package/lib/cjs/queries/QueryContext.d.ts +0 -50
- package/lib/cjs/queries/QueryContext.js +0 -147
- package/lib/cjs/queries/QueryContext.js.map +0 -1
- package/lib/cjs/queries/QueryFactory.d.ts +0 -80
- package/lib/cjs/queries/QueryFactory.js +0 -21
- package/lib/cjs/queries/QueryFactory.js.map +0 -1
- package/lib/cjs/queries/SelectQuery.d.ts +0 -302
- package/lib/cjs/queries/SelectQuery.js +0 -761
- package/lib/cjs/queries/SelectQuery.js.map +0 -1
- package/lib/cjs/queries/UpdateBuilder.d.ts +0 -69
- package/lib/cjs/queries/UpdateBuilder.js +0 -199
- package/lib/cjs/queries/UpdateBuilder.js.map +0 -1
- package/lib/cjs/queries/UpdateQuery.d.ts +0 -18
- package/lib/cjs/queries/UpdateQuery.js +0 -3
- package/lib/cjs/queries/UpdateQuery.js.map +0 -1
- package/lib/cjs/queries/fromJSON.d.ts +0 -16
- package/lib/cjs/queries/fromJSON.js +0 -41
- package/lib/cjs/queries/fromJSON.js.map +0 -1
- package/lib/cjs/queries/lower.d.ts +0 -28
- package/lib/cjs/queries/lower.js +0 -114
- package/lib/cjs/queries/lower.js.map +0 -1
- package/lib/cjs/queries/lowerMutationJSON.d.ts +0 -25
- package/lib/cjs/queries/lowerMutationJSON.js +0 -173
- package/lib/cjs/queries/lowerMutationJSON.js.map +0 -1
- package/lib/cjs/queries/mutationLowerSpec.d.ts +0 -32
- package/lib/cjs/queries/mutationLowerSpec.js +0 -8
- package/lib/cjs/queries/mutationLowerSpec.js.map +0 -1
- package/lib/cjs/queries/queryDispatch.d.ts +0 -33
- package/lib/cjs/queries/queryDispatch.js +0 -33
- package/lib/cjs/queries/queryDispatch.js.map +0 -1
- package/lib/cjs/queries/resolveShape.d.ts +0 -10
- package/lib/cjs/queries/resolveShape.js +0 -23
- package/lib/cjs/queries/resolveShape.js.map +0 -1
- package/lib/cjs/queries/wireVersion.d.ts +0 -7
- package/lib/cjs/queries/wireVersion.js +0 -25
- package/lib/cjs/queries/wireVersion.js.map +0 -1
- package/lib/cjs/shapes/List.d.ts +0 -35
- package/lib/cjs/shapes/List.js +0 -72
- package/lib/cjs/shapes/List.js.map +0 -1
- package/lib/cjs/shapes/PathNode.d.ts +0 -20
- package/lib/cjs/shapes/PathNode.js +0 -68
- package/lib/cjs/shapes/PathNode.js.map +0 -1
- package/lib/cjs/shapes/SHACL.d.ts +0 -300
- package/lib/cjs/shapes/SHACL.js +0 -612
- package/lib/cjs/shapes/SHACL.js.map +0 -1
- package/lib/cjs/shapes/Shape.d.ts +0 -74
- package/lib/cjs/shapes/Shape.js +0 -124
- package/lib/cjs/shapes/Shape.js.map +0 -1
- package/lib/cjs/shapes/serializePathToNodeData.d.ts +0 -24
- package/lib/cjs/shapes/serializePathToNodeData.js +0 -67
- package/lib/cjs/shapes/serializePathToNodeData.js.map +0 -1
- package/lib/cjs/shapes/syncShapes.d.ts +0 -38
- package/lib/cjs/shapes/syncShapes.js +0 -214
- package/lib/cjs/shapes/syncShapes.js.map +0 -1
- package/lib/cjs/sparql/SparqlAlgebra.d.ts +0 -190
- package/lib/cjs/sparql/SparqlAlgebra.js +0 -4
- package/lib/cjs/sparql/SparqlAlgebra.js.map +0 -1
- package/lib/cjs/sparql/SparqlDataset.d.ts +0 -60
- package/lib/cjs/sparql/SparqlDataset.js +0 -117
- package/lib/cjs/sparql/SparqlDataset.js.map +0 -1
- package/lib/cjs/sparql/algebraToString.d.ts +0 -13
- package/lib/cjs/sparql/algebraToString.js +0 -385
- package/lib/cjs/sparql/algebraToString.js.map +0 -1
- package/lib/cjs/sparql/index.d.ts +0 -9
- package/lib/cjs/sparql/index.js +0 -41
- package/lib/cjs/sparql/index.js.map +0 -1
- package/lib/cjs/sparql/irToAlgebra.d.ts +0 -79
- package/lib/cjs/sparql/irToAlgebra.js +0 -2042
- package/lib/cjs/sparql/irToAlgebra.js.map +0 -1
- package/lib/cjs/sparql/resultMapping.d.ts +0 -36
- package/lib/cjs/sparql/resultMapping.js +0 -563
- package/lib/cjs/sparql/resultMapping.js.map +0 -1
- package/lib/cjs/sparql/sparqlUtils.d.ts +0 -53
- package/lib/cjs/sparql/sparqlUtils.js +0 -154
- package/lib/cjs/sparql/sparqlUtils.js.map +0 -1
- package/lib/cjs/utils/LinkedErrorLogging.d.ts +0 -13
- package/lib/cjs/utils/LinkedErrorLogging.js +0 -22
- package/lib/cjs/utils/LinkedErrorLogging.js.map +0 -1
- package/lib/cjs/utils/LinkedFileStorage.d.ts +0 -23
- package/lib/cjs/utils/LinkedFileStorage.js +0 -62
- package/lib/cjs/utils/LinkedFileStorage.js.map +0 -1
- package/lib/cjs/utils/LinkedStorage.d.ts +0 -40
- package/lib/cjs/utils/LinkedStorage.js +0 -148
- package/lib/cjs/utils/LinkedStorage.js.map +0 -1
- package/lib/cjs/utils/NameSpace.d.ts +0 -2
- package/lib/cjs/utils/NameSpace.js +0 -8
- package/lib/cjs/utils/NameSpace.js.map +0 -1
- package/lib/cjs/utils/NodeReference.d.ts +0 -30
- package/lib/cjs/utils/NodeReference.js +0 -54
- package/lib/cjs/utils/NodeReference.js.map +0 -1
- package/lib/cjs/utils/Package.d.ts +0 -153
- package/lib/cjs/utils/Package.js +0 -419
- package/lib/cjs/utils/Package.js.map +0 -1
- package/lib/cjs/utils/Prefix.d.ts +0 -22
- package/lib/cjs/utils/Prefix.js +0 -104
- package/lib/cjs/utils/Prefix.js.map +0 -1
- package/lib/cjs/utils/ShapeClass.d.ts +0 -30
- package/lib/cjs/utils/ShapeClass.js +0 -180
- package/lib/cjs/utils/ShapeClass.js.map +0 -1
- package/lib/cjs/utils/URI.d.ts +0 -18
- package/lib/cjs/utils/URI.js +0 -42
- package/lib/cjs/utils/URI.js.map +0 -1
- package/lib/cjs/utils/cached.d.ts +0 -14
- package/lib/cjs/utils/cached.js +0 -71
- package/lib/cjs/utils/cached.js.map +0 -1
- package/lib/cjs/utils/loadStores.d.ts +0 -34
- package/lib/cjs/utils/loadStores.js +0 -105
- package/lib/cjs/utils/loadStores.js.map +0 -1
- package/lib/cjs/utils/parseDatasetsConfig.d.ts +0 -49
- package/lib/cjs/utils/parseDatasetsConfig.js +0 -110
- package/lib/cjs/utils/parseDatasetsConfig.js.map +0 -1
- package/lib/esm/package.json +0 -9
|
@@ -1,2042 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.selectToAlgebra = selectToAlgebra;
|
|
4
|
-
exports.createToAlgebra = createToAlgebra;
|
|
5
|
-
exports.updateToAlgebra = updateToAlgebra;
|
|
6
|
-
exports.buildOwnedCascade = buildOwnedCascade;
|
|
7
|
-
exports.deleteToAlgebra = deleteToAlgebra;
|
|
8
|
-
exports.deleteAllToAlgebra = deleteAllToAlgebra;
|
|
9
|
-
exports.deleteWhereToAlgebra = deleteWhereToAlgebra;
|
|
10
|
-
exports.updateWhereToAlgebra = updateWhereToAlgebra;
|
|
11
|
-
exports.selectToSparql = selectToSparql;
|
|
12
|
-
exports.createToSparql = createToSparql;
|
|
13
|
-
exports.updateToSparql = updateToSparql;
|
|
14
|
-
exports.deleteToSparql = deleteToSparql;
|
|
15
|
-
exports.deleteAllToSparql = deleteAllToSparql;
|
|
16
|
-
exports.deleteWhereToSparql = deleteWhereToSparql;
|
|
17
|
-
exports.updateWhereToSparql = updateWhereToSparql;
|
|
18
|
-
const pathExprToSparql_js_1 = require("../paths/pathExprToSparql.js");
|
|
19
|
-
const sparqlUtils_js_1 = require("./sparqlUtils.js");
|
|
20
|
-
const algebraToString_js_1 = require("./algebraToString.js");
|
|
21
|
-
const rdf_js_1 = require("../ontologies/rdf.js");
|
|
22
|
-
const shacl_js_1 = require("../ontologies/shacl.js");
|
|
23
|
-
const xsd_js_1 = require("../ontologies/xsd.js");
|
|
24
|
-
const normalizePropertyPath_js_1 = require("../paths/normalizePropertyPath.js");
|
|
25
|
-
const ShapeClass_js_1 = require("../utils/ShapeClass.js");
|
|
26
|
-
const QueryContext_js_1 = require("../queries/QueryContext.js");
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Constants
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
const RDF_TYPE = rdf_js_1.rdf.type.id;
|
|
31
|
-
const XSD_DATETIME = xsd_js_1.xsd.dateTime.id;
|
|
32
|
-
const XSD_BOOLEAN = xsd_js_1.xsd.boolean.id;
|
|
33
|
-
const XSD_INTEGER = xsd_js_1.xsd.integer.id;
|
|
34
|
-
const XSD_DOUBLE = xsd_js_1.xsd.double.id;
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// Helpers
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
function iriTerm(value) {
|
|
39
|
-
return { kind: 'iri', value };
|
|
40
|
-
}
|
|
41
|
-
function varTerm(name) {
|
|
42
|
-
return { kind: 'variable', name };
|
|
43
|
-
}
|
|
44
|
-
function literalTerm(value, datatype) {
|
|
45
|
-
if (datatype) {
|
|
46
|
-
return { kind: 'literal', value, datatype };
|
|
47
|
-
}
|
|
48
|
-
return { kind: 'literal', value };
|
|
49
|
-
}
|
|
50
|
-
function tripleOf(subject, predicate, object) {
|
|
51
|
-
return { subject, predicate, object };
|
|
52
|
-
}
|
|
53
|
-
function shouldResolveShapeOrPropertyId(resolvedId) {
|
|
54
|
-
return !!resolvedId && !resolvedId.startsWith('linked://tmp/');
|
|
55
|
-
}
|
|
56
|
-
function resolveShapeScanIri(shapeId) {
|
|
57
|
-
var _a;
|
|
58
|
-
const shapeClass = (0, ShapeClass_js_1.getShapeClass)(shapeId);
|
|
59
|
-
const targetClassId = (_a = shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.targetClass) === null || _a === void 0 ? void 0 : _a.id;
|
|
60
|
-
return shouldResolveShapeOrPropertyId(targetClassId) ? targetClassId : shapeId;
|
|
61
|
-
}
|
|
62
|
-
/**
|
|
63
|
-
* Resolve a SHACL property-shape id to the predicate term used in a triple.
|
|
64
|
-
*
|
|
65
|
-
* - Simple single-IRI path → `{kind:'iri'}` (byte-for-byte unchanged behaviour).
|
|
66
|
-
* - Structured path (sequence / inverse / alternative / …) → `{kind:'path'}` built from the property
|
|
67
|
-
* shape's `sh:path`, reusing the same `pathExprToSparql` / `collectPathUris` machinery as the
|
|
68
|
-
* inline-`pathExpr` branches. Without this, structured named-property paths collapsed to a shadow IRI
|
|
69
|
-
* that matched nothing.
|
|
70
|
-
* - No matching shape / unresolvable → `{kind:'iri'}` of the property id itself (unchanged shadow fallback).
|
|
71
|
-
*/
|
|
72
|
-
// Memoizes resolved predicate terms across the many call sites that resolve the
|
|
73
|
-
// same property. Guarded by the shape-registry size so it self-invalidates when
|
|
74
|
-
// new shapes register (e.g. across test-file module instances). Only successful
|
|
75
|
-
// resolutions are cached — a not-found fallback is never stored, so a predicate
|
|
76
|
-
// resolved before its shape registers can still resolve correctly afterwards.
|
|
77
|
-
const predicateTermCache = new Map();
|
|
78
|
-
let predicateTermCacheSize = -1;
|
|
79
|
-
function resolvePropertyPredicateTerm(propertyId) {
|
|
80
|
-
var _a, _b, _c;
|
|
81
|
-
const shapeClasses = (0, ShapeClass_js_1.getAllShapeClasses)();
|
|
82
|
-
if (shapeClasses.size !== predicateTermCacheSize) {
|
|
83
|
-
predicateTermCache.clear();
|
|
84
|
-
predicateTermCacheSize = shapeClasses.size;
|
|
85
|
-
}
|
|
86
|
-
const cached = predicateTermCache.get(propertyId);
|
|
87
|
-
if (cached)
|
|
88
|
-
return cached;
|
|
89
|
-
for (const shapeClass of shapeClasses.values()) {
|
|
90
|
-
const propertyShape = (_c = (_b = (_a = shapeClass.shape) === null || _a === void 0 ? void 0 : _a.getPropertyShapes) === null || _b === void 0 ? void 0 : _b.call(_a, true)) === null || _c === void 0 ? void 0 : _c.find((prop) => prop.id === propertyId);
|
|
91
|
-
if (!propertyShape)
|
|
92
|
-
continue;
|
|
93
|
-
const simplePathId = (0, normalizePropertyPath_js_1.getSimplePathId)(propertyShape.path);
|
|
94
|
-
let term;
|
|
95
|
-
if (simplePathId !== null) {
|
|
96
|
-
// Simple single-IRI path: resolve to the IRI, or fall back to the property id
|
|
97
|
-
// (e.g. unresolvable `linked://tmp/` ids) — unchanged from before.
|
|
98
|
-
term = iriTerm(shouldResolveShapeOrPropertyId(simplePathId) ? simplePathId : propertyId);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
// Structured sh:path — emit a property-path predicate instead of a shadow IRI.
|
|
102
|
-
term = {
|
|
103
|
-
kind: 'path',
|
|
104
|
-
value: (0, pathExprToSparql_js_1.pathExprToSparql)(propertyShape.path),
|
|
105
|
-
uris: (0, pathExprToSparql_js_1.collectPathUris)(propertyShape.path),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
|
-
predicateTermCache.set(propertyId, term);
|
|
109
|
-
return term;
|
|
110
|
-
}
|
|
111
|
-
return iriTerm(propertyId);
|
|
112
|
-
}
|
|
113
|
-
/**
|
|
114
|
-
* Resolve the predicate term for a traversal/property node: a structured
|
|
115
|
-
* property-path when an explicit `pathExpr` is present, otherwise the
|
|
116
|
-
* registry-resolved predicate for `property`. Centralizes the invariant that
|
|
117
|
-
* these two branches stay in lockstep across every triple-building site.
|
|
118
|
-
*/
|
|
119
|
-
function buildPredicateTerm(spec) {
|
|
120
|
-
return spec.pathExpr
|
|
121
|
-
? {
|
|
122
|
-
kind: 'path',
|
|
123
|
-
value: (0, pathExprToSparql_js_1.pathExprToSparql)(spec.pathExpr),
|
|
124
|
-
uris: (0, pathExprToSparql_js_1.collectPathUris)(spec.pathExpr),
|
|
125
|
-
}
|
|
126
|
-
: resolvePropertyPredicateTerm(spec.property);
|
|
127
|
-
}
|
|
128
|
-
/** Produce variable name suffix from the last segment of a property URI. */
|
|
129
|
-
function propertySuffix(propertyUri) {
|
|
130
|
-
const hashIdx = propertyUri.lastIndexOf('#');
|
|
131
|
-
if (hashIdx >= 0)
|
|
132
|
-
return propertyUri.substring(hashIdx + 1);
|
|
133
|
-
const slashIdx = propertyUri.lastIndexOf('/');
|
|
134
|
-
return slashIdx >= 0 ? propertyUri.substring(slashIdx + 1) : propertyUri;
|
|
135
|
-
}
|
|
136
|
-
/**
|
|
137
|
-
* Sanitize a string so it's valid in a SPARQL variable name.
|
|
138
|
-
* Replaces any non-alphanumeric/underscore characters with underscores.
|
|
139
|
-
*/
|
|
140
|
-
function sanitizeVarName(name) {
|
|
141
|
-
return name.replace(/[^A-Za-z0-9_]/g, '_');
|
|
142
|
-
}
|
|
143
|
-
const IR_EXPRESSION_KINDS = new Set([
|
|
144
|
-
'literal_expr', 'property_expr', 'binary_expr', 'logical_expr',
|
|
145
|
-
'not_expr', 'function_expr', 'aggregate_expr', 'reference_expr',
|
|
146
|
-
'alias_expr', 'context_property_expr', 'exists_expr', 'in_expr',
|
|
147
|
-
]);
|
|
148
|
-
function isIRExpression(value) {
|
|
149
|
-
return !!value && typeof value === 'object' && 'kind' in value &&
|
|
150
|
-
typeof value.kind === 'string' &&
|
|
151
|
-
IR_EXPRESSION_KINDS.has(value.kind);
|
|
152
|
-
}
|
|
153
|
-
/**
|
|
154
|
-
* Wrap a single node in a LeftJoin, making `right` optional relative to `left`.
|
|
155
|
-
*/
|
|
156
|
-
function wrapOptional(left, right) {
|
|
157
|
-
return { type: 'left_join', left, right };
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Join two algebra nodes. If left is null, returns right.
|
|
161
|
-
*/
|
|
162
|
-
function joinNodes(left, right) {
|
|
163
|
-
if (!left)
|
|
164
|
-
return right;
|
|
165
|
-
return { type: 'join', left, right };
|
|
166
|
-
}
|
|
167
|
-
function bindingKey(alias, property) {
|
|
168
|
-
return `${alias}::${property}`;
|
|
169
|
-
}
|
|
170
|
-
function contextAliasKey(contextIri) {
|
|
171
|
-
return `__ctx__${contextIri}`;
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Defense-in-depth: a `reference_expr`/`context_property_expr` must arrive here with its
|
|
175
|
-
* IRI already resolved (lowering's `resolveContextRefs` fills it from `contextName`). If it
|
|
176
|
-
* didn't — an unresolved `{@ctx}` reaching the algebra through some path that bypassed
|
|
177
|
-
* resolution — fail with a clear `UnresolvedContextError` instead of an opaque
|
|
178
|
-
* `undefined.substring` crash deeper in URI formatting.
|
|
179
|
-
*/
|
|
180
|
-
function resolvedContextIri(iri, contextName) {
|
|
181
|
-
if (iri === undefined) {
|
|
182
|
-
throw new QueryContext_js_1.UnresolvedContextError(contextName !== null && contextName !== void 0 ? contextName : '<unknown>');
|
|
183
|
-
}
|
|
184
|
-
return iri;
|
|
185
|
-
}
|
|
186
|
-
function mergeKeySets(...sets) {
|
|
187
|
-
const merged = new Set();
|
|
188
|
-
for (const set of sets) {
|
|
189
|
-
for (const key of set) {
|
|
190
|
-
merged.add(key);
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
return merged;
|
|
194
|
-
}
|
|
195
|
-
function intersectKeySets(sets) {
|
|
196
|
-
if (sets.length === 0) {
|
|
197
|
-
return new Set();
|
|
198
|
-
}
|
|
199
|
-
const [first, ...rest] = sets;
|
|
200
|
-
const intersection = new Set(first);
|
|
201
|
-
for (const value of intersection) {
|
|
202
|
-
if (!rest.every((set) => set.has(value))) {
|
|
203
|
-
intersection.delete(value);
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
return intersection;
|
|
207
|
-
}
|
|
208
|
-
// ---------------------------------------------------------------------------
|
|
209
|
-
// Pattern helpers
|
|
210
|
-
// ---------------------------------------------------------------------------
|
|
211
|
-
/**
|
|
212
|
-
* Recursively collects all traversal alias target variables from IR patterns.
|
|
213
|
-
* Used to ensure traversal aliases appear in the SELECT projection for result grouping.
|
|
214
|
-
*/
|
|
215
|
-
function collectTraversalAliases(patterns) {
|
|
216
|
-
const aliases = [];
|
|
217
|
-
for (const p of patterns) {
|
|
218
|
-
if (p.kind === 'traverse') {
|
|
219
|
-
aliases.push(p.to);
|
|
220
|
-
}
|
|
221
|
-
else if (p.kind === 'join') {
|
|
222
|
-
aliases.push(...collectTraversalAliases(p.patterns));
|
|
223
|
-
}
|
|
224
|
-
else if (p.kind === 'optional') {
|
|
225
|
-
aliases.push(...collectTraversalAliases([p.pattern]));
|
|
226
|
-
}
|
|
227
|
-
else if (p.kind === 'union') {
|
|
228
|
-
for (const branch of p.branches) {
|
|
229
|
-
aliases.push(...collectTraversalAliases([branch]));
|
|
230
|
-
}
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
|
-
return aliases;
|
|
234
|
-
}
|
|
235
|
-
function buildTraverseTriple(pattern) {
|
|
236
|
-
const predicate = buildPredicateTerm(pattern);
|
|
237
|
-
return tripleOf(varTerm(pattern.from), predicate, varTerm(pattern.to));
|
|
238
|
-
}
|
|
239
|
-
function collectTraversePatternsInOrder(patterns, out = []) {
|
|
240
|
-
for (const pattern of patterns) {
|
|
241
|
-
switch (pattern.kind) {
|
|
242
|
-
case 'traverse':
|
|
243
|
-
out.push(pattern);
|
|
244
|
-
break;
|
|
245
|
-
case 'join':
|
|
246
|
-
collectTraversePatternsInOrder(pattern.patterns, out);
|
|
247
|
-
break;
|
|
248
|
-
case 'optional':
|
|
249
|
-
case 'exists':
|
|
250
|
-
collectTraversePatternsInOrder([pattern.pattern], out);
|
|
251
|
-
break;
|
|
252
|
-
case 'union':
|
|
253
|
-
for (const branch of pattern.branches) {
|
|
254
|
-
collectTraversePatternsInOrder([branch], out);
|
|
255
|
-
}
|
|
256
|
-
break;
|
|
257
|
-
case 'shape_scan':
|
|
258
|
-
case 'minus':
|
|
259
|
-
break;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return out;
|
|
263
|
-
}
|
|
264
|
-
function collectDirectProjectionAliases(query, rootAlias) {
|
|
265
|
-
const aliases = new Set();
|
|
266
|
-
for (const item of query.projection) {
|
|
267
|
-
if (item.expression.kind === 'property_expr') {
|
|
268
|
-
if (item.expression.sourceAlias !== rootAlias) {
|
|
269
|
-
aliases.add(item.expression.sourceAlias);
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
else if (item.expression.kind === 'alias_expr') {
|
|
273
|
-
if (item.expression.alias !== rootAlias) {
|
|
274
|
-
aliases.add(item.expression.alias);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
return aliases;
|
|
279
|
-
}
|
|
280
|
-
function collectExpressionAliases(expr, aliases) {
|
|
281
|
-
switch (expr.kind) {
|
|
282
|
-
case 'property_expr':
|
|
283
|
-
aliases.add(expr.sourceAlias);
|
|
284
|
-
break;
|
|
285
|
-
case 'alias_expr':
|
|
286
|
-
aliases.add(expr.alias);
|
|
287
|
-
break;
|
|
288
|
-
case 'binary_expr':
|
|
289
|
-
collectExpressionAliases(expr.left, aliases);
|
|
290
|
-
collectExpressionAliases(expr.right, aliases);
|
|
291
|
-
break;
|
|
292
|
-
case 'in_expr':
|
|
293
|
-
collectExpressionAliases(expr.value, aliases);
|
|
294
|
-
for (const el of expr.source.list)
|
|
295
|
-
collectExpressionAliases(el, aliases);
|
|
296
|
-
break;
|
|
297
|
-
case 'logical_expr':
|
|
298
|
-
for (const sub of expr.expressions) {
|
|
299
|
-
collectExpressionAliases(sub, aliases);
|
|
300
|
-
}
|
|
301
|
-
break;
|
|
302
|
-
case 'not_expr':
|
|
303
|
-
collectExpressionAliases(expr.expression, aliases);
|
|
304
|
-
break;
|
|
305
|
-
case 'function_expr':
|
|
306
|
-
case 'aggregate_expr':
|
|
307
|
-
for (const arg of expr.args) {
|
|
308
|
-
collectExpressionAliases(arg, aliases);
|
|
309
|
-
}
|
|
310
|
-
break;
|
|
311
|
-
case 'exists_expr':
|
|
312
|
-
if (expr.filter) {
|
|
313
|
-
collectExpressionAliases(expr.filter, aliases);
|
|
314
|
-
}
|
|
315
|
-
break;
|
|
316
|
-
case 'literal_expr':
|
|
317
|
-
case 'reference_expr':
|
|
318
|
-
case 'context_property_expr':
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
function markAliasAndAncestors(alias, traversePatternMap, target) {
|
|
323
|
-
var _a;
|
|
324
|
-
let currentAlias = alias;
|
|
325
|
-
while (currentAlias && traversePatternMap.has(currentAlias)) {
|
|
326
|
-
if (target.has(currentAlias)) {
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
target.add(currentAlias);
|
|
330
|
-
currentAlias = (_a = traversePatternMap.get(currentAlias)) === null || _a === void 0 ? void 0 : _a.from;
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
function collectRequiredTraversalAliases(query, traversePatternMap) {
|
|
334
|
-
const directAliases = new Set();
|
|
335
|
-
if (query.where) {
|
|
336
|
-
collectExpressionAliases(query.where, directAliases);
|
|
337
|
-
}
|
|
338
|
-
if (query.orderBy) {
|
|
339
|
-
for (const item of query.orderBy) {
|
|
340
|
-
collectExpressionAliases(item.expression, directAliases);
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
for (const pattern of traversePatternMap.values()) {
|
|
344
|
-
if (pattern.filter) {
|
|
345
|
-
directAliases.add(pattern.to);
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
const requiredAliases = new Set();
|
|
349
|
-
for (const alias of directAliases) {
|
|
350
|
-
markAliasAndAncestors(alias, traversePatternMap, requiredAliases);
|
|
351
|
-
}
|
|
352
|
-
return requiredAliases;
|
|
353
|
-
}
|
|
354
|
-
function buildOptionalTraversalSubtree(alias, traversePatternMap, childOptionalAliasesByParent, propertyTriplesByAlias) {
|
|
355
|
-
var _a, _b;
|
|
356
|
-
const pattern = traversePatternMap.get(alias);
|
|
357
|
-
if (!pattern) {
|
|
358
|
-
throw new Error(`Missing traverse pattern for alias "${alias}"`);
|
|
359
|
-
}
|
|
360
|
-
let subtree = {
|
|
361
|
-
type: 'bgp',
|
|
362
|
-
triples: [buildTraverseTriple(pattern)],
|
|
363
|
-
};
|
|
364
|
-
for (const propTriple of (_a = propertyTriplesByAlias.get(alias)) !== null && _a !== void 0 ? _a : []) {
|
|
365
|
-
subtree = wrapOptional(subtree, {
|
|
366
|
-
type: 'bgp',
|
|
367
|
-
triples: [propTriple],
|
|
368
|
-
});
|
|
369
|
-
}
|
|
370
|
-
for (const childAlias of (_b = childOptionalAliasesByParent.get(alias)) !== null && _b !== void 0 ? _b : []) {
|
|
371
|
-
subtree = wrapOptional(subtree, buildOptionalTraversalSubtree(childAlias, traversePatternMap, childOptionalAliasesByParent, propertyTriplesByAlias));
|
|
372
|
-
}
|
|
373
|
-
return subtree;
|
|
374
|
-
}
|
|
375
|
-
// ---------------------------------------------------------------------------
|
|
376
|
-
// Variable Registry
|
|
377
|
-
// ---------------------------------------------------------------------------
|
|
378
|
-
/**
|
|
379
|
-
* Maps (alias, property) → SPARQL variable name.
|
|
380
|
-
* Used to deduplicate variables across traverse and property_expr nodes.
|
|
381
|
-
*/
|
|
382
|
-
class VariableRegistry {
|
|
383
|
-
constructor() {
|
|
384
|
-
this.map = new Map();
|
|
385
|
-
this.usedVarNames = new Set();
|
|
386
|
-
}
|
|
387
|
-
key(alias, property) {
|
|
388
|
-
return bindingKey(alias, property);
|
|
389
|
-
}
|
|
390
|
-
has(alias, property) {
|
|
391
|
-
return this.map.has(this.key(alias, property));
|
|
392
|
-
}
|
|
393
|
-
get(alias, property) {
|
|
394
|
-
return this.map.get(this.key(alias, property));
|
|
395
|
-
}
|
|
396
|
-
set(alias, property, variable) {
|
|
397
|
-
this.map.set(this.key(alias, property), variable);
|
|
398
|
-
this.usedVarNames.add(variable);
|
|
399
|
-
}
|
|
400
|
-
getOrCreate(alias, property) {
|
|
401
|
-
const existing = this.get(alias, property);
|
|
402
|
-
if (existing)
|
|
403
|
-
return existing;
|
|
404
|
-
const suffix = propertySuffix(property);
|
|
405
|
-
let varName = `${sanitizeVarName(alias)}_${suffix}`;
|
|
406
|
-
// Deduplicate: if varName is already used by a different (alias, property),
|
|
407
|
-
// append a counter to ensure unique SPARQL variable names
|
|
408
|
-
let counter = 2;
|
|
409
|
-
while (this.usedVarNames.has(varName)) {
|
|
410
|
-
varName = `${sanitizeVarName(alias)}_${suffix}_${counter}`;
|
|
411
|
-
counter++;
|
|
412
|
-
}
|
|
413
|
-
this.set(alias, property, varName);
|
|
414
|
-
return varName;
|
|
415
|
-
}
|
|
416
|
-
}
|
|
417
|
-
// ---------------------------------------------------------------------------
|
|
418
|
-
// Aggregate detection
|
|
419
|
-
// ---------------------------------------------------------------------------
|
|
420
|
-
/**
|
|
421
|
-
* Checks whether a SparqlExpression tree contains an aggregate sub-expression.
|
|
422
|
-
* Used to route aggregate-containing filters to HAVING instead of FILTER.
|
|
423
|
-
*/
|
|
424
|
-
function containsAggregate(expr) {
|
|
425
|
-
switch (expr.kind) {
|
|
426
|
-
case 'aggregate_expr':
|
|
427
|
-
return true;
|
|
428
|
-
case 'in_expr':
|
|
429
|
-
return containsAggregate(expr.value) || expr.list.some(containsAggregate);
|
|
430
|
-
case 'binary_expr':
|
|
431
|
-
return containsAggregate(expr.left) || containsAggregate(expr.right);
|
|
432
|
-
case 'logical_expr':
|
|
433
|
-
return expr.exprs.some(containsAggregate);
|
|
434
|
-
case 'not_expr':
|
|
435
|
-
return containsAggregate(expr.inner);
|
|
436
|
-
case 'function_expr':
|
|
437
|
-
return expr.args.some(containsAggregate);
|
|
438
|
-
default:
|
|
439
|
-
return false;
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
// ---------------------------------------------------------------------------
|
|
443
|
-
// Select conversion
|
|
444
|
-
// ---------------------------------------------------------------------------
|
|
445
|
-
/**
|
|
446
|
-
* Converts an IRSelectQuery to a SparqlSelectPlan.
|
|
447
|
-
*/
|
|
448
|
-
function selectToAlgebra(query, _options) {
|
|
449
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
450
|
-
const registry = new VariableRegistry();
|
|
451
|
-
// Promote bindings only when the top-level WHERE would reject rows without
|
|
452
|
-
// them. This keeps human-like SPARQL for null-rejecting filters without
|
|
453
|
-
// over-constraining OR cases that can still match through other branches.
|
|
454
|
-
const requiredPropertyKeys = query.where
|
|
455
|
-
? collectRequiredBindingKeys(query.where)
|
|
456
|
-
: new Set();
|
|
457
|
-
const requiredPropertyTriples = [];
|
|
458
|
-
// Track property triples that need to be added as OPTIONAL
|
|
459
|
-
const optionalPropertyTriples = [];
|
|
460
|
-
// Track filtered traversals (inline where) — these get their own OPTIONAL blocks
|
|
461
|
-
const filteredTraverseBlocks = [];
|
|
462
|
-
// 1. Root shape scan → BGP with type triple
|
|
463
|
-
if (!(query === null || query === void 0 ? void 0 : query.root)) {
|
|
464
|
-
throw new Error('selectToAlgebra: query.root is undefined. The query IR is missing its root shape scan. ' +
|
|
465
|
-
'This usually means the query was built with a null/undefined subject (e.g. getQueryContext returned null).');
|
|
466
|
-
}
|
|
467
|
-
const rootAlias = query.root.alias;
|
|
468
|
-
const shapeUri = query.root.shape;
|
|
469
|
-
const typeTriple = tripleOf(varTerm(rootAlias), iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(shapeUri)));
|
|
470
|
-
const requiredTriples = [typeTriple];
|
|
471
|
-
// Track traverse triples (required pattern)
|
|
472
|
-
const traverseTriples = [];
|
|
473
|
-
const traversePatternsInOrder = collectTraversePatternsInOrder(query.patterns);
|
|
474
|
-
const traversePatternMap = new Map(traversePatternsInOrder.map((pattern) => [pattern.to, pattern]));
|
|
475
|
-
// ── Nested-select inner LIMIT/OFFSET (Option A: single-subject only) ──────
|
|
476
|
-
// A nested select on a related collection may carry an inner LIMIT/OFFSET.
|
|
477
|
-
// A plain SPARQL sub-SELECT is uncorrelated, so its LIMIT bounds GLOBALLY.
|
|
478
|
-
// That only equals per-parent windowing when the outer query targets exactly
|
|
479
|
-
// ONE root subject — then we inline that subject into the sub-SELECT.
|
|
480
|
-
const paginatedTraversePatterns = traversePatternsInOrder.filter((p) => typeof p.innerLimit === 'number' ||
|
|
481
|
-
typeof p.innerOffset === 'number' ||
|
|
482
|
-
(p.innerOrderBy && p.innerOrderBy.length > 0));
|
|
483
|
-
const singleSubjectIri = (_a = query.subjectId) !== null && _a !== void 0 ? _a : (query.subjectIds && query.subjectIds.length === 1
|
|
484
|
-
? query.subjectIds[0]
|
|
485
|
-
: undefined);
|
|
486
|
-
if (paginatedTraversePatterns.length > 0 && !singleSubjectIri) {
|
|
487
|
-
throw new Error('Inner LIMIT/OFFSET on a nested select is only supported when the outer ' +
|
|
488
|
-
'query targets a single subject; multi-parent per-group pagination is not ' +
|
|
489
|
-
'yet implemented.');
|
|
490
|
-
}
|
|
491
|
-
// Only root→child traversals can be inlined against the single subject.
|
|
492
|
-
// A paginated traversal deeper than root→child has a parent collection that is
|
|
493
|
-
// itself multi-valued (effectively multi-parent), so a sub-SELECT LIMIT there
|
|
494
|
-
// would bound globally, not per-parent. Reject it loudly rather than silently
|
|
495
|
-
// dropping the inner limit.
|
|
496
|
-
const deepPaginated = paginatedTraversePatterns.filter((p) => p.from !== rootAlias);
|
|
497
|
-
if (deepPaginated.length > 0) {
|
|
498
|
-
throw new Error('Inner LIMIT/OFFSET on a nested select is only supported on a direct nested ' +
|
|
499
|
-
'select of the single root subject; pagination on a deeper (grandchild) ' +
|
|
500
|
-
'collection is multi-parent and not yet implemented.');
|
|
501
|
-
}
|
|
502
|
-
const subSelectAliases = new Set(paginatedTraversePatterns.map((p) => p.to));
|
|
503
|
-
// 2. Process patterns → traverse triples, populate variable registry.
|
|
504
|
-
// Skip sub-SELECT-wrapped traversals here — they are emitted separately
|
|
505
|
-
// (their child variable is still registered so projection/optionals work).
|
|
506
|
-
for (const pattern of query.patterns) {
|
|
507
|
-
processPattern(pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases);
|
|
508
|
-
}
|
|
509
|
-
// 3. Pre-register filter property references BEFORE processing projections.
|
|
510
|
-
// This ensures that property triples needed by inline where filters are
|
|
511
|
-
// co-located inside the filtered OPTIONAL block, not in separate OPTIONALs.
|
|
512
|
-
const filterPropertyTriplesMap = new Map();
|
|
513
|
-
filteredTraverseBlocks.forEach((block, idx) => {
|
|
514
|
-
const filterPropertyTriples = [];
|
|
515
|
-
processExpressionForProperties(block.filter, registry, filterPropertyTriples);
|
|
516
|
-
filterPropertyTriplesMap.set(idx, filterPropertyTriples);
|
|
517
|
-
});
|
|
518
|
-
// 4. Process projection expressions, where clause, orderBy expressions
|
|
519
|
-
// to discover any additional property_expr references.
|
|
520
|
-
// Properties already registered by inline filters (above) will be skipped.
|
|
521
|
-
for (const item of query.projection) {
|
|
522
|
-
processExpressionForProperties(item.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
523
|
-
}
|
|
524
|
-
if (query.where) {
|
|
525
|
-
processExpressionForProperties(query.where, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
526
|
-
}
|
|
527
|
-
if (query.orderBy) {
|
|
528
|
-
for (const orderItem of query.orderBy) {
|
|
529
|
-
processExpressionForProperties(orderItem.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
530
|
-
}
|
|
531
|
-
}
|
|
532
|
-
const projectedTraversalAliases = new Set();
|
|
533
|
-
const directProjectionAliases = collectDirectProjectionAliases(query, rootAlias);
|
|
534
|
-
for (const alias of directProjectionAliases) {
|
|
535
|
-
markAliasAndAncestors(alias, traversePatternMap, projectedTraversalAliases);
|
|
536
|
-
}
|
|
537
|
-
const requiredTraversalAliases = collectRequiredTraversalAliases(query, traversePatternMap);
|
|
538
|
-
const optionalTraversalAliases = new Set([...projectedTraversalAliases].filter((alias) => {
|
|
539
|
-
const pattern = traversePatternMap.get(alias);
|
|
540
|
-
// Lower projection-only traversals into OPTIONAL subtrees regardless of
|
|
541
|
-
// cardinality, so a parent with an empty relationship is preserved (with
|
|
542
|
-
// an empty array / null child) instead of being inner-joined away. The
|
|
543
|
-
// result grouper already collects multiple child bindings into an array.
|
|
544
|
-
//
|
|
545
|
-
// Excluded:
|
|
546
|
-
// - filtered traversals (`.where(...)`) — the filter makes the child required
|
|
547
|
-
// - aliases otherwise required by the query (e.g. used in a top-level filter)
|
|
548
|
-
// - paginated traversals — emitted separately as a sub-SELECT (section 5a),
|
|
549
|
-
// so lowering them here too would double-emit the traverse triple
|
|
550
|
-
return !!pattern &&
|
|
551
|
-
!pattern.filter &&
|
|
552
|
-
!subSelectAliases.has(alias) &&
|
|
553
|
-
!requiredTraversalAliases.has(alias);
|
|
554
|
-
}));
|
|
555
|
-
const requiredTraverseTriples = traverseTriples.filter((triple) => !(triple.object.kind === 'variable' &&
|
|
556
|
-
optionalTraversalAliases.has(triple.object.name)));
|
|
557
|
-
const nestedOptionalPropertyTriplesByAlias = new Map();
|
|
558
|
-
const topLevelOptionalPropertyTriples = [];
|
|
559
|
-
// Property triples whose subject is a sub-SELECT child alias must be nested
|
|
560
|
-
// INSIDE the sub-SELECT's OPTIONAL block (keyed by alias), so that ?childVar
|
|
561
|
-
// stays scoped — otherwise an empty window leaves ?childVar unbound and the
|
|
562
|
-
// outer property OPTIONAL leaks across the whole graph.
|
|
563
|
-
const subSelectChildPropertyTriplesByAlias = new Map();
|
|
564
|
-
// Same scoping rule for filtered traversals (`.where(...)`): anything anchored
|
|
565
|
-
// on the filtered alias must live inside its filtered OPTIONAL block — when
|
|
566
|
-
// the filter matches nothing, the alias is unbound and a top-level OPTIONAL
|
|
567
|
-
// would range over the whole graph.
|
|
568
|
-
const filteredTraverseAliases = new Set(filteredTraverseBlocks.map((b) => b.toAlias));
|
|
569
|
-
const filteredChildPropertyTriplesByAlias = new Map();
|
|
570
|
-
for (const propTriple of optionalPropertyTriples) {
|
|
571
|
-
if (propTriple.subject.kind === 'variable' &&
|
|
572
|
-
subSelectAliases.has(propTriple.subject.name)) {
|
|
573
|
-
const triples = (_b = subSelectChildPropertyTriplesByAlias.get(propTriple.subject.name)) !== null && _b !== void 0 ? _b : [];
|
|
574
|
-
triples.push(propTriple);
|
|
575
|
-
subSelectChildPropertyTriplesByAlias.set(propTriple.subject.name, triples);
|
|
576
|
-
}
|
|
577
|
-
else if (propTriple.subject.kind === 'variable' &&
|
|
578
|
-
filteredTraverseAliases.has(propTriple.subject.name)) {
|
|
579
|
-
const triples = (_c = filteredChildPropertyTriplesByAlias.get(propTriple.subject.name)) !== null && _c !== void 0 ? _c : [];
|
|
580
|
-
triples.push(propTriple);
|
|
581
|
-
filteredChildPropertyTriplesByAlias.set(propTriple.subject.name, triples);
|
|
582
|
-
}
|
|
583
|
-
else if (propTriple.subject.kind === 'variable' &&
|
|
584
|
-
optionalTraversalAliases.has(propTriple.subject.name)) {
|
|
585
|
-
const triples = (_d = nestedOptionalPropertyTriplesByAlias.get(propTriple.subject.name)) !== null && _d !== void 0 ? _d : [];
|
|
586
|
-
triples.push(propTriple);
|
|
587
|
-
nestedOptionalPropertyTriplesByAlias.set(propTriple.subject.name, triples);
|
|
588
|
-
}
|
|
589
|
-
else {
|
|
590
|
-
topLevelOptionalPropertyTriples.push(propTriple);
|
|
591
|
-
}
|
|
592
|
-
}
|
|
593
|
-
const childOptionalAliasesByParent = new Map();
|
|
594
|
-
for (const pattern of traversePatternsInOrder) {
|
|
595
|
-
if (!optionalTraversalAliases.has(pattern.to))
|
|
596
|
-
continue;
|
|
597
|
-
const siblings = (_e = childOptionalAliasesByParent.get(pattern.from)) !== null && _e !== void 0 ? _e : [];
|
|
598
|
-
siblings.push(pattern.to);
|
|
599
|
-
childOptionalAliasesByParent.set(pattern.from, siblings);
|
|
600
|
-
}
|
|
601
|
-
const rootOptionalTraversalAliases = traversePatternsInOrder
|
|
602
|
-
.filter((pattern) => optionalTraversalAliases.has(pattern.to) &&
|
|
603
|
-
!optionalTraversalAliases.has(pattern.from) &&
|
|
604
|
-
// Children of a filtered traversal nest inside its filtered block (5b)
|
|
605
|
-
!filteredTraverseAliases.has(pattern.from))
|
|
606
|
-
.map((pattern) => pattern.to);
|
|
607
|
-
// 5. Build the algebra tree
|
|
608
|
-
// - Start with the required BGP (type triple + traverse triples)
|
|
609
|
-
// - Wrap each optional property triple in a LeftJoin
|
|
610
|
-
const requiredBgp = {
|
|
611
|
-
type: 'bgp',
|
|
612
|
-
triples: [...requiredTriples, ...requiredTraverseTriples, ...requiredPropertyTriples],
|
|
613
|
-
};
|
|
614
|
-
let algebra = requiredBgp;
|
|
615
|
-
// 5a. Nested-select inner LIMIT/OFFSET → sub-SELECT (single-subject only).
|
|
616
|
-
// Wrap each root→child paginated traversal in a sub-SELECT that inlines
|
|
617
|
-
// the single subject, projecting only the child variable. The child's own
|
|
618
|
-
// property triples stay OUTSIDE (as OPTIONALs), joined on the child var —
|
|
619
|
-
// so the flat-row structure and result mapping are unchanged.
|
|
620
|
-
// Every pattern here is a root→child paginated traversal (the deep-pagination
|
|
621
|
-
// guard above rejected anything else), so each maps 1:1 to a sub-SELECT.
|
|
622
|
-
for (const pattern of paginatedTraversePatterns) {
|
|
623
|
-
const predicate = buildPredicateTerm(pattern);
|
|
624
|
-
const innerTriple = tripleOf(iriTerm(singleSubjectIri), predicate, varTerm(pattern.to));
|
|
625
|
-
// Default ORDER BY ?childVar for a deterministic window, unless the nested
|
|
626
|
-
// select supplied its own ordering. When it did, each ordered property must
|
|
627
|
-
// also be bound inside the sub-SELECT so the ORDER BY variable resolves.
|
|
628
|
-
let orderBy;
|
|
629
|
-
let innerNode = { type: 'bgp', triples: [innerTriple] };
|
|
630
|
-
if (pattern.innerOrderBy && pattern.innerOrderBy.length > 0) {
|
|
631
|
-
orderBy = pattern.innerOrderBy.map((o) => {
|
|
632
|
-
const orderVar = registry.getOrCreate(pattern.to, o.property);
|
|
633
|
-
innerNode = joinNodes(innerNode, {
|
|
634
|
-
type: 'bgp',
|
|
635
|
-
triples: [tripleOf(varTerm(pattern.to), resolvePropertyPredicateTerm(o.property), varTerm(orderVar))],
|
|
636
|
-
});
|
|
637
|
-
return {
|
|
638
|
-
expression: { kind: 'variable_expr', name: orderVar },
|
|
639
|
-
direction: o.direction,
|
|
640
|
-
};
|
|
641
|
-
});
|
|
642
|
-
}
|
|
643
|
-
else {
|
|
644
|
-
orderBy = [{
|
|
645
|
-
expression: { kind: 'variable_expr', name: pattern.to },
|
|
646
|
-
direction: 'ASC',
|
|
647
|
-
}];
|
|
648
|
-
}
|
|
649
|
-
const subSelect = Object.assign(Object.assign({ type: 'subselect', projection: [pattern.to], inner: innerNode, orderBy }, (typeof pattern.innerLimit === 'number' ? { limit: pattern.innerLimit } : {})), (typeof pattern.innerOffset === 'number' ? { offset: pattern.innerOffset } : {}));
|
|
650
|
-
// Nest the child's own property triples (e.g. ?a1 <name> ?a1_name) INSIDE
|
|
651
|
-
// this block, each as its own OPTIONAL, so they only bind when the window
|
|
652
|
-
// produced a ?childVar. This keeps the flat-row projection unchanged.
|
|
653
|
-
let childBlock = subSelect;
|
|
654
|
-
for (const propTriple of (_f = subSelectChildPropertyTriplesByAlias.get(pattern.to)) !== null && _f !== void 0 ? _f : []) {
|
|
655
|
-
childBlock = wrapOptional(childBlock, { type: 'bgp', triples: [propTriple] });
|
|
656
|
-
}
|
|
657
|
-
// OPTIONAL so a parent with an empty (or fully-windowed-out) child set is
|
|
658
|
-
// still returned — the sub-SELECT yields no ?a1 binding, but the parent row
|
|
659
|
-
// survives (mirrors the plain nested-traverse OPTIONAL behaviour).
|
|
660
|
-
algebra = wrapOptional(algebra, childBlock);
|
|
661
|
-
}
|
|
662
|
-
// 5b. Build filtered OPTIONAL blocks for inline where traversals.
|
|
663
|
-
// Each block contains: traverse triple + OPTIONAL property triples + FILTER.
|
|
664
|
-
// Filter property triples are nested as OPTIONALs so that OR filters work
|
|
665
|
-
// even when some entities lack certain properties.
|
|
666
|
-
// First pass: build each filtered block's inner group and filter expression.
|
|
667
|
-
const filteredBlockInners = [];
|
|
668
|
-
const filteredBlockExprs = [];
|
|
669
|
-
const filteredBlockIdxByAlias = new Map(filteredTraverseBlocks.map((block, i) => [block.toAlias, i]));
|
|
670
|
-
for (let i = 0; i < filteredTraverseBlocks.length; i++) {
|
|
671
|
-
const block = filteredTraverseBlocks[i];
|
|
672
|
-
const filterPropertyTriples = filterPropertyTriplesMap.get(i) || [];
|
|
673
|
-
const filterExpr = convertExpression(block.filter, registry, filterPropertyTriples);
|
|
674
|
-
// Start with the traverse triple as the required BGP
|
|
675
|
-
let blockInner = { type: 'bgp', triples: [block.traverseTriple] };
|
|
676
|
-
// Wrap each filter property triple in its own nested OPTIONAL
|
|
677
|
-
for (const propTriple of filterPropertyTriples) {
|
|
678
|
-
blockInner = wrapOptional(blockInner, { type: 'bgp', triples: [propTriple] });
|
|
679
|
-
}
|
|
680
|
-
// Projected property triples on the filtered alias that aren't part of the
|
|
681
|
-
// filter (e.g. `.where(name=...).select(pp => [pp.hobby])`)
|
|
682
|
-
for (const propTriple of (_g = filteredChildPropertyTriplesByAlias.get(block.toAlias)) !== null && _g !== void 0 ? _g : []) {
|
|
683
|
-
blockInner = wrapOptional(blockInner, { type: 'bgp', triples: [propTriple] });
|
|
684
|
-
}
|
|
685
|
-
// Optional child traversal subtrees (nested sub-selects below the filtered
|
|
686
|
-
// alias) — kept inside the block so an empty filter match leaves them unbound
|
|
687
|
-
for (const childAlias of (_h = childOptionalAliasesByParent.get(block.toAlias)) !== null && _h !== void 0 ? _h : []) {
|
|
688
|
-
blockInner = wrapOptional(blockInner, buildOptionalTraversalSubtree(childAlias, traversePatternMap, childOptionalAliasesByParent, nestedOptionalPropertyTriplesByAlias));
|
|
689
|
-
}
|
|
690
|
-
filteredBlockInners.push(blockInner);
|
|
691
|
-
filteredBlockExprs.push(filterExpr);
|
|
692
|
-
}
|
|
693
|
-
// Second pass, children first (blocks are created parent-before-child):
|
|
694
|
-
// finish each block and either nest it inside its parent's filtered block —
|
|
695
|
-
// a filtered sub-select inside another filtered sub-select must stay scoped
|
|
696
|
-
// to the parent alias, or an empty parent match leaves the alias unbound and
|
|
697
|
-
// the block cross-products over the whole graph — or attach it at top level.
|
|
698
|
-
const rootFilteredBlocks = [];
|
|
699
|
-
for (let i = filteredTraverseBlocks.length - 1; i >= 0; i--) {
|
|
700
|
-
const block = filteredTraverseBlocks[i];
|
|
701
|
-
const finished = {
|
|
702
|
-
type: 'filter',
|
|
703
|
-
expression: filteredBlockExprs[i],
|
|
704
|
-
inner: filteredBlockInners[i],
|
|
705
|
-
};
|
|
706
|
-
const subject = block.traverseTriple.subject;
|
|
707
|
-
const parentIdx = subject.kind === 'variable'
|
|
708
|
-
? filteredBlockIdxByAlias.get(subject.name)
|
|
709
|
-
: undefined;
|
|
710
|
-
if (parentIdx !== undefined && parentIdx !== i) {
|
|
711
|
-
filteredBlockInners[parentIdx] = wrapOptional(filteredBlockInners[parentIdx], finished);
|
|
712
|
-
}
|
|
713
|
-
else {
|
|
714
|
-
rootFilteredBlocks.unshift(finished);
|
|
715
|
-
}
|
|
716
|
-
}
|
|
717
|
-
for (const filteredBlock of rootFilteredBlocks) {
|
|
718
|
-
algebra = wrapOptional(algebra, filteredBlock);
|
|
719
|
-
}
|
|
720
|
-
for (const alias of rootOptionalTraversalAliases) {
|
|
721
|
-
algebra = wrapOptional(algebra, buildOptionalTraversalSubtree(alias, traversePatternMap, childOptionalAliasesByParent, nestedOptionalPropertyTriplesByAlias));
|
|
722
|
-
}
|
|
723
|
-
// Wrap each optional property triple in its own OPTIONAL (LeftJoin)
|
|
724
|
-
for (const propTriple of topLevelOptionalPropertyTriples) {
|
|
725
|
-
algebra = wrapOptional(algebra, {
|
|
726
|
-
type: 'bgp',
|
|
727
|
-
triples: [propTriple],
|
|
728
|
-
});
|
|
729
|
-
}
|
|
730
|
-
// 5. Where clause → Filter wrapping (or HAVING if aggregate-containing)
|
|
731
|
-
let havingExpr;
|
|
732
|
-
if (query.where) {
|
|
733
|
-
const filterExpr = convertExpression(query.where, registry, optionalPropertyTriples);
|
|
734
|
-
if (containsAggregate(filterExpr)) {
|
|
735
|
-
havingExpr = filterExpr;
|
|
736
|
-
}
|
|
737
|
-
else {
|
|
738
|
-
algebra = {
|
|
739
|
-
type: 'filter',
|
|
740
|
-
expression: filterExpr,
|
|
741
|
-
inner: algebra,
|
|
742
|
-
};
|
|
743
|
-
}
|
|
744
|
-
}
|
|
745
|
-
// 5b. MINUS patterns — wrap algebra in SparqlMinus for each minus pattern
|
|
746
|
-
for (const pattern of query.patterns) {
|
|
747
|
-
if (pattern.kind === 'minus') {
|
|
748
|
-
let minusAlgebra = convertExistsPattern(pattern.pattern, registry);
|
|
749
|
-
if (pattern.filter) {
|
|
750
|
-
const minusPropertyTriples = [];
|
|
751
|
-
processExpressionForProperties(pattern.filter, registry, minusPropertyTriples);
|
|
752
|
-
// Add property triples into the MINUS block
|
|
753
|
-
if (minusPropertyTriples.length > 0) {
|
|
754
|
-
minusAlgebra = joinNodes(minusAlgebra, { type: 'bgp', triples: minusPropertyTriples });
|
|
755
|
-
}
|
|
756
|
-
const filterExpr = convertExpression(pattern.filter, registry, minusPropertyTriples);
|
|
757
|
-
minusAlgebra = { type: 'filter', expression: filterExpr, inner: minusAlgebra };
|
|
758
|
-
}
|
|
759
|
-
algebra = { type: 'minus', left: algebra, right: minusAlgebra };
|
|
760
|
-
}
|
|
761
|
-
}
|
|
762
|
-
// 6. SubjectId → Filter / SubjectIds → VALUES
|
|
763
|
-
if (query.subjectIds && query.subjectIds.length > 0) {
|
|
764
|
-
// Multiple subjects: use VALUES clause for efficient filtering
|
|
765
|
-
algebra = joinNodes({ type: 'values', variable: rootAlias, iris: query.subjectIds }, algebra);
|
|
766
|
-
}
|
|
767
|
-
else if (query.subjectId) {
|
|
768
|
-
const subjectFilter = {
|
|
769
|
-
kind: 'binary_expr',
|
|
770
|
-
op: '=',
|
|
771
|
-
left: { kind: 'variable_expr', name: rootAlias },
|
|
772
|
-
right: { kind: 'iri_expr', value: query.subjectId },
|
|
773
|
-
};
|
|
774
|
-
algebra = {
|
|
775
|
-
type: 'filter',
|
|
776
|
-
expression: subjectFilter,
|
|
777
|
-
inner: algebra,
|
|
778
|
-
};
|
|
779
|
-
}
|
|
780
|
-
// 7. Build projection
|
|
781
|
-
const projection = [];
|
|
782
|
-
const aggregates = [];
|
|
783
|
-
let hasAggregates = false;
|
|
784
|
-
// Always include root alias as first projection variable
|
|
785
|
-
projection.push({ kind: 'variable', name: rootAlias });
|
|
786
|
-
// Collect traversal aliases upfront to detect aggregate alias collisions
|
|
787
|
-
const traversalAliasSet = new Set(collectTraversalAliases(query.patterns));
|
|
788
|
-
// Track traversal aliases consumed by aggregate renames (should not be
|
|
789
|
-
// re-projected as plain variables, which would alter GROUP BY semantics)
|
|
790
|
-
const aggregateRenamedAliases = new Set();
|
|
791
|
-
for (const item of query.projection) {
|
|
792
|
-
const sparqlExpr = convertExpression(item.expression, registry, optionalPropertyTriples);
|
|
793
|
-
if (sparqlExpr.kind === 'aggregate_expr') {
|
|
794
|
-
hasAggregates = true;
|
|
795
|
-
// Avoid collision: if aggregate alias matches a traversal alias,
|
|
796
|
-
// rename it so SPARQL doesn't produce duplicate variable bindings
|
|
797
|
-
let aggAlias = item.alias;
|
|
798
|
-
if (traversalAliasSet.has(aggAlias)) {
|
|
799
|
-
aggregateRenamedAliases.add(aggAlias);
|
|
800
|
-
aggAlias = `${aggAlias}_agg`;
|
|
801
|
-
// Update resultMap so result mapping uses the renamed alias
|
|
802
|
-
for (const rm of query.resultMap) {
|
|
803
|
-
if (rm.alias === item.alias)
|
|
804
|
-
rm.alias = aggAlias;
|
|
805
|
-
}
|
|
806
|
-
}
|
|
807
|
-
projection.push({
|
|
808
|
-
kind: 'aggregate',
|
|
809
|
-
expression: sparqlExpr,
|
|
810
|
-
alias: aggAlias,
|
|
811
|
-
});
|
|
812
|
-
aggregates.push({
|
|
813
|
-
variable: aggAlias,
|
|
814
|
-
aggregate: sparqlExpr,
|
|
815
|
-
});
|
|
816
|
-
}
|
|
817
|
-
else {
|
|
818
|
-
// For property_expr, the variable is the resolved name from registry
|
|
819
|
-
const varName = resolveExpressionVariable(item.expression, registry);
|
|
820
|
-
if (varName && varName !== rootAlias) {
|
|
821
|
-
projection.push({ kind: 'variable', name: varName });
|
|
822
|
-
}
|
|
823
|
-
else if (!varName) {
|
|
824
|
-
// Non-variable expression (binary_expr, function_expr, etc.)
|
|
825
|
-
// → project as (expr AS ?alias)
|
|
826
|
-
let exprAlias = item.alias;
|
|
827
|
-
if (traversalAliasSet.has(exprAlias)) {
|
|
828
|
-
// The output alias collides with a traversal target variable — e.g. a
|
|
829
|
-
// computed expression over a traversed path lowers to
|
|
830
|
-
// (UCASE(?a1_name) AS ?a1), and ?a1 is already bound by the traverse
|
|
831
|
-
// triple. Rename the output so SPARQL doesn't reuse an in-scope var.
|
|
832
|
-
exprAlias = `${exprAlias}_expr`;
|
|
833
|
-
for (const rm of query.resultMap) {
|
|
834
|
-
if (rm.alias === item.alias)
|
|
835
|
-
rm.alias = exprAlias;
|
|
836
|
-
}
|
|
837
|
-
}
|
|
838
|
-
projection.push({ kind: 'expression', expression: sparqlExpr, alias: exprAlias });
|
|
839
|
-
}
|
|
840
|
-
}
|
|
841
|
-
}
|
|
842
|
-
// 7b. Include traversal aliases needed for result grouping
|
|
843
|
-
// When nested results are projected (e.g. p.friends.name), the result
|
|
844
|
-
// mapping needs the traversal alias variable (?a1) in the bindings to
|
|
845
|
-
// group nested rows by entity. Without this, mapNestedRows() can't
|
|
846
|
-
// identify which nested fields belong to which traversed entity.
|
|
847
|
-
const projectedNames = new Set();
|
|
848
|
-
for (const p of projection) {
|
|
849
|
-
if (p.kind === 'variable')
|
|
850
|
-
projectedNames.add(p.name);
|
|
851
|
-
else if (p.kind === 'aggregate' || p.kind === 'expression')
|
|
852
|
-
projectedNames.add(p.alias);
|
|
853
|
-
}
|
|
854
|
-
for (const alias of collectTraversalAliases(query.patterns)) {
|
|
855
|
-
if (!projectedNames.has(alias) && !aggregateRenamedAliases.has(alias)) {
|
|
856
|
-
projection.push({ kind: 'variable', name: alias });
|
|
857
|
-
projectedNames.add(alias);
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
// 8. GROUP BY inference
|
|
861
|
-
let groupBy;
|
|
862
|
-
if (havingExpr) {
|
|
863
|
-
hasAggregates = true;
|
|
864
|
-
}
|
|
865
|
-
if (hasAggregates) {
|
|
866
|
-
// All non-aggregate projected variables become GROUP BY targets
|
|
867
|
-
groupBy = projection
|
|
868
|
-
.filter((p) => p.kind === 'variable')
|
|
869
|
-
.map((p) => p.name);
|
|
870
|
-
}
|
|
871
|
-
// 9. OrderBy
|
|
872
|
-
let orderBy;
|
|
873
|
-
if (query.orderBy) {
|
|
874
|
-
orderBy = query.orderBy.map((item) => ({
|
|
875
|
-
expression: convertExpression(item.expression, registry, optionalPropertyTriples),
|
|
876
|
-
direction: item.direction,
|
|
877
|
-
}));
|
|
878
|
-
}
|
|
879
|
-
// `.one()` lowers to limit=1 + singleResult, but LIMIT bounds ROWS, not
|
|
880
|
-
// entities. When the query yields multiple rows per root entity (traversals
|
|
881
|
-
// or plural properties), a row-level LIMIT would truncate the entity's own
|
|
882
|
-
// nested data — omit it and let the result mapper pick the single entity.
|
|
883
|
-
const multiRowPerEntity = query.patterns.some((p) => p.kind === 'traverse') ||
|
|
884
|
-
query.projection.some((item) => item.expression.kind === 'property_expr' &&
|
|
885
|
-
!(typeof item.expression.maxCount === 'number' && item.expression.maxCount <= 1));
|
|
886
|
-
const limit = query.singleResult && query.limit === 1 && multiRowPerEntity
|
|
887
|
-
? undefined
|
|
888
|
-
: query.limit;
|
|
889
|
-
return {
|
|
890
|
-
type: 'select',
|
|
891
|
-
algebra,
|
|
892
|
-
projection,
|
|
893
|
-
distinct: !hasAggregates ? true : undefined,
|
|
894
|
-
orderBy,
|
|
895
|
-
limit,
|
|
896
|
-
offset: query.offset,
|
|
897
|
-
groupBy,
|
|
898
|
-
having: havingExpr,
|
|
899
|
-
aggregates: aggregates.length > 0 ? aggregates : undefined,
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
// ---------------------------------------------------------------------------
|
|
903
|
-
// Pattern processing
|
|
904
|
-
// ---------------------------------------------------------------------------
|
|
905
|
-
function processPattern(pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases) {
|
|
906
|
-
switch (pattern.kind) {
|
|
907
|
-
case 'shape_scan':
|
|
908
|
-
// Additional shape scans (non-root) are handled as type triples
|
|
909
|
-
// but this case is rare — root is handled separately
|
|
910
|
-
break;
|
|
911
|
-
case 'traverse': {
|
|
912
|
-
// Register the traverse variable: (from, property) → to
|
|
913
|
-
registry.set(pattern.from, pattern.property, pattern.to);
|
|
914
|
-
// Traversals wrapped in a sub-SELECT (inner LIMIT/OFFSET) are emitted
|
|
915
|
-
// separately — don't add their triple to the required BGP here.
|
|
916
|
-
if (subSelectAliases === null || subSelectAliases === void 0 ? void 0 : subSelectAliases.has(pattern.to)) {
|
|
917
|
-
break;
|
|
918
|
-
}
|
|
919
|
-
// Add traverse triple to required pattern (or filtered block if inline where)
|
|
920
|
-
const triple = buildTraverseTriple(pattern);
|
|
921
|
-
if (pattern.filter && filteredTraverseBlocks) {
|
|
922
|
-
filteredTraverseBlocks.push({
|
|
923
|
-
traverseTriple: triple,
|
|
924
|
-
filter: pattern.filter,
|
|
925
|
-
toAlias: pattern.to,
|
|
926
|
-
});
|
|
927
|
-
}
|
|
928
|
-
else {
|
|
929
|
-
traverseTriples.push(triple);
|
|
930
|
-
}
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
case 'join': {
|
|
934
|
-
for (const sub of pattern.patterns) {
|
|
935
|
-
processPattern(sub, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases);
|
|
936
|
-
}
|
|
937
|
-
break;
|
|
938
|
-
}
|
|
939
|
-
case 'optional': {
|
|
940
|
-
// Optional patterns — process inner patterns but keep them optional
|
|
941
|
-
processPattern(pattern.pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases);
|
|
942
|
-
break;
|
|
943
|
-
}
|
|
944
|
-
case 'union': {
|
|
945
|
-
for (const branch of pattern.branches) {
|
|
946
|
-
processPattern(branch, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases);
|
|
947
|
-
}
|
|
948
|
-
break;
|
|
949
|
-
}
|
|
950
|
-
case 'exists': {
|
|
951
|
-
processPattern(pattern.pattern, registry, traverseTriples, optionalPropertyTriples, filteredTraverseBlocks, subSelectAliases);
|
|
952
|
-
break;
|
|
953
|
-
}
|
|
954
|
-
case 'minus': {
|
|
955
|
-
// MINUS patterns are handled separately in selectToAlgebra — skip in processPattern.
|
|
956
|
-
break;
|
|
957
|
-
}
|
|
958
|
-
}
|
|
959
|
-
}
|
|
960
|
-
// ---------------------------------------------------------------------------
|
|
961
|
-
// Expression processing — discover property_expr references
|
|
962
|
-
// ---------------------------------------------------------------------------
|
|
963
|
-
function processExpressionForProperties(expr, registry, optionalPropertyTriples, requiredPropertyTriples = [], requiredPropertyKeys = new Set()) {
|
|
964
|
-
switch (expr.kind) {
|
|
965
|
-
case 'property_expr': {
|
|
966
|
-
if (!registry.has(expr.sourceAlias, expr.property)) {
|
|
967
|
-
const varName = registry.getOrCreate(expr.sourceAlias, expr.property);
|
|
968
|
-
const predicate = buildPredicateTerm(expr);
|
|
969
|
-
const triple = tripleOf(varTerm(expr.sourceAlias), predicate, varTerm(varName));
|
|
970
|
-
const triples = requiredPropertyKeys.has(bindingKey(expr.sourceAlias, expr.property))
|
|
971
|
-
? requiredPropertyTriples
|
|
972
|
-
: optionalPropertyTriples;
|
|
973
|
-
triples.push(triple);
|
|
974
|
-
}
|
|
975
|
-
break;
|
|
976
|
-
}
|
|
977
|
-
case 'binary_expr':
|
|
978
|
-
processExpressionForProperties(expr.left, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
979
|
-
processExpressionForProperties(expr.right, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
980
|
-
break;
|
|
981
|
-
case 'in_expr':
|
|
982
|
-
// The tested value binds a property; list elements are constants.
|
|
983
|
-
processExpressionForProperties(expr.value, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
984
|
-
break;
|
|
985
|
-
case 'logical_expr':
|
|
986
|
-
for (const sub of expr.expressions) {
|
|
987
|
-
processExpressionForProperties(sub, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
988
|
-
}
|
|
989
|
-
break;
|
|
990
|
-
case 'not_expr':
|
|
991
|
-
processExpressionForProperties(expr.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
992
|
-
break;
|
|
993
|
-
case 'function_expr':
|
|
994
|
-
for (const arg of expr.args) {
|
|
995
|
-
processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
996
|
-
}
|
|
997
|
-
break;
|
|
998
|
-
case 'aggregate_expr':
|
|
999
|
-
for (const arg of expr.args) {
|
|
1000
|
-
processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
1001
|
-
}
|
|
1002
|
-
break;
|
|
1003
|
-
case 'exists_expr':
|
|
1004
|
-
// exists_expr filter properties belong INSIDE the EXISTS block, not in
|
|
1005
|
-
// the outer scope. Do NOT register them here — convertExpression's
|
|
1006
|
-
// exists_expr handler will collect and emit them locally.
|
|
1007
|
-
break;
|
|
1008
|
-
case 'context_property_expr': {
|
|
1009
|
-
// Context entity property — emit a triple with fixed IRI as subject.
|
|
1010
|
-
// Use raw IRI as registry key to avoid collision between IRIs that
|
|
1011
|
-
// sanitize to the same string (e.g. ctx-1 vs ctx_1).
|
|
1012
|
-
const contextIri = resolvedContextIri(expr.contextIri, expr.contextName);
|
|
1013
|
-
const ctxKey = contextAliasKey(contextIri);
|
|
1014
|
-
if (!registry.has(ctxKey, expr.property)) {
|
|
1015
|
-
const varName = registry.getOrCreate(ctxKey, expr.property);
|
|
1016
|
-
const triple = tripleOf(iriTerm(contextIri), resolvePropertyPredicateTerm(expr.property), varTerm(varName));
|
|
1017
|
-
const triples = requiredPropertyKeys.has(bindingKey(ctxKey, expr.property))
|
|
1018
|
-
? requiredPropertyTriples
|
|
1019
|
-
: optionalPropertyTriples;
|
|
1020
|
-
triples.push(triple);
|
|
1021
|
-
}
|
|
1022
|
-
break;
|
|
1023
|
-
}
|
|
1024
|
-
case 'literal_expr':
|
|
1025
|
-
case 'reference_expr':
|
|
1026
|
-
case 'alias_expr':
|
|
1027
|
-
// No property references to discover
|
|
1028
|
-
break;
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
/**
|
|
1032
|
-
* Compute which bindings are mandatory for a top-level FILTER to keep a row.
|
|
1033
|
-
* AND makes either side required; OR only keeps bindings required by every branch.
|
|
1034
|
-
*/
|
|
1035
|
-
function collectRequiredBindingKeys(expr) {
|
|
1036
|
-
switch (expr.kind) {
|
|
1037
|
-
case 'property_expr':
|
|
1038
|
-
return new Set([bindingKey(expr.sourceAlias, expr.property)]);
|
|
1039
|
-
case 'context_property_expr':
|
|
1040
|
-
return new Set([bindingKey(contextAliasKey(resolvedContextIri(expr.contextIri, expr.contextName)), expr.property)]);
|
|
1041
|
-
case 'in_expr':
|
|
1042
|
-
// The tested value must be bound; list elements are constants.
|
|
1043
|
-
return collectRequiredBindingKeys(expr.value);
|
|
1044
|
-
case 'binary_expr':
|
|
1045
|
-
return mergeKeySets(collectRequiredBindingKeys(expr.left), collectRequiredBindingKeys(expr.right));
|
|
1046
|
-
case 'function_expr': {
|
|
1047
|
-
const fn = expr.name.toUpperCase();
|
|
1048
|
-
// BOUND explicitly tests boundness — forcing its argument into a
|
|
1049
|
-
// required (inner-join) pattern would make !BOUND unsatisfiable.
|
|
1050
|
-
// COALESCE is unbound-tolerant by design — requiring its arguments
|
|
1051
|
-
// would make the fallback unreachable.
|
|
1052
|
-
if (fn === 'BOUND' || fn === 'COALESCE') {
|
|
1053
|
-
return new Set();
|
|
1054
|
-
}
|
|
1055
|
-
// IF: an unbound variable in the condition errors the row out either
|
|
1056
|
-
// way, so the condition may keep its requirements — but the then/else
|
|
1057
|
-
// branches must stay optional (the untaken branch may reference a
|
|
1058
|
-
// property the entity doesn't have).
|
|
1059
|
-
if (fn === 'IF') {
|
|
1060
|
-
return expr.args.length > 0
|
|
1061
|
-
? collectRequiredBindingKeys(expr.args[0])
|
|
1062
|
-
: new Set();
|
|
1063
|
-
}
|
|
1064
|
-
return mergeKeySets(...expr.args.map((arg) => collectRequiredBindingKeys(arg)));
|
|
1065
|
-
}
|
|
1066
|
-
case 'not_expr':
|
|
1067
|
-
return collectRequiredBindingKeys(expr.expression);
|
|
1068
|
-
case 'logical_expr': {
|
|
1069
|
-
const childSets = expr.expressions.map((sub) => collectRequiredBindingKeys(sub));
|
|
1070
|
-
if (expr.operator === 'and') {
|
|
1071
|
-
return mergeKeySets(...childSets);
|
|
1072
|
-
}
|
|
1073
|
-
return intersectKeySets(childSets);
|
|
1074
|
-
}
|
|
1075
|
-
case 'aggregate_expr':
|
|
1076
|
-
case 'exists_expr':
|
|
1077
|
-
case 'literal_expr':
|
|
1078
|
-
case 'reference_expr':
|
|
1079
|
-
case 'alias_expr':
|
|
1080
|
-
return new Set();
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
// ---------------------------------------------------------------------------
|
|
1084
|
-
// Expression conversion
|
|
1085
|
-
// ---------------------------------------------------------------------------
|
|
1086
|
-
function convertExpression(expr, registry, optionalPropertyTriples) {
|
|
1087
|
-
switch (expr.kind) {
|
|
1088
|
-
case 'literal_expr': {
|
|
1089
|
-
const value = expr.value;
|
|
1090
|
-
if (value === null || value === undefined) {
|
|
1091
|
-
return { kind: 'literal_expr', value: '' };
|
|
1092
|
-
}
|
|
1093
|
-
if (typeof value === 'boolean') {
|
|
1094
|
-
return {
|
|
1095
|
-
kind: 'literal_expr',
|
|
1096
|
-
value: String(value),
|
|
1097
|
-
datatype: XSD_BOOLEAN,
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
|
-
if (typeof value === 'number') {
|
|
1101
|
-
if (Number.isInteger(value)) {
|
|
1102
|
-
return {
|
|
1103
|
-
kind: 'literal_expr',
|
|
1104
|
-
value: String(value),
|
|
1105
|
-
datatype: XSD_INTEGER,
|
|
1106
|
-
};
|
|
1107
|
-
}
|
|
1108
|
-
return {
|
|
1109
|
-
kind: 'literal_expr',
|
|
1110
|
-
value: String(value),
|
|
1111
|
-
datatype: XSD_DOUBLE,
|
|
1112
|
-
};
|
|
1113
|
-
}
|
|
1114
|
-
return { kind: 'literal_expr', value: String(value) };
|
|
1115
|
-
}
|
|
1116
|
-
case 'reference_expr':
|
|
1117
|
-
return { kind: 'iri_expr', value: resolvedContextIri(expr.value, expr.contextName) };
|
|
1118
|
-
case 'alias_expr':
|
|
1119
|
-
return { kind: 'variable_expr', name: expr.alias };
|
|
1120
|
-
case 'context_property_expr': {
|
|
1121
|
-
const ctxKey = `__ctx__${resolvedContextIri(expr.contextIri, expr.contextName)}`;
|
|
1122
|
-
const ctxVarName = registry.getOrCreate(ctxKey, expr.property);
|
|
1123
|
-
return { kind: 'variable_expr', name: ctxVarName };
|
|
1124
|
-
}
|
|
1125
|
-
case 'property_expr': {
|
|
1126
|
-
const varName = registry.getOrCreate(expr.sourceAlias, expr.property);
|
|
1127
|
-
return { kind: 'variable_expr', name: varName };
|
|
1128
|
-
}
|
|
1129
|
-
case 'in_expr':
|
|
1130
|
-
return {
|
|
1131
|
-
kind: 'in_expr',
|
|
1132
|
-
negated: expr.negated,
|
|
1133
|
-
value: convertExpression(expr.value, registry, optionalPropertyTriples),
|
|
1134
|
-
list: expr.source.list.map((e) => convertExpression(e, registry, optionalPropertyTriples)),
|
|
1135
|
-
};
|
|
1136
|
-
case 'binary_expr':
|
|
1137
|
-
return {
|
|
1138
|
-
kind: 'binary_expr',
|
|
1139
|
-
op: expr.operator,
|
|
1140
|
-
left: convertExpression(expr.left, registry, optionalPropertyTriples),
|
|
1141
|
-
right: convertExpression(expr.right, registry, optionalPropertyTriples),
|
|
1142
|
-
};
|
|
1143
|
-
case 'logical_expr':
|
|
1144
|
-
return {
|
|
1145
|
-
kind: 'logical_expr',
|
|
1146
|
-
op: expr.operator,
|
|
1147
|
-
exprs: expr.expressions.map((e) => convertExpression(e, registry, optionalPropertyTriples)),
|
|
1148
|
-
};
|
|
1149
|
-
case 'not_expr':
|
|
1150
|
-
return {
|
|
1151
|
-
kind: 'not_expr',
|
|
1152
|
-
inner: convertExpression(expr.expression, registry, optionalPropertyTriples),
|
|
1153
|
-
};
|
|
1154
|
-
case 'function_expr':
|
|
1155
|
-
return {
|
|
1156
|
-
kind: 'function_expr',
|
|
1157
|
-
name: expr.name,
|
|
1158
|
-
args: expr.args.map((a) => convertExpression(a, registry, optionalPropertyTriples)),
|
|
1159
|
-
};
|
|
1160
|
-
case 'aggregate_expr':
|
|
1161
|
-
return {
|
|
1162
|
-
kind: 'aggregate_expr',
|
|
1163
|
-
name: expr.name,
|
|
1164
|
-
args: expr.args.map((a) => convertExpression(a, registry, optionalPropertyTriples)),
|
|
1165
|
-
};
|
|
1166
|
-
case 'exists_expr': {
|
|
1167
|
-
// Convert exists expression with inner pattern + filter.
|
|
1168
|
-
// Filter property triples must live INSIDE the EXISTS block
|
|
1169
|
-
// (not in the outer scope), so we collect them locally.
|
|
1170
|
-
let innerAlgebra = convertExistsPattern(expr.pattern, registry);
|
|
1171
|
-
if (expr.filter) {
|
|
1172
|
-
// First, discover and register filter property references,
|
|
1173
|
-
// collecting their triples into a local array (NOT the outer scope).
|
|
1174
|
-
const existsPropertyTriples = [];
|
|
1175
|
-
processExpressionForProperties(expr.filter, registry, existsPropertyTriples);
|
|
1176
|
-
// Now convert the filter expression (variables are registered above).
|
|
1177
|
-
const filterExpr = convertExpression(expr.filter, registry, existsPropertyTriples);
|
|
1178
|
-
// Add filter property triples inside the EXISTS
|
|
1179
|
-
for (const propTriple of existsPropertyTriples) {
|
|
1180
|
-
innerAlgebra = joinNodes(innerAlgebra, { type: 'bgp', triples: [propTriple] });
|
|
1181
|
-
}
|
|
1182
|
-
// Wrap the inner pattern with a filter
|
|
1183
|
-
const filteredInner = {
|
|
1184
|
-
type: 'filter',
|
|
1185
|
-
expression: filterExpr,
|
|
1186
|
-
inner: innerAlgebra,
|
|
1187
|
-
};
|
|
1188
|
-
return {
|
|
1189
|
-
kind: 'exists_expr',
|
|
1190
|
-
pattern: filteredInner,
|
|
1191
|
-
negated: false,
|
|
1192
|
-
};
|
|
1193
|
-
}
|
|
1194
|
-
return {
|
|
1195
|
-
kind: 'exists_expr',
|
|
1196
|
-
pattern: innerAlgebra,
|
|
1197
|
-
negated: false,
|
|
1198
|
-
};
|
|
1199
|
-
}
|
|
1200
|
-
default:
|
|
1201
|
-
throw new Error(`Unknown IR expression kind: ${expr.kind}`);
|
|
1202
|
-
}
|
|
1203
|
-
}
|
|
1204
|
-
/**
|
|
1205
|
-
* Convert an exists pattern (from exists_expr) into an algebra node.
|
|
1206
|
-
* Recursively handles all IR graph pattern kinds.
|
|
1207
|
-
*/
|
|
1208
|
-
function convertExistsPattern(pattern, registry) {
|
|
1209
|
-
switch (pattern.kind) {
|
|
1210
|
-
case 'traverse': {
|
|
1211
|
-
const existsPredicate = buildPredicateTerm(pattern);
|
|
1212
|
-
const triple = tripleOf(varTerm(pattern.from), existsPredicate, varTerm(pattern.to));
|
|
1213
|
-
return { type: 'bgp', triples: [triple] };
|
|
1214
|
-
}
|
|
1215
|
-
case 'join': {
|
|
1216
|
-
let result = null;
|
|
1217
|
-
for (const sub of pattern.patterns) {
|
|
1218
|
-
const subNode = convertExistsPattern(sub, registry);
|
|
1219
|
-
result = result ? joinNodes(result, subNode) : subNode;
|
|
1220
|
-
}
|
|
1221
|
-
return result || { type: 'bgp', triples: [] };
|
|
1222
|
-
}
|
|
1223
|
-
case 'shape_scan': {
|
|
1224
|
-
return {
|
|
1225
|
-
type: 'bgp',
|
|
1226
|
-
triples: [
|
|
1227
|
-
tripleOf(varTerm(pattern.alias), iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(pattern.shape))),
|
|
1228
|
-
],
|
|
1229
|
-
};
|
|
1230
|
-
}
|
|
1231
|
-
case 'optional': {
|
|
1232
|
-
const inner = convertExistsPattern(pattern.pattern, registry);
|
|
1233
|
-
return wrapOptional({ type: 'bgp', triples: [] }, inner);
|
|
1234
|
-
}
|
|
1235
|
-
case 'union': {
|
|
1236
|
-
let result = null;
|
|
1237
|
-
for (const branch of pattern.branches) {
|
|
1238
|
-
const branchNode = convertExistsPattern(branch, registry);
|
|
1239
|
-
if (!result) {
|
|
1240
|
-
result = branchNode;
|
|
1241
|
-
}
|
|
1242
|
-
else {
|
|
1243
|
-
result = { type: 'union', left: result, right: branchNode };
|
|
1244
|
-
}
|
|
1245
|
-
}
|
|
1246
|
-
return result || { type: 'bgp', triples: [] };
|
|
1247
|
-
}
|
|
1248
|
-
case 'exists': {
|
|
1249
|
-
return convertExistsPattern(pattern.pattern, registry);
|
|
1250
|
-
}
|
|
1251
|
-
case 'minus': {
|
|
1252
|
-
return convertExistsPattern(pattern.pattern, registry);
|
|
1253
|
-
}
|
|
1254
|
-
default:
|
|
1255
|
-
throw new Error(`Unsupported pattern kind in EXISTS: ${pattern.kind}`);
|
|
1256
|
-
}
|
|
1257
|
-
}
|
|
1258
|
-
/**
|
|
1259
|
-
* Resolve what variable name an IR expression ultimately refers to.
|
|
1260
|
-
*/
|
|
1261
|
-
function resolveExpressionVariable(expr, registry) {
|
|
1262
|
-
switch (expr.kind) {
|
|
1263
|
-
case 'alias_expr':
|
|
1264
|
-
return expr.alias;
|
|
1265
|
-
case 'property_expr':
|
|
1266
|
-
return registry.getOrCreate(expr.sourceAlias, expr.property);
|
|
1267
|
-
default:
|
|
1268
|
-
return null;
|
|
1269
|
-
}
|
|
1270
|
-
}
|
|
1271
|
-
// ---------------------------------------------------------------------------
|
|
1272
|
-
// Mutation conversions
|
|
1273
|
-
// ---------------------------------------------------------------------------
|
|
1274
|
-
/**
|
|
1275
|
-
* Convert a field value to one or more SparqlTerm objects for triple objects.
|
|
1276
|
-
*/
|
|
1277
|
-
function fieldValueToTerms(value, options) {
|
|
1278
|
-
if (value === null || value === undefined) {
|
|
1279
|
-
return [];
|
|
1280
|
-
}
|
|
1281
|
-
if (typeof value === 'string') {
|
|
1282
|
-
return [literalTerm(value)];
|
|
1283
|
-
}
|
|
1284
|
-
if (typeof value === 'number') {
|
|
1285
|
-
if (Number.isInteger(value)) {
|
|
1286
|
-
return [literalTerm(String(value), XSD_INTEGER)];
|
|
1287
|
-
}
|
|
1288
|
-
return [literalTerm(String(value), XSD_DOUBLE)];
|
|
1289
|
-
}
|
|
1290
|
-
if (typeof value === 'boolean') {
|
|
1291
|
-
return [literalTerm(String(value), XSD_BOOLEAN)];
|
|
1292
|
-
}
|
|
1293
|
-
if (value instanceof Date) {
|
|
1294
|
-
return [literalTerm(value.toISOString(), XSD_DATETIME)];
|
|
1295
|
-
}
|
|
1296
|
-
// Computed/expression value in create: create lowers to INSERT DATA (ground
|
|
1297
|
-
// triples, no WHERE), so an expression can't be evaluated here. Fail loudly
|
|
1298
|
-
// instead of silently emitting no triple (report 021 §3, G4).
|
|
1299
|
-
if (value &&
|
|
1300
|
-
typeof value === 'object' &&
|
|
1301
|
-
(isIRExpression(value) || 'ir' in value)) {
|
|
1302
|
-
throw new Error('Computed/expression values are not supported in create (INSERT DATA has no WHERE clause to evaluate them). Use a literal value, or use update.');
|
|
1303
|
-
}
|
|
1304
|
-
// NodeReferenceValue
|
|
1305
|
-
if (typeof value === 'object' && 'id' in value && !('shape' in value) && !('fields' in value)) {
|
|
1306
|
-
return [iriTerm(value.id)];
|
|
1307
|
-
}
|
|
1308
|
-
// IRNodeData — should not produce a term directly (handled by nested create)
|
|
1309
|
-
if (typeof value === 'object' && 'shape' in value && 'fields' in value) {
|
|
1310
|
-
return []; // Handled separately
|
|
1311
|
-
}
|
|
1312
|
-
// Array
|
|
1313
|
-
if (Array.isArray(value)) {
|
|
1314
|
-
const terms = [];
|
|
1315
|
-
for (const item of value) {
|
|
1316
|
-
terms.push(...fieldValueToTerms(item, options));
|
|
1317
|
-
}
|
|
1318
|
-
return terms;
|
|
1319
|
-
}
|
|
1320
|
-
return [];
|
|
1321
|
-
}
|
|
1322
|
-
/**
|
|
1323
|
-
* Recursively generate triples for an IRNodeData (used in create and nested creates).
|
|
1324
|
-
* Returns the URI used for this node and all generated triples.
|
|
1325
|
-
*/
|
|
1326
|
-
function generateNodeDataTriples(data, options) {
|
|
1327
|
-
const uri = data.id || (0, sparqlUtils_js_1.generateEntityUri)(data.shape, options);
|
|
1328
|
-
const triples = [];
|
|
1329
|
-
const subjectTerm = iriTerm(uri);
|
|
1330
|
-
// Type triple — resolve the SHACL NodeShape id in the IR to the ontology
|
|
1331
|
-
// targetClass URI (sibling of PR #77's SELECT-side resolveShapeScanIri).
|
|
1332
|
-
// Mutations were not covered by #77; see mutation-uri-fidelity.test.ts.
|
|
1333
|
-
triples.push(tripleOf(subjectTerm, iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(data.shape))));
|
|
1334
|
-
// Field triples
|
|
1335
|
-
for (const field of data.fields) {
|
|
1336
|
-
// Resolve the SHACL PropertyShape id to its declared `path` URI.
|
|
1337
|
-
const propertyTerm = resolvePropertyPredicateTerm(field.property);
|
|
1338
|
-
if (field.value === null || field.value === undefined) {
|
|
1339
|
-
continue;
|
|
1340
|
-
}
|
|
1341
|
-
// Handle arrays (including mixed arrays of references and nested creates)
|
|
1342
|
-
if (Array.isArray(field.value)) {
|
|
1343
|
-
for (const item of field.value) {
|
|
1344
|
-
if (item && typeof item === 'object' && 'shape' in item && 'fields' in item) {
|
|
1345
|
-
// Nested create
|
|
1346
|
-
const nested = generateNodeDataTriples(item, options);
|
|
1347
|
-
triples.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
1348
|
-
triples.push(...nested.triples);
|
|
1349
|
-
}
|
|
1350
|
-
else {
|
|
1351
|
-
const terms = fieldValueToTerms(item, options);
|
|
1352
|
-
for (const term of terms) {
|
|
1353
|
-
triples.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
1357
|
-
continue;
|
|
1358
|
-
}
|
|
1359
|
-
// Handle nested IRNodeData
|
|
1360
|
-
if (typeof field.value === 'object' && 'shape' in field.value && 'fields' in field.value) {
|
|
1361
|
-
const nested = generateNodeDataTriples(field.value, options);
|
|
1362
|
-
triples.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
1363
|
-
triples.push(...nested.triples);
|
|
1364
|
-
continue;
|
|
1365
|
-
}
|
|
1366
|
-
// Simple values
|
|
1367
|
-
const terms = fieldValueToTerms(field.value, options);
|
|
1368
|
-
for (const term of terms) {
|
|
1369
|
-
triples.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
1370
|
-
}
|
|
1371
|
-
}
|
|
1372
|
-
return { uri, triples };
|
|
1373
|
-
}
|
|
1374
|
-
/**
|
|
1375
|
-
* Converts an IRCreateMutation to a SparqlInsertDataPlan.
|
|
1376
|
-
*/
|
|
1377
|
-
function createToAlgebra(query, options) {
|
|
1378
|
-
const { triples } = generateNodeDataTriples(query.data, options);
|
|
1379
|
-
return {
|
|
1380
|
-
type: 'insert_data',
|
|
1381
|
-
triples,
|
|
1382
|
-
};
|
|
1383
|
-
}
|
|
1384
|
-
// ---------------------------------------------------------------------------
|
|
1385
|
-
// Shared update field processing
|
|
1386
|
-
// ---------------------------------------------------------------------------
|
|
1387
|
-
/**
|
|
1388
|
-
* Processes IRNodeData fields into DELETE/INSERT/WHERE triples.
|
|
1389
|
-
* Shared between updateToAlgebra (IRI subject) and updateWhereToAlgebra (variable subject).
|
|
1390
|
-
*/
|
|
1391
|
-
function processUpdateFields(data, subjectTerm, options) {
|
|
1392
|
-
const deletePatterns = [];
|
|
1393
|
-
const insertPatterns = [];
|
|
1394
|
-
const oldValueTriples = [];
|
|
1395
|
-
const extends_ = [];
|
|
1396
|
-
// Owned-subtree cascade: replacing a `contains` property must also
|
|
1397
|
-
// delete the old object's owned subtree, not just the one-hop edge.
|
|
1398
|
-
const { containsPreds: updContainsPreds } = collectContainment();
|
|
1399
|
-
const containsOldVars = [];
|
|
1400
|
-
const cascadeOptionals = [];
|
|
1401
|
-
for (const field of data.fields) {
|
|
1402
|
-
const propertyTerm = resolvePropertyPredicateTerm(field.property);
|
|
1403
|
-
const suffix = propertySuffix(field.property);
|
|
1404
|
-
const isContainsField = propertyTerm.kind === 'iri' && updContainsPreds.includes(propertyTerm.value);
|
|
1405
|
-
// Check for set modification ({add, remove})
|
|
1406
|
-
if (field.value &&
|
|
1407
|
-
typeof field.value === 'object' &&
|
|
1408
|
-
!Array.isArray(field.value) &&
|
|
1409
|
-
!(field.value instanceof Date) &&
|
|
1410
|
-
!('id' in field.value) &&
|
|
1411
|
-
!('shape' in field.value) &&
|
|
1412
|
-
('add' in field.value || 'remove' in field.value)) {
|
|
1413
|
-
const setMod = field.value;
|
|
1414
|
-
if (setMod.remove) {
|
|
1415
|
-
setMod.remove.forEach((removeItem, ri) => {
|
|
1416
|
-
const removeTerm = iriTerm(removeItem.id);
|
|
1417
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
|
|
1418
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, removeTerm));
|
|
1419
|
-
// For a `contains` set, removing a value also removes its owned subtree.
|
|
1420
|
-
if (isContainsField) {
|
|
1421
|
-
const cascade = buildOwnedCascade(removeTerm, `ucr_${suffix}_${ri}_`);
|
|
1422
|
-
deletePatterns.push(...cascade.deletePatterns);
|
|
1423
|
-
cascadeOptionals.push(...cascade.whereOptionals);
|
|
1424
|
-
}
|
|
1425
|
-
});
|
|
1426
|
-
}
|
|
1427
|
-
if (setMod.add) {
|
|
1428
|
-
for (const addItem of setMod.add) {
|
|
1429
|
-
if (addItem && typeof addItem === 'object' && 'shape' in addItem && 'fields' in addItem) {
|
|
1430
|
-
const nested = generateNodeDataTriples(addItem, options);
|
|
1431
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
1432
|
-
insertPatterns.push(...nested.triples);
|
|
1433
|
-
}
|
|
1434
|
-
else {
|
|
1435
|
-
const terms = fieldValueToTerms(addItem, options);
|
|
1436
|
-
for (const term of terms) {
|
|
1437
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
1438
|
-
}
|
|
1439
|
-
}
|
|
1440
|
-
}
|
|
1441
|
-
}
|
|
1442
|
-
continue;
|
|
1443
|
-
}
|
|
1444
|
-
// Non-set-modification replace of a `contains` property: cascade the old value's
|
|
1445
|
-
// subtree (every branch below binds `old_${suffix}` in the WHERE).
|
|
1446
|
-
if (isContainsField) {
|
|
1447
|
-
containsOldVars.push(`old_${suffix}`);
|
|
1448
|
-
}
|
|
1449
|
-
// Unset (undefined/null) — delete only
|
|
1450
|
-
if (field.value === undefined || field.value === null) {
|
|
1451
|
-
const oldVar = varTerm(`old_${suffix}`);
|
|
1452
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1453
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1454
|
-
continue;
|
|
1455
|
-
}
|
|
1456
|
-
// Array overwrite — delete old values + insert new ones
|
|
1457
|
-
if (Array.isArray(field.value)) {
|
|
1458
|
-
const oldVar = varTerm(`old_${suffix}`);
|
|
1459
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1460
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1461
|
-
for (const item of field.value) {
|
|
1462
|
-
if (item && typeof item === 'object' && 'shape' in item && 'fields' in item) {
|
|
1463
|
-
const nested = generateNodeDataTriples(item, options);
|
|
1464
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
1465
|
-
insertPatterns.push(...nested.triples);
|
|
1466
|
-
}
|
|
1467
|
-
else {
|
|
1468
|
-
const terms = fieldValueToTerms(item, options);
|
|
1469
|
-
for (const term of terms) {
|
|
1470
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
1471
|
-
}
|
|
1472
|
-
}
|
|
1473
|
-
}
|
|
1474
|
-
continue;
|
|
1475
|
-
}
|
|
1476
|
-
// Nested create (single object field)
|
|
1477
|
-
if (typeof field.value === 'object' && 'shape' in field.value && 'fields' in field.value) {
|
|
1478
|
-
const oldVar = varTerm(`old_${suffix}`);
|
|
1479
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1480
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1481
|
-
const nested = generateNodeDataTriples(field.value, options);
|
|
1482
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, iriTerm(nested.uri)));
|
|
1483
|
-
insertPatterns.push(...nested.triples);
|
|
1484
|
-
continue;
|
|
1485
|
-
}
|
|
1486
|
-
// IRExpression — computed value update (e.g. p.age.plus(1))
|
|
1487
|
-
if (isIRExpression(field.value)) {
|
|
1488
|
-
const expr = field.value;
|
|
1489
|
-
const oldVar = varTerm(`old_${suffix}`);
|
|
1490
|
-
const computedVarName = `computed_${suffix}`;
|
|
1491
|
-
const computedVar = varTerm(computedVarName);
|
|
1492
|
-
// DELETE old value
|
|
1493
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1494
|
-
// WHERE: OPTIONAL for old value
|
|
1495
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1496
|
-
// Discover additional property references in the expression and add OPTIONAL triples
|
|
1497
|
-
const registry = new VariableRegistry();
|
|
1498
|
-
const mutationSubjectAlias = '__mutation_subject__';
|
|
1499
|
-
// Pre-register the subject variable mapping for the field being updated
|
|
1500
|
-
registry.set(mutationSubjectAlias, field.property, `old_${suffix}`);
|
|
1501
|
-
const additionalOptionals = [];
|
|
1502
|
-
processExpressionForProperties(expr, registry, additionalOptionals);
|
|
1503
|
-
// Add any additional property OPTIONAL triples (for refs to other properties)
|
|
1504
|
-
for (const triple of additionalOptionals) {
|
|
1505
|
-
// Rewrite the subject from the placeholder variable to the actual subject term
|
|
1506
|
-
if (triple.subject.kind === 'variable' && triple.subject.name === mutationSubjectAlias) {
|
|
1507
|
-
oldValueTriples.push(tripleOf(subjectTerm, triple.predicate, triple.object));
|
|
1508
|
-
}
|
|
1509
|
-
else {
|
|
1510
|
-
oldValueTriples.push(triple);
|
|
1511
|
-
}
|
|
1512
|
-
}
|
|
1513
|
-
// Convert IRExpression to SparqlExpression
|
|
1514
|
-
const sparqlExpr = convertExpression(expr, registry, additionalOptionals);
|
|
1515
|
-
// BIND computed expression
|
|
1516
|
-
extends_.push({ variable: computedVarName, expression: sparqlExpr });
|
|
1517
|
-
// INSERT computed value
|
|
1518
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, computedVar));
|
|
1519
|
-
continue;
|
|
1520
|
-
}
|
|
1521
|
-
// Simple value update — delete old + insert new
|
|
1522
|
-
const oldVar = varTerm(`old_${suffix}`);
|
|
1523
|
-
deletePatterns.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1524
|
-
oldValueTriples.push(tripleOf(subjectTerm, propertyTerm, oldVar));
|
|
1525
|
-
const terms = fieldValueToTerms(field.value, options);
|
|
1526
|
-
for (const term of terms) {
|
|
1527
|
-
insertPatterns.push(tripleOf(subjectTerm, propertyTerm, term));
|
|
1528
|
-
}
|
|
1529
|
-
}
|
|
1530
|
-
// Append cascade for each replaced `contains` property's old value.
|
|
1531
|
-
containsOldVars.forEach((oldVar, n) => {
|
|
1532
|
-
const cascade = buildOwnedCascade(varTerm(oldVar), `uc${n}_`);
|
|
1533
|
-
deletePatterns.push(...cascade.deletePatterns);
|
|
1534
|
-
cascadeOptionals.push(...cascade.whereOptionals);
|
|
1535
|
-
});
|
|
1536
|
-
return {
|
|
1537
|
-
deletePatterns,
|
|
1538
|
-
insertPatterns,
|
|
1539
|
-
oldValueTriples,
|
|
1540
|
-
extends: extends_,
|
|
1541
|
-
cascadeOptionals,
|
|
1542
|
-
};
|
|
1543
|
-
}
|
|
1544
|
-
/**
|
|
1545
|
-
* Wraps old-value triples in OPTIONAL (LEFT JOIN) so UPDATE succeeds
|
|
1546
|
-
* even when the old value doesn't exist.
|
|
1547
|
-
*/
|
|
1548
|
-
function wrapOldValueOptionals(base, oldValueTriples) {
|
|
1549
|
-
let algebra = base;
|
|
1550
|
-
if (oldValueTriples.length === 0) {
|
|
1551
|
-
return algebra;
|
|
1552
|
-
}
|
|
1553
|
-
for (const triple of oldValueTriples) {
|
|
1554
|
-
algebra = {
|
|
1555
|
-
type: 'left_join',
|
|
1556
|
-
left: algebra,
|
|
1557
|
-
right: { type: 'bgp', triples: [triple] },
|
|
1558
|
-
};
|
|
1559
|
-
}
|
|
1560
|
-
return algebra;
|
|
1561
|
-
}
|
|
1562
|
-
/**
|
|
1563
|
-
* Converts an IRUpdateMutation to a SparqlDeleteInsertPlan.
|
|
1564
|
-
*/
|
|
1565
|
-
function updateToAlgebra(query, options) {
|
|
1566
|
-
var _a, _b, _c;
|
|
1567
|
-
const subjectTerm = iriTerm(query.id);
|
|
1568
|
-
const result = processUpdateFields(query.data, subjectTerm, options);
|
|
1569
|
-
// Partition old-value/optional triples into subject-anchored ones and those
|
|
1570
|
-
// anchored on a traversal target variable (e.g. `?a1 <name> ?a1_name` from a
|
|
1571
|
-
// `p.bestFriend.name.ucase()` expression). Traversal-anchored triples MUST be
|
|
1572
|
-
// nested inside the same OPTIONAL group as their traversal edge — otherwise,
|
|
1573
|
-
// when the subject has no such edge, the leaf variable is unbound and the
|
|
1574
|
-
// property triple matches every entity in the graph (data-corruption bug).
|
|
1575
|
-
const travTos = new Set(((_a = query.traversalPatterns) !== null && _a !== void 0 ? _a : []).map((t) => t.to));
|
|
1576
|
-
const travAnchoredByTo = new Map();
|
|
1577
|
-
const subjectAnchored = [];
|
|
1578
|
-
for (const triple of result.oldValueTriples) {
|
|
1579
|
-
if (triple.subject.kind === 'variable' && travTos.has(triple.subject.name)) {
|
|
1580
|
-
const list = (_b = travAnchoredByTo.get(triple.subject.name)) !== null && _b !== void 0 ? _b : [];
|
|
1581
|
-
list.push(triple);
|
|
1582
|
-
travAnchoredByTo.set(triple.subject.name, list);
|
|
1583
|
-
}
|
|
1584
|
-
else {
|
|
1585
|
-
subjectAnchored.push(triple);
|
|
1586
|
-
}
|
|
1587
|
-
}
|
|
1588
|
-
let whereAlgebra = wrapOldValueOptionals({ type: 'bgp', triples: [] }, subjectAnchored);
|
|
1589
|
-
// Add traversal OPTIONAL patterns (for multi-segment expression refs). The
|
|
1590
|
-
// traversal edge and its dependent leaf property triples share one OPTIONAL
|
|
1591
|
-
// group so the leaf variable is scoped to the traversal target. These come
|
|
1592
|
-
// BEFORE expression BINDs since the BINDs reference the traversal variables.
|
|
1593
|
-
if (query.traversalPatterns) {
|
|
1594
|
-
for (const trav of query.traversalPatterns) {
|
|
1595
|
-
const fromTerm = trav.from === '__mutation_subject__' ? subjectTerm : varTerm(trav.from);
|
|
1596
|
-
const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
|
|
1597
|
-
// The traversal edge binds the target var; each dependent leaf property is
|
|
1598
|
-
// a nested OPTIONAL *within* that scope, so a missing optional property
|
|
1599
|
-
// (e.g. a bestFriend with no hobby) doesn't drop the whole group, while an
|
|
1600
|
-
// absent edge still leaves every leaf var unbound (no cross-entity match).
|
|
1601
|
-
let travNode = { type: 'bgp', triples: [traversalTriple] };
|
|
1602
|
-
for (const leaf of (_c = travAnchoredByTo.get(trav.to)) !== null && _c !== void 0 ? _c : []) {
|
|
1603
|
-
travNode = {
|
|
1604
|
-
type: 'left_join',
|
|
1605
|
-
left: travNode,
|
|
1606
|
-
right: { type: 'bgp', triples: [leaf] },
|
|
1607
|
-
};
|
|
1608
|
-
}
|
|
1609
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: travNode };
|
|
1610
|
-
}
|
|
1611
|
-
}
|
|
1612
|
-
// Owned-subtree cascade OPTIONALs for replaced `contains` properties.
|
|
1613
|
-
for (const optional of result.cascadeOptionals) {
|
|
1614
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
|
|
1615
|
-
}
|
|
1616
|
-
// Add BIND expressions for computed fields
|
|
1617
|
-
for (const ext of result.extends) {
|
|
1618
|
-
whereAlgebra = {
|
|
1619
|
-
type: 'extend',
|
|
1620
|
-
inner: whereAlgebra,
|
|
1621
|
-
variable: ext.variable,
|
|
1622
|
-
expression: ext.expression,
|
|
1623
|
-
};
|
|
1624
|
-
}
|
|
1625
|
-
return {
|
|
1626
|
-
type: 'delete_insert',
|
|
1627
|
-
deletePatterns: result.deletePatterns,
|
|
1628
|
-
insertPatterns: result.insertPatterns,
|
|
1629
|
-
whereAlgebra,
|
|
1630
|
-
};
|
|
1631
|
-
}
|
|
1632
|
-
// ---------------------------------------------------------------------------
|
|
1633
|
-
// Owned-subtree cascade
|
|
1634
|
-
//
|
|
1635
|
-
// Composition cleanup driven by two declarative flags:
|
|
1636
|
-
// - a property marked `contains` → the cascade FOLLOWS that edge,
|
|
1637
|
-
// - a shape marked `dependent` → its instances may be DELETED when reached.
|
|
1638
|
-
// Both are read from the live registry, so no vocabulary is hardcoded. From a root
|
|
1639
|
-
// node we follow `(c1|c2|…)+` over all contains predicates and, for each dependent
|
|
1640
|
-
// targetClass, wildcard-delete the reached typed nodes. Requiring an asserted
|
|
1641
|
-
// `?owned a <dependentType>` naturally excludes shared predicate IRIs (e.g. a simple
|
|
1642
|
-
// `sh:path`) and `rdf:nil` (never typed), so only owned structural nodes are removed.
|
|
1643
|
-
// ---------------------------------------------------------------------------
|
|
1644
|
-
/** True when the shape (or an ancestor) declares at least one `contains` property. */
|
|
1645
|
-
function shapeHasContainsProperty(shapeId) {
|
|
1646
|
-
var _a;
|
|
1647
|
-
const nodeShape = (_a = (0, ShapeClass_js_1.getShapeClass)(shapeId)) === null || _a === void 0 ? void 0 : _a.shape;
|
|
1648
|
-
if (!nodeShape || typeof nodeShape.getPropertyShapes !== 'function')
|
|
1649
|
-
return false;
|
|
1650
|
-
return nodeShape
|
|
1651
|
-
.getPropertyShapes(true)
|
|
1652
|
-
.some((ps) => ps.contains);
|
|
1653
|
-
}
|
|
1654
|
-
// Memoized registry scan for cascade lowering — called once per update/delete and
|
|
1655
|
-
// again per owned-cascade item inside loops. Guarded by registry size (same rationale
|
|
1656
|
-
// as predicateTermCache). Callers treat the result as read-only.
|
|
1657
|
-
let containmentCache = null;
|
|
1658
|
-
let containmentCacheSize = -1;
|
|
1659
|
-
/** Gather contains-predicate IRIs and dependent targetClass IRIs from the registry. */
|
|
1660
|
-
function collectContainment() {
|
|
1661
|
-
var _a;
|
|
1662
|
-
const shapeClasses = (0, ShapeClass_js_1.getAllShapeClasses)();
|
|
1663
|
-
if (containmentCache && shapeClasses.size === containmentCacheSize) {
|
|
1664
|
-
return containmentCache;
|
|
1665
|
-
}
|
|
1666
|
-
const containsPreds = new Set();
|
|
1667
|
-
const dependentTypes = new Set();
|
|
1668
|
-
for (const [, shapeClass] of shapeClasses) {
|
|
1669
|
-
const nodeShape = shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape;
|
|
1670
|
-
if (!nodeShape)
|
|
1671
|
-
continue;
|
|
1672
|
-
if (nodeShape.dependent && ((_a = nodeShape.targetClass) === null || _a === void 0 ? void 0 : _a.id)) {
|
|
1673
|
-
dependentTypes.add(nodeShape.targetClass.id);
|
|
1674
|
-
}
|
|
1675
|
-
const propertyShapes = typeof nodeShape.getPropertyShapes === 'function'
|
|
1676
|
-
? nodeShape.getPropertyShapes(false)
|
|
1677
|
-
: [];
|
|
1678
|
-
for (const ps of propertyShapes) {
|
|
1679
|
-
if (ps.contains) {
|
|
1680
|
-
const predId = (0, normalizePropertyPath_js_1.getSimplePathId)(ps.path);
|
|
1681
|
-
if (predId)
|
|
1682
|
-
containsPreds.add(predId);
|
|
1683
|
-
}
|
|
1684
|
-
}
|
|
1685
|
-
}
|
|
1686
|
-
containmentCache = { containsPreds: [...containsPreds], dependentTypes: [...dependentTypes] };
|
|
1687
|
-
containmentCacheSize = shapeClasses.size;
|
|
1688
|
-
return containmentCache;
|
|
1689
|
-
}
|
|
1690
|
-
/**
|
|
1691
|
-
* Build DELETE patterns + WHERE OPTIONAL blocks that cascade-delete the owned subtree
|
|
1692
|
-
* reachable from `rootTerm` via `contains` edges. `varPrefix` keeps generated variables
|
|
1693
|
-
* unique across multiple roots in one query. Returns empty when nothing is owned.
|
|
1694
|
-
*/
|
|
1695
|
-
function buildOwnedCascade(rootTerm, varPrefix) {
|
|
1696
|
-
const { containsPreds, dependentTypes } = collectContainment();
|
|
1697
|
-
if (containsPreds.length === 0 || dependentTypes.length === 0) {
|
|
1698
|
-
return { deletePatterns: [], whereOptionals: [] };
|
|
1699
|
-
}
|
|
1700
|
-
// (c1|c2|…)+ — follow one-or-more contains edges from the root.
|
|
1701
|
-
const pathExpr = containsPreds.length === 1
|
|
1702
|
-
? { oneOrMore: { id: containsPreds[0] } }
|
|
1703
|
-
: { oneOrMore: { alt: containsPreds.map((id) => ({ id })) } };
|
|
1704
|
-
const pathTerm = {
|
|
1705
|
-
kind: 'path',
|
|
1706
|
-
value: (0, pathExprToSparql_js_1.pathExprToSparql)(pathExpr),
|
|
1707
|
-
uris: (0, pathExprToSparql_js_1.collectPathUris)(pathExpr),
|
|
1708
|
-
};
|
|
1709
|
-
const deletePatterns = [];
|
|
1710
|
-
const whereOptionals = [];
|
|
1711
|
-
dependentTypes.forEach((typeId, i) => {
|
|
1712
|
-
const owned = varTerm(`${varPrefix}own${i}`);
|
|
1713
|
-
const p = varTerm(`${varPrefix}op${i}`);
|
|
1714
|
-
const o = varTerm(`${varPrefix}ov${i}`);
|
|
1715
|
-
deletePatterns.push(tripleOf(owned, p, o));
|
|
1716
|
-
whereOptionals.push({
|
|
1717
|
-
type: 'bgp',
|
|
1718
|
-
triples: [
|
|
1719
|
-
tripleOf(rootTerm, pathTerm, owned),
|
|
1720
|
-
tripleOf(owned, iriTerm(RDF_TYPE), iriTerm(typeId)),
|
|
1721
|
-
tripleOf(owned, p, o),
|
|
1722
|
-
],
|
|
1723
|
-
});
|
|
1724
|
-
});
|
|
1725
|
-
return { deletePatterns, whereOptionals };
|
|
1726
|
-
}
|
|
1727
|
-
/**
|
|
1728
|
-
* Converts an IRDeleteMutation to a SparqlDeleteInsertPlan (DELETE + WHERE).
|
|
1729
|
-
*/
|
|
1730
|
-
function deleteToAlgebra(query, _options) {
|
|
1731
|
-
const deletePatterns = [];
|
|
1732
|
-
const requiredTriples = [];
|
|
1733
|
-
const optionalTriples = [];
|
|
1734
|
-
const cascadeOptionals = [];
|
|
1735
|
-
for (let i = 0; i < query.ids.length; i++) {
|
|
1736
|
-
const subjectTerm = iriTerm(query.ids[i].id);
|
|
1737
|
-
const idx = query.ids.length > 1 ? `_${i}` : '';
|
|
1738
|
-
const subjWild = tripleOf(subjectTerm, varTerm(`p${idx}`), varTerm(`o${idx}`));
|
|
1739
|
-
const objWild = tripleOf(varTerm(`s${idx}`), varTerm(`p2${idx}`), subjectTerm);
|
|
1740
|
-
const typeGuard = tripleOf(subjectTerm, iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(query.shape)));
|
|
1741
|
-
// DELETE block: all patterns (subject-wildcard, object-wildcard, type)
|
|
1742
|
-
deletePatterns.push(subjWild, objWild, typeGuard);
|
|
1743
|
-
// WHERE block: subject-wildcard and type guard are required;
|
|
1744
|
-
// object-wildcard is OPTIONAL (entity may have no incoming references)
|
|
1745
|
-
requiredTriples.push(subjWild, typeGuard);
|
|
1746
|
-
optionalTriples.push(objWild);
|
|
1747
|
-
// Cascade-delete the owned subtree — only for shapes that actually own something
|
|
1748
|
-
// (have a `contains` property); a plain entity delete is left untouched.
|
|
1749
|
-
if (shapeHasContainsProperty(query.shape)) {
|
|
1750
|
-
const cascade = buildOwnedCascade(subjectTerm, `c${idx}_`);
|
|
1751
|
-
deletePatterns.push(...cascade.deletePatterns);
|
|
1752
|
-
cascadeOptionals.push(...cascade.whereOptionals);
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
// Build WHERE algebra: required BGP + OPTIONAL for each object-wildcard + cascade OPTIONALs
|
|
1756
|
-
let whereAlgebra = { type: 'bgp', triples: requiredTriples };
|
|
1757
|
-
for (const triple of optionalTriples) {
|
|
1758
|
-
whereAlgebra = {
|
|
1759
|
-
type: 'left_join',
|
|
1760
|
-
left: whereAlgebra,
|
|
1761
|
-
right: { type: 'bgp', triples: [triple] },
|
|
1762
|
-
};
|
|
1763
|
-
}
|
|
1764
|
-
for (const optional of cascadeOptionals) {
|
|
1765
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
|
|
1766
|
-
}
|
|
1767
|
-
return {
|
|
1768
|
-
type: 'delete_insert',
|
|
1769
|
-
deletePatterns,
|
|
1770
|
-
insertPatterns: [],
|
|
1771
|
-
whereAlgebra,
|
|
1772
|
-
};
|
|
1773
|
-
}
|
|
1774
|
-
// ---------------------------------------------------------------------------
|
|
1775
|
-
// Blank node tree walking for schema-aware delete cleanup
|
|
1776
|
-
// ---------------------------------------------------------------------------
|
|
1777
|
-
/**
|
|
1778
|
-
* Checks whether a PropertyShape points to blank nodes (sh:BlankNode or
|
|
1779
|
-
* sh:BlankNodeOrIRI). Returns true when the property's range *may* include
|
|
1780
|
-
* blank node values that should be cleaned up on delete.
|
|
1781
|
-
*/
|
|
1782
|
-
function isBlankNodeProperty(prop) {
|
|
1783
|
-
var _a;
|
|
1784
|
-
const nk = (_a = prop.nodeKind) === null || _a === void 0 ? void 0 : _a.id;
|
|
1785
|
-
if (!nk)
|
|
1786
|
-
return false;
|
|
1787
|
-
return nk === shacl_js_1.shacl.BlankNode.id || nk === shacl_js_1.shacl.BlankNodeOrIRI.id;
|
|
1788
|
-
}
|
|
1789
|
-
/**
|
|
1790
|
-
* Recursively builds DELETE + WHERE patterns for blank-node-typed properties.
|
|
1791
|
-
*
|
|
1792
|
-
* For each blank-node property on the shape:
|
|
1793
|
-
* - DELETE: `?bnVar ?pN ?oN .` (wildcard all triples on the blank node)
|
|
1794
|
-
* - WHERE: `OPTIONAL { ?parent <property> ?bnVar . FILTER(isBlank(?bnVar)) . ?bnVar ?pN ?oN . }`
|
|
1795
|
-
*
|
|
1796
|
-
* Recurses into the property's valueShape to handle nested blank nodes
|
|
1797
|
-
* (e.g. Person → Address (blank) → GeoPoint (blank)).
|
|
1798
|
-
*/
|
|
1799
|
-
function walkBlankNodeTree(shapeId, parentVar, depth, deletePatterns) {
|
|
1800
|
-
var _a;
|
|
1801
|
-
const shapeClass = (0, ShapeClass_js_1.getShapeClass)(shapeId);
|
|
1802
|
-
if (!(shapeClass === null || shapeClass === void 0 ? void 0 : shapeClass.shape))
|
|
1803
|
-
return null;
|
|
1804
|
-
let optionals = null;
|
|
1805
|
-
const props = shapeClass.shape.getPropertyShapes(true);
|
|
1806
|
-
for (const prop of props) {
|
|
1807
|
-
if (!isBlankNodeProperty(prop))
|
|
1808
|
-
continue;
|
|
1809
|
-
const bnVar = `bn${depth}`;
|
|
1810
|
-
const pVar = `p${depth}`;
|
|
1811
|
-
const oVar = `o${depth}`;
|
|
1812
|
-
// DELETE pattern: wildcard all triples on the blank node
|
|
1813
|
-
deletePatterns.push(tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar)));
|
|
1814
|
-
// WHERE: parent --<property>--> ?bnVar
|
|
1815
|
-
const traverseTriple = tripleOf(varTerm(parentVar), resolvePropertyPredicateTerm(prop.id), varTerm(bnVar));
|
|
1816
|
-
// FILTER(isBlank(?bnVar))
|
|
1817
|
-
const isBlankFilter = {
|
|
1818
|
-
kind: 'function_expr',
|
|
1819
|
-
name: 'isBlank',
|
|
1820
|
-
args: [{ kind: 'variable_expr', name: bnVar }],
|
|
1821
|
-
};
|
|
1822
|
-
// ?bnVar ?pN ?oN
|
|
1823
|
-
const wildcardTriple = tripleOf(varTerm(bnVar), varTerm(pVar), varTerm(oVar));
|
|
1824
|
-
// Build inner pattern: traverse + filter + wildcard
|
|
1825
|
-
let innerPattern = {
|
|
1826
|
-
type: 'bgp',
|
|
1827
|
-
triples: [traverseTriple, wildcardTriple],
|
|
1828
|
-
};
|
|
1829
|
-
innerPattern = { type: 'filter', expression: isBlankFilter, inner: innerPattern };
|
|
1830
|
-
// Recurse into valueShape for nested blank nodes
|
|
1831
|
-
if ((_a = prop.valueShape) === null || _a === void 0 ? void 0 : _a.id) {
|
|
1832
|
-
const nestedOptional = walkBlankNodeTree(prop.valueShape.id, bnVar, depth + 1, deletePatterns);
|
|
1833
|
-
if (nestedOptional) {
|
|
1834
|
-
innerPattern = { type: 'left_join', left: innerPattern, right: nestedOptional };
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
// Wrap in OPTIONAL (left_join)
|
|
1838
|
-
if (optionals) {
|
|
1839
|
-
optionals = { type: 'left_join', left: optionals, right: innerPattern };
|
|
1840
|
-
}
|
|
1841
|
-
else {
|
|
1842
|
-
optionals = innerPattern;
|
|
1843
|
-
}
|
|
1844
|
-
depth++;
|
|
1845
|
-
}
|
|
1846
|
-
return optionals;
|
|
1847
|
-
}
|
|
1848
|
-
/**
|
|
1849
|
-
* Converts an IRDeleteAllMutation to a SparqlDeleteInsertPlan.
|
|
1850
|
-
*
|
|
1851
|
-
* Generates DELETE { ?a0 ?p ?o . [blank node wildcards] }
|
|
1852
|
-
* WHERE { ?a0 a <Shape> . ?a0 ?p ?o . OPTIONAL { [blank node traversals] } }
|
|
1853
|
-
*/
|
|
1854
|
-
function deleteAllToAlgebra(query, _options) {
|
|
1855
|
-
const subjectVar = 'a0';
|
|
1856
|
-
// DELETE patterns: root wildcard
|
|
1857
|
-
const deletePatterns = [
|
|
1858
|
-
tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
|
|
1859
|
-
];
|
|
1860
|
-
// WHERE: type triple + root wildcard
|
|
1861
|
-
const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(query.shape)));
|
|
1862
|
-
const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
|
|
1863
|
-
let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
|
|
1864
|
-
// Walk blank node tree for cleanup
|
|
1865
|
-
const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
|
|
1866
|
-
if (blankNodeOptional) {
|
|
1867
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
|
|
1868
|
-
}
|
|
1869
|
-
return {
|
|
1870
|
-
type: 'delete_insert',
|
|
1871
|
-
deletePatterns,
|
|
1872
|
-
insertPatterns: [],
|
|
1873
|
-
whereAlgebra,
|
|
1874
|
-
};
|
|
1875
|
-
}
|
|
1876
|
-
/**
|
|
1877
|
-
* Converts an IRDeleteWhereMutation to a SparqlDeleteInsertPlan.
|
|
1878
|
-
*
|
|
1879
|
-
* Like deleteAllToAlgebra but adds filter conditions from the where clause.
|
|
1880
|
-
*/
|
|
1881
|
-
function deleteWhereToAlgebra(query, _options) {
|
|
1882
|
-
const subjectVar = 'a0';
|
|
1883
|
-
const registry = new VariableRegistry();
|
|
1884
|
-
// DELETE patterns: root wildcard
|
|
1885
|
-
const deletePatterns = [
|
|
1886
|
-
tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o')),
|
|
1887
|
-
];
|
|
1888
|
-
// WHERE: type triple + root wildcard
|
|
1889
|
-
const typeTriple = tripleOf(varTerm(subjectVar), iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(query.shape)));
|
|
1890
|
-
const rootWildcard = tripleOf(varTerm(subjectVar), varTerm('p'), varTerm('o'));
|
|
1891
|
-
let whereAlgebra = { type: 'bgp', triples: [typeTriple, rootWildcard] };
|
|
1892
|
-
// Process where patterns (traversals from the where clause)
|
|
1893
|
-
const traverseTriples = [];
|
|
1894
|
-
const optionalPropertyTriples = [];
|
|
1895
|
-
for (const pattern of query.wherePatterns) {
|
|
1896
|
-
processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
|
|
1897
|
-
}
|
|
1898
|
-
// Add traverse triples to required BGP
|
|
1899
|
-
if (traverseTriples.length > 0) {
|
|
1900
|
-
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
|
|
1901
|
-
}
|
|
1902
|
-
// Process expression to discover property triples
|
|
1903
|
-
processExpressionForProperties(query.where, registry, optionalPropertyTriples);
|
|
1904
|
-
// Add optional property triples
|
|
1905
|
-
for (const triple of optionalPropertyTriples) {
|
|
1906
|
-
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
|
|
1907
|
-
}
|
|
1908
|
-
// Convert and add filter expression
|
|
1909
|
-
const filterExpr = convertExpression(query.where, registry, []);
|
|
1910
|
-
whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
|
|
1911
|
-
// Walk blank node tree for cleanup
|
|
1912
|
-
const blankNodeOptional = walkBlankNodeTree(query.shape, subjectVar, 1, deletePatterns);
|
|
1913
|
-
if (blankNodeOptional) {
|
|
1914
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: blankNodeOptional };
|
|
1915
|
-
}
|
|
1916
|
-
return {
|
|
1917
|
-
type: 'delete_insert',
|
|
1918
|
-
deletePatterns,
|
|
1919
|
-
insertPatterns: [],
|
|
1920
|
-
whereAlgebra,
|
|
1921
|
-
};
|
|
1922
|
-
}
|
|
1923
|
-
/**
|
|
1924
|
-
* Converts an IRUpdateWhereMutation to a SparqlDeleteInsertPlan.
|
|
1925
|
-
*
|
|
1926
|
-
* Like updateToAlgebra but uses a variable subject (?a0) instead of a
|
|
1927
|
-
* hardcoded entity IRI, adds a type triple, and optionally includes
|
|
1928
|
-
* filter conditions from the where clause.
|
|
1929
|
-
*/
|
|
1930
|
-
function updateWhereToAlgebra(query, options) {
|
|
1931
|
-
const subjectTerm = varTerm('a0');
|
|
1932
|
-
const result = processUpdateFields(query.data, subjectTerm, options);
|
|
1933
|
-
// WHERE: type triple is always required
|
|
1934
|
-
const typeTriple = tripleOf(subjectTerm, iriTerm(RDF_TYPE), iriTerm(resolveShapeScanIri(query.data.shape)));
|
|
1935
|
-
let whereAlgebra = { type: 'bgp', triples: [typeTriple] };
|
|
1936
|
-
// Process where filter conditions (if any)
|
|
1937
|
-
if (query.where && query.wherePatterns) {
|
|
1938
|
-
const registry = new VariableRegistry();
|
|
1939
|
-
const traverseTriples = [];
|
|
1940
|
-
const optionalPropertyTriples = [];
|
|
1941
|
-
for (const pattern of query.wherePatterns) {
|
|
1942
|
-
processPattern(pattern, registry, traverseTriples, optionalPropertyTriples);
|
|
1943
|
-
}
|
|
1944
|
-
if (traverseTriples.length > 0) {
|
|
1945
|
-
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: traverseTriples });
|
|
1946
|
-
}
|
|
1947
|
-
processExpressionForProperties(query.where, registry, optionalPropertyTriples);
|
|
1948
|
-
for (const triple of optionalPropertyTriples) {
|
|
1949
|
-
whereAlgebra = joinNodes(whereAlgebra, { type: 'bgp', triples: [triple] });
|
|
1950
|
-
}
|
|
1951
|
-
const filterExpr = convertExpression(query.where, registry, []);
|
|
1952
|
-
whereAlgebra = { type: 'filter', expression: filterExpr, inner: whereAlgebra };
|
|
1953
|
-
}
|
|
1954
|
-
whereAlgebra = wrapOldValueOptionals(whereAlgebra, result.oldValueTriples);
|
|
1955
|
-
// Add traversal OPTIONAL patterns (for multi-segment expression refs)
|
|
1956
|
-
// These must come BEFORE expression BINDs since the BINDs reference traversal variables.
|
|
1957
|
-
if (query.traversalPatterns) {
|
|
1958
|
-
for (const trav of query.traversalPatterns) {
|
|
1959
|
-
const fromTerm = trav.from === '__mutation_subject__' ? varTerm('a0') : varTerm(trav.from);
|
|
1960
|
-
const traversalTriple = tripleOf(fromTerm, iriTerm(trav.property), varTerm(trav.to));
|
|
1961
|
-
whereAlgebra = {
|
|
1962
|
-
type: 'left_join',
|
|
1963
|
-
left: whereAlgebra,
|
|
1964
|
-
right: { type: 'bgp', triples: [traversalTriple] },
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
}
|
|
1968
|
-
// Owned-subtree cascade OPTIONALs for replaced `contains` properties.
|
|
1969
|
-
for (const optional of result.cascadeOptionals) {
|
|
1970
|
-
whereAlgebra = { type: 'left_join', left: whereAlgebra, right: optional };
|
|
1971
|
-
}
|
|
1972
|
-
// Add BIND expressions for computed fields
|
|
1973
|
-
for (const ext of result.extends) {
|
|
1974
|
-
whereAlgebra = {
|
|
1975
|
-
type: 'extend',
|
|
1976
|
-
inner: whereAlgebra,
|
|
1977
|
-
variable: ext.variable,
|
|
1978
|
-
expression: ext.expression,
|
|
1979
|
-
};
|
|
1980
|
-
}
|
|
1981
|
-
return {
|
|
1982
|
-
type: 'delete_insert',
|
|
1983
|
-
deletePatterns: result.deletePatterns,
|
|
1984
|
-
insertPatterns: result.insertPatterns,
|
|
1985
|
-
whereAlgebra,
|
|
1986
|
-
};
|
|
1987
|
-
}
|
|
1988
|
-
// ---------------------------------------------------------------------------
|
|
1989
|
-
// Convenience wrappers: IR → algebra → SPARQL string in one call
|
|
1990
|
-
// ---------------------------------------------------------------------------
|
|
1991
|
-
/**
|
|
1992
|
-
* Converts an IRSelectQuery to a SPARQL string.
|
|
1993
|
-
*/
|
|
1994
|
-
function selectToSparql(query, options) {
|
|
1995
|
-
const plan = selectToAlgebra(query, options);
|
|
1996
|
-
return (0, algebraToString_js_1.selectPlanToSparql)(plan, options);
|
|
1997
|
-
}
|
|
1998
|
-
/**
|
|
1999
|
-
* Converts an IRCreateMutation to a SPARQL string.
|
|
2000
|
-
* Stub: will be implemented when algebraToString is available.
|
|
2001
|
-
*/
|
|
2002
|
-
function createToSparql(query, options) {
|
|
2003
|
-
const plan = createToAlgebra(query, options);
|
|
2004
|
-
return (0, algebraToString_js_1.insertDataPlanToSparql)(plan, options);
|
|
2005
|
-
}
|
|
2006
|
-
/**
|
|
2007
|
-
* Converts an IRUpdateMutation to a SPARQL string.
|
|
2008
|
-
* Stub: will be implemented when algebraToString is available.
|
|
2009
|
-
*/
|
|
2010
|
-
function updateToSparql(query, options) {
|
|
2011
|
-
const plan = updateToAlgebra(query, options);
|
|
2012
|
-
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
2013
|
-
}
|
|
2014
|
-
/**
|
|
2015
|
-
* Converts an IRDeleteMutation to a SPARQL string.
|
|
2016
|
-
*/
|
|
2017
|
-
function deleteToSparql(query, options) {
|
|
2018
|
-
const plan = deleteToAlgebra(query, options);
|
|
2019
|
-
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
2020
|
-
}
|
|
2021
|
-
/**
|
|
2022
|
-
* Converts an IRDeleteAllMutation to a SPARQL string.
|
|
2023
|
-
*/
|
|
2024
|
-
function deleteAllToSparql(query, options) {
|
|
2025
|
-
const plan = deleteAllToAlgebra(query, options);
|
|
2026
|
-
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
2027
|
-
}
|
|
2028
|
-
/**
|
|
2029
|
-
* Converts an IRDeleteWhereMutation to a SPARQL string.
|
|
2030
|
-
*/
|
|
2031
|
-
function deleteWhereToSparql(query, options) {
|
|
2032
|
-
const plan = deleteWhereToAlgebra(query, options);
|
|
2033
|
-
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
2034
|
-
}
|
|
2035
|
-
/**
|
|
2036
|
-
* Converts an IRUpdateWhereMutation to a SPARQL string.
|
|
2037
|
-
*/
|
|
2038
|
-
function updateWhereToSparql(query, options) {
|
|
2039
|
-
const plan = updateWhereToAlgebra(query, options);
|
|
2040
|
-
return (0, algebraToString_js_1.deleteInsertPlanToSparql)(plan, options);
|
|
2041
|
-
}
|
|
2042
|
-
//# sourceMappingURL=irToAlgebra.js.map
|