@_linked/core 2.4.0 → 2.4.1-next.20260406160450
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 +12 -0
- package/lib/cjs/queries/FieldSet.js +4 -0
- package/lib/cjs/queries/FieldSet.js.map +1 -1
- package/lib/cjs/queries/QueryBuilder.d.ts +17 -5
- package/lib/cjs/queries/QueryBuilder.js +123 -66
- package/lib/cjs/queries/QueryBuilder.js.map +1 -1
- package/lib/cjs/queries/QueryBuilderSerialization.d.ts +95 -0
- package/lib/cjs/queries/QueryBuilderSerialization.js +332 -0
- package/lib/cjs/queries/QueryBuilderSerialization.js.map +1 -0
- package/lib/cjs/sparql/irToAlgebra.js +89 -16
- package/lib/cjs/sparql/irToAlgebra.js.map +1 -1
- package/lib/cjs/test-helpers/query-fixtures.d.ts +7 -0
- package/lib/cjs/test-helpers/query-fixtures.js +2 -0
- package/lib/cjs/test-helpers/query-fixtures.js.map +1 -1
- package/lib/esm/queries/FieldSet.js +4 -0
- package/lib/esm/queries/FieldSet.js.map +1 -1
- package/lib/esm/queries/QueryBuilder.d.ts +17 -5
- package/lib/esm/queries/QueryBuilder.js +123 -66
- package/lib/esm/queries/QueryBuilder.js.map +1 -1
- package/lib/esm/queries/QueryBuilderSerialization.d.ts +95 -0
- package/lib/esm/queries/QueryBuilderSerialization.js +289 -0
- package/lib/esm/queries/QueryBuilderSerialization.js.map +1 -0
- package/lib/esm/sparql/irToAlgebra.js +89 -16
- package/lib/esm/sparql/irToAlgebra.js.map +1 -1
- package/lib/esm/test-helpers/query-fixtures.d.ts +7 -0
- package/lib/esm/test-helpers/query-fixtures.js +2 -0
- package/lib/esm/test-helpers/query-fixtures.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Serialization and deserialization helpers for QueryBuilder fields that contain
|
|
3
|
+
* live object references (PropertyShape, ExpressionNode, etc.).
|
|
4
|
+
*
|
|
5
|
+
* These convert between runtime WherePath / SortByPath / RawMinusEntry structures
|
|
6
|
+
* and plain JSON-safe representations that can round-trip through JSON.stringify/parse.
|
|
7
|
+
*/
|
|
8
|
+
import { getShapeClass } from '../utils/ShapeClass.js';
|
|
9
|
+
import { walkPropertyPath } from './PropertyPath.js';
|
|
10
|
+
import { ExpressionNode } from '../expressions/ExpressionNode.js';
|
|
11
|
+
// ExistsCondition only exists after the WherePath refactor; access dynamically
|
|
12
|
+
// so this file compiles on branches where it doesn't exist yet.
|
|
13
|
+
import * as _exprModule from '../expressions/ExpressionNode.js';
|
|
14
|
+
const ExistsConditionCtor = _exprModule.ExistsCondition;
|
|
15
|
+
// =============================================================================
|
|
16
|
+
// Serialization
|
|
17
|
+
// =============================================================================
|
|
18
|
+
export function serializeQueryPropertyPath(path) {
|
|
19
|
+
return path.map(serializeQueryStep);
|
|
20
|
+
}
|
|
21
|
+
function serializeQueryStep(step) {
|
|
22
|
+
// PropertyQueryStep — has a .property field that is a PropertyShape
|
|
23
|
+
if ('property' in step && step.property) {
|
|
24
|
+
const pqs = step;
|
|
25
|
+
const json = {
|
|
26
|
+
kind: 'property',
|
|
27
|
+
label: pqs.property.label,
|
|
28
|
+
};
|
|
29
|
+
if (pqs.where) {
|
|
30
|
+
json.where = serializeWherePath(pqs.where);
|
|
31
|
+
}
|
|
32
|
+
return json;
|
|
33
|
+
}
|
|
34
|
+
// SizeStep — has a .count field
|
|
35
|
+
if ('count' in step) {
|
|
36
|
+
const ss = step;
|
|
37
|
+
const json = {
|
|
38
|
+
kind: 'size',
|
|
39
|
+
count: serializeQueryPropertyPath(ss.count),
|
|
40
|
+
};
|
|
41
|
+
if (ss.label)
|
|
42
|
+
json.label = ss.label;
|
|
43
|
+
return json;
|
|
44
|
+
}
|
|
45
|
+
// ShapeReferenceValue — has .id and .shape
|
|
46
|
+
const ref = step;
|
|
47
|
+
return { kind: 'shapeRef', id: ref.id, shapeId: ref.shape.id };
|
|
48
|
+
}
|
|
49
|
+
export function serializeWherePath(where) {
|
|
50
|
+
var _a, _b;
|
|
51
|
+
if ('expressionNode' in where) {
|
|
52
|
+
const expr = where.expressionNode;
|
|
53
|
+
const json = {
|
|
54
|
+
kind: 'expression',
|
|
55
|
+
ir: expr.ir,
|
|
56
|
+
};
|
|
57
|
+
if (expr._refs.size > 0) {
|
|
58
|
+
const refs = {};
|
|
59
|
+
for (const [k, v] of expr._refs)
|
|
60
|
+
refs[k] = [...v];
|
|
61
|
+
json.refs = refs;
|
|
62
|
+
}
|
|
63
|
+
return json;
|
|
64
|
+
}
|
|
65
|
+
if ('firstPath' in where) {
|
|
66
|
+
const andOr = where;
|
|
67
|
+
return {
|
|
68
|
+
kind: 'andOr',
|
|
69
|
+
firstPath: serializeWherePath(andOr.firstPath),
|
|
70
|
+
andOr: andOr.andOr.map((token) => {
|
|
71
|
+
const t = {};
|
|
72
|
+
if (token.and)
|
|
73
|
+
t.and = serializeWherePath(token.and);
|
|
74
|
+
if (token.or)
|
|
75
|
+
t.or = serializeWherePath(token.or);
|
|
76
|
+
return t;
|
|
77
|
+
}),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
if ('path' in where && 'method' in where && 'args' in where) {
|
|
81
|
+
const ev = where;
|
|
82
|
+
return {
|
|
83
|
+
kind: 'evaluation',
|
|
84
|
+
path: serializeQueryPropertyPath(ev.path),
|
|
85
|
+
method: ev.method,
|
|
86
|
+
args: ev.args.map(serializeQueryArg),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
if ('existsCondition' in where) {
|
|
90
|
+
// WhereExistsPath — serialize the ExistsCondition structurally
|
|
91
|
+
const ec = where.existsCondition;
|
|
92
|
+
return {
|
|
93
|
+
kind: 'exists',
|
|
94
|
+
pathSegmentIds: [...ec.pathSegmentIds],
|
|
95
|
+
predicate: { ir: ec.predicate.ir, refs: serializePropertyRefMap(ec.predicate._refs) },
|
|
96
|
+
negated: (_a = ec.negated) !== null && _a !== void 0 ? _a : false,
|
|
97
|
+
chain: ((_b = ec.chain) !== null && _b !== void 0 ? _b : []).map((c) => {
|
|
98
|
+
var _a;
|
|
99
|
+
return ({
|
|
100
|
+
op: c.op,
|
|
101
|
+
condition: 'pathSegmentIds' in c.condition
|
|
102
|
+
? { kind: 'exists', pathSegmentIds: [...c.condition.pathSegmentIds], predicate: { ir: c.condition.predicate.ir, refs: serializePropertyRefMap(c.condition.predicate._refs) }, negated: (_a = c.condition.negated) !== null && _a !== void 0 ? _a : false, chain: [] }
|
|
103
|
+
: { kind: 'expression', ir: c.condition.ir, refs: serializePropertyRefMap(c.condition._refs) },
|
|
104
|
+
});
|
|
105
|
+
}),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
throw new Error(`Cannot serialize WherePath: ${JSON.stringify(Object.keys(where))}`);
|
|
109
|
+
}
|
|
110
|
+
function serializePropertyRefMap(refs) {
|
|
111
|
+
if (!refs || refs.size === 0)
|
|
112
|
+
return undefined;
|
|
113
|
+
const out = {};
|
|
114
|
+
for (const [k, v] of refs)
|
|
115
|
+
out[k] = [...v];
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
function serializeQueryArg(arg) {
|
|
119
|
+
if (typeof arg === 'string' || typeof arg === 'number' || typeof arg === 'boolean') {
|
|
120
|
+
return { kind: 'primitive', value: arg };
|
|
121
|
+
}
|
|
122
|
+
if (arg instanceof Date) {
|
|
123
|
+
return { kind: 'date', value: arg.toISOString() };
|
|
124
|
+
}
|
|
125
|
+
if (typeof arg === 'object' && arg !== null) {
|
|
126
|
+
if ('expressionNode' in arg || 'existsCondition' in arg || 'firstPath' in arg || ('path' in arg && 'method' in arg && 'args' in arg)) {
|
|
127
|
+
return { kind: 'where', where: serializeWherePath(arg) };
|
|
128
|
+
}
|
|
129
|
+
// ArgPath — has 'subject' and 'path'
|
|
130
|
+
if ('subject' in arg && 'path' in arg) {
|
|
131
|
+
const ap = arg;
|
|
132
|
+
return {
|
|
133
|
+
kind: 'argPath',
|
|
134
|
+
path: serializeQueryPropertyPath(ap.path),
|
|
135
|
+
subject: { id: ap.subject.id, shapeId: ap.subject.shape.id },
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
// NodeReferenceValue — has 'id'
|
|
139
|
+
if ('id' in arg) {
|
|
140
|
+
return { kind: 'nodeRef', id: arg.id };
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
throw new Error(`Cannot serialize query arg: ${JSON.stringify(arg)}`);
|
|
144
|
+
}
|
|
145
|
+
export function serializeSortByPath(sort) {
|
|
146
|
+
return {
|
|
147
|
+
paths: sort.paths.map((p) => p.toString()),
|
|
148
|
+
direction: sort.direction,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
export function serializeRawMinusEntry(entry) {
|
|
152
|
+
const json = {};
|
|
153
|
+
if (entry.shapeId)
|
|
154
|
+
json.shapeId = entry.shapeId;
|
|
155
|
+
if (entry.where)
|
|
156
|
+
json.where = serializeWherePath(entry.where);
|
|
157
|
+
if (entry.propertyPaths) {
|
|
158
|
+
json.propertyPaths = entry.propertyPaths.map((pp) => pp.map((seg) => seg.propertyShapeId));
|
|
159
|
+
}
|
|
160
|
+
return json;
|
|
161
|
+
}
|
|
162
|
+
// =============================================================================
|
|
163
|
+
// Deserialization
|
|
164
|
+
// =============================================================================
|
|
165
|
+
export function deserializeQueryPropertyPath(shape, steps) {
|
|
166
|
+
var _a;
|
|
167
|
+
const result = [];
|
|
168
|
+
let currentShape = shape;
|
|
169
|
+
for (const step of steps) {
|
|
170
|
+
if (step.kind === 'property') {
|
|
171
|
+
const propertyShape = currentShape.getPropertyShape(step.label);
|
|
172
|
+
if (!propertyShape) {
|
|
173
|
+
throw new Error(`Property '${step.label}' not found on shape '${currentShape.label || currentShape.id}'`);
|
|
174
|
+
}
|
|
175
|
+
const queryStep = { property: propertyShape };
|
|
176
|
+
if (step.where) {
|
|
177
|
+
const nestedShape = propertyShape.valueShape
|
|
178
|
+
? (_a = getShapeClass(propertyShape.valueShape)) === null || _a === void 0 ? void 0 : _a.shape
|
|
179
|
+
: currentShape;
|
|
180
|
+
queryStep.where = deserializeWherePath(nestedShape || currentShape, step.where);
|
|
181
|
+
}
|
|
182
|
+
result.push(queryStep);
|
|
183
|
+
// Advance shape context for subsequent traversals
|
|
184
|
+
if (propertyShape.valueShape) {
|
|
185
|
+
const cls = getShapeClass(propertyShape.valueShape);
|
|
186
|
+
if (cls === null || cls === void 0 ? void 0 : cls.shape)
|
|
187
|
+
currentShape = cls.shape;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (step.kind === 'size') {
|
|
191
|
+
result.push({
|
|
192
|
+
count: deserializeQueryPropertyPath(currentShape, step.count),
|
|
193
|
+
label: step.label,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
else {
|
|
197
|
+
// shapeRef
|
|
198
|
+
result.push({ id: step.id, shape: { id: step.shapeId } });
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return result;
|
|
202
|
+
}
|
|
203
|
+
export function deserializeWherePath(shape, json) {
|
|
204
|
+
if (json.kind === 'expression') {
|
|
205
|
+
const refs = new Map();
|
|
206
|
+
if (json.refs) {
|
|
207
|
+
for (const [k, v] of Object.entries(json.refs))
|
|
208
|
+
refs.set(k, v);
|
|
209
|
+
}
|
|
210
|
+
return { expressionNode: new ExpressionNode(json.ir, refs) };
|
|
211
|
+
}
|
|
212
|
+
if (json.kind === 'exists') {
|
|
213
|
+
if (!ExistsConditionCtor)
|
|
214
|
+
throw new Error('ExistsCondition is not available on this build');
|
|
215
|
+
const predRefs = deserializeRefMap(json.predicate.refs);
|
|
216
|
+
const predicate = new ExpressionNode(json.predicate.ir, predRefs);
|
|
217
|
+
const chain = json.chain.map((c) => {
|
|
218
|
+
if (c.condition.kind === 'exists') {
|
|
219
|
+
const innerRefs = deserializeRefMap(c.condition.predicate.refs);
|
|
220
|
+
return { op: c.op, condition: new ExistsConditionCtor(c.condition.pathSegmentIds, new ExpressionNode(c.condition.predicate.ir, innerRefs), c.condition.negated) };
|
|
221
|
+
}
|
|
222
|
+
const exprRefs = deserializeRefMap(c.condition.refs);
|
|
223
|
+
return { op: c.op, condition: new ExpressionNode(c.condition.ir, exprRefs) };
|
|
224
|
+
});
|
|
225
|
+
return { existsCondition: new ExistsConditionCtor(json.pathSegmentIds, predicate, json.negated, chain) };
|
|
226
|
+
}
|
|
227
|
+
if (json.kind === 'andOr') {
|
|
228
|
+
return {
|
|
229
|
+
firstPath: deserializeWherePath(shape, json.firstPath),
|
|
230
|
+
andOr: json.andOr.map((token) => {
|
|
231
|
+
const t = {};
|
|
232
|
+
if (token.and)
|
|
233
|
+
t.and = deserializeWherePath(shape, token.and);
|
|
234
|
+
if (token.or)
|
|
235
|
+
t.or = deserializeWherePath(shape, token.or);
|
|
236
|
+
return t;
|
|
237
|
+
}),
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
// evaluation (legacy)
|
|
241
|
+
return {
|
|
242
|
+
path: deserializeQueryPropertyPath(shape, json.path),
|
|
243
|
+
method: json.method,
|
|
244
|
+
args: json.args.map((a) => deserializeQueryArg(shape, a)),
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
function deserializeRefMap(refs) {
|
|
248
|
+
const m = new Map();
|
|
249
|
+
if (refs) {
|
|
250
|
+
for (const [k, v] of Object.entries(refs))
|
|
251
|
+
m.set(k, v);
|
|
252
|
+
}
|
|
253
|
+
return m;
|
|
254
|
+
}
|
|
255
|
+
function deserializeQueryArg(shape, json) {
|
|
256
|
+
switch (json.kind) {
|
|
257
|
+
case 'primitive':
|
|
258
|
+
return json.value;
|
|
259
|
+
case 'date':
|
|
260
|
+
return new Date(json.value);
|
|
261
|
+
case 'nodeRef':
|
|
262
|
+
return { id: json.id };
|
|
263
|
+
case 'where':
|
|
264
|
+
return deserializeWherePath(shape, json.where);
|
|
265
|
+
case 'argPath':
|
|
266
|
+
return {
|
|
267
|
+
path: deserializeQueryPropertyPath(shape, json.path),
|
|
268
|
+
subject: { id: json.subject.id, shape: { id: json.subject.shapeId } },
|
|
269
|
+
};
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
export function deserializeSortByPath(shape, json) {
|
|
273
|
+
return {
|
|
274
|
+
paths: json.paths.map((p) => walkPropertyPath(shape, p)),
|
|
275
|
+
direction: json.direction,
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
export function deserializeRawMinusEntry(shape, json) {
|
|
279
|
+
const entry = {};
|
|
280
|
+
if (json.shapeId)
|
|
281
|
+
entry.shapeId = json.shapeId;
|
|
282
|
+
if (json.where)
|
|
283
|
+
entry.where = deserializeWherePath(shape, json.where);
|
|
284
|
+
if (json.propertyPaths) {
|
|
285
|
+
entry.propertyPaths = json.propertyPaths.map((pp) => pp.map((id) => ({ propertyShapeId: id })));
|
|
286
|
+
}
|
|
287
|
+
return entry;
|
|
288
|
+
}
|
|
289
|
+
//# sourceMappingURL=QueryBuilderSerialization.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"QueryBuilderSerialization.js","sourceRoot":"","sources":["../../../src/queries/QueryBuilderSerialization.ts"],"names":[],"mappings":"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"}
|
|
@@ -86,6 +86,34 @@ function joinNodes(left, right) {
|
|
|
86
86
|
return right;
|
|
87
87
|
return { type: 'join', left, right };
|
|
88
88
|
}
|
|
89
|
+
function bindingKey(alias, property) {
|
|
90
|
+
return `${alias}::${property}`;
|
|
91
|
+
}
|
|
92
|
+
function contextAliasKey(contextIri) {
|
|
93
|
+
return `__ctx__${contextIri}`;
|
|
94
|
+
}
|
|
95
|
+
function mergeKeySets(...sets) {
|
|
96
|
+
const merged = new Set();
|
|
97
|
+
for (const set of sets) {
|
|
98
|
+
for (const key of set) {
|
|
99
|
+
merged.add(key);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return merged;
|
|
103
|
+
}
|
|
104
|
+
function intersectKeySets(sets) {
|
|
105
|
+
if (sets.length === 0) {
|
|
106
|
+
return new Set();
|
|
107
|
+
}
|
|
108
|
+
const [first, ...rest] = sets;
|
|
109
|
+
const intersection = new Set(first);
|
|
110
|
+
for (const value of intersection) {
|
|
111
|
+
if (!rest.every((set) => set.has(value))) {
|
|
112
|
+
intersection.delete(value);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return intersection;
|
|
116
|
+
}
|
|
89
117
|
// ---------------------------------------------------------------------------
|
|
90
118
|
// Pattern helpers
|
|
91
119
|
// ---------------------------------------------------------------------------
|
|
@@ -126,7 +154,7 @@ class VariableRegistry {
|
|
|
126
154
|
this.usedVarNames = new Set();
|
|
127
155
|
}
|
|
128
156
|
key(alias, property) {
|
|
129
|
-
return
|
|
157
|
+
return bindingKey(alias, property);
|
|
130
158
|
}
|
|
131
159
|
has(alias, property) {
|
|
132
160
|
return this.map.has(this.key(alias, property));
|
|
@@ -186,6 +214,13 @@ function containsAggregate(expr) {
|
|
|
186
214
|
*/
|
|
187
215
|
export function selectToAlgebra(query, _options) {
|
|
188
216
|
const registry = new VariableRegistry();
|
|
217
|
+
// Promote bindings only when the top-level WHERE would reject rows without
|
|
218
|
+
// them. This keeps human-like SPARQL for null-rejecting filters without
|
|
219
|
+
// over-constraining OR cases that can still match through other branches.
|
|
220
|
+
const requiredPropertyKeys = query.where
|
|
221
|
+
? collectRequiredBindingKeys(query.where)
|
|
222
|
+
: new Set();
|
|
223
|
+
const requiredPropertyTriples = [];
|
|
189
224
|
// Track property triples that need to be added as OPTIONAL
|
|
190
225
|
const optionalPropertyTriples = [];
|
|
191
226
|
// Track filtered traversals (inline where) — these get their own OPTIONAL blocks
|
|
@@ -218,14 +253,14 @@ export function selectToAlgebra(query, _options) {
|
|
|
218
253
|
// to discover any additional property_expr references.
|
|
219
254
|
// Properties already registered by inline filters (above) will be skipped.
|
|
220
255
|
for (const item of query.projection) {
|
|
221
|
-
processExpressionForProperties(item.expression, registry, optionalPropertyTriples);
|
|
256
|
+
processExpressionForProperties(item.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
222
257
|
}
|
|
223
258
|
if (query.where) {
|
|
224
|
-
processExpressionForProperties(query.where, registry, optionalPropertyTriples);
|
|
259
|
+
processExpressionForProperties(query.where, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
225
260
|
}
|
|
226
261
|
if (query.orderBy) {
|
|
227
262
|
for (const orderItem of query.orderBy) {
|
|
228
|
-
processExpressionForProperties(orderItem.expression, registry, optionalPropertyTriples);
|
|
263
|
+
processExpressionForProperties(orderItem.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
229
264
|
}
|
|
230
265
|
}
|
|
231
266
|
// 5. Build the algebra tree
|
|
@@ -233,7 +268,7 @@ export function selectToAlgebra(query, _options) {
|
|
|
233
268
|
// - Wrap each optional property triple in a LeftJoin
|
|
234
269
|
const requiredBgp = {
|
|
235
270
|
type: 'bgp',
|
|
236
|
-
triples: [...requiredTriples, ...traverseTriples],
|
|
271
|
+
triples: [...requiredTriples, ...traverseTriples, ...requiredPropertyTriples],
|
|
237
272
|
};
|
|
238
273
|
let algebra = requiredBgp;
|
|
239
274
|
// 5b. Build filtered OPTIONAL blocks for inline where traversals.
|
|
@@ -469,39 +504,42 @@ function processPattern(pattern, registry, traverseTriples, optionalPropertyTrip
|
|
|
469
504
|
// ---------------------------------------------------------------------------
|
|
470
505
|
// Expression processing — discover property_expr references
|
|
471
506
|
// ---------------------------------------------------------------------------
|
|
472
|
-
function processExpressionForProperties(expr, registry, optionalPropertyTriples) {
|
|
507
|
+
function processExpressionForProperties(expr, registry, optionalPropertyTriples, requiredPropertyTriples = [], requiredPropertyKeys = new Set()) {
|
|
473
508
|
switch (expr.kind) {
|
|
474
509
|
case 'property_expr': {
|
|
475
510
|
if (!registry.has(expr.sourceAlias, expr.property)) {
|
|
476
|
-
// Create a new OPTIONAL triple for this property
|
|
477
511
|
const varName = registry.getOrCreate(expr.sourceAlias, expr.property);
|
|
478
512
|
const predicate = expr.pathExpr
|
|
479
513
|
? { kind: 'path', value: pathExprToSparql(expr.pathExpr), uris: collectPathUris(expr.pathExpr) }
|
|
480
514
|
: iriTerm(expr.property);
|
|
481
|
-
|
|
515
|
+
const triple = tripleOf(varTerm(expr.sourceAlias), predicate, varTerm(varName));
|
|
516
|
+
const triples = requiredPropertyKeys.has(bindingKey(expr.sourceAlias, expr.property))
|
|
517
|
+
? requiredPropertyTriples
|
|
518
|
+
: optionalPropertyTriples;
|
|
519
|
+
triples.push(triple);
|
|
482
520
|
}
|
|
483
521
|
break;
|
|
484
522
|
}
|
|
485
523
|
case 'binary_expr':
|
|
486
|
-
processExpressionForProperties(expr.left, registry, optionalPropertyTriples);
|
|
487
|
-
processExpressionForProperties(expr.right, registry, optionalPropertyTriples);
|
|
524
|
+
processExpressionForProperties(expr.left, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
525
|
+
processExpressionForProperties(expr.right, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
488
526
|
break;
|
|
489
527
|
case 'logical_expr':
|
|
490
528
|
for (const sub of expr.expressions) {
|
|
491
|
-
processExpressionForProperties(sub, registry, optionalPropertyTriples);
|
|
529
|
+
processExpressionForProperties(sub, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
492
530
|
}
|
|
493
531
|
break;
|
|
494
532
|
case 'not_expr':
|
|
495
|
-
processExpressionForProperties(expr.expression, registry, optionalPropertyTriples);
|
|
533
|
+
processExpressionForProperties(expr.expression, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
496
534
|
break;
|
|
497
535
|
case 'function_expr':
|
|
498
536
|
for (const arg of expr.args) {
|
|
499
|
-
processExpressionForProperties(arg, registry, optionalPropertyTriples);
|
|
537
|
+
processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
500
538
|
}
|
|
501
539
|
break;
|
|
502
540
|
case 'aggregate_expr':
|
|
503
541
|
for (const arg of expr.args) {
|
|
504
|
-
processExpressionForProperties(arg, registry, optionalPropertyTriples);
|
|
542
|
+
processExpressionForProperties(arg, registry, optionalPropertyTriples, requiredPropertyTriples, requiredPropertyKeys);
|
|
505
543
|
}
|
|
506
544
|
break;
|
|
507
545
|
case 'exists_expr':
|
|
@@ -513,10 +551,14 @@ function processExpressionForProperties(expr, registry, optionalPropertyTriples)
|
|
|
513
551
|
// Context entity property — emit a triple with fixed IRI as subject.
|
|
514
552
|
// Use raw IRI as registry key to avoid collision between IRIs that
|
|
515
553
|
// sanitize to the same string (e.g. ctx-1 vs ctx_1).
|
|
516
|
-
const ctxKey =
|
|
554
|
+
const ctxKey = contextAliasKey(expr.contextIri);
|
|
517
555
|
if (!registry.has(ctxKey, expr.property)) {
|
|
518
556
|
const varName = registry.getOrCreate(ctxKey, expr.property);
|
|
519
|
-
|
|
557
|
+
const triple = tripleOf(iriTerm(expr.contextIri), iriTerm(expr.property), varTerm(varName));
|
|
558
|
+
const triples = requiredPropertyKeys.has(bindingKey(ctxKey, expr.property))
|
|
559
|
+
? requiredPropertyTriples
|
|
560
|
+
: optionalPropertyTriples;
|
|
561
|
+
triples.push(triple);
|
|
520
562
|
}
|
|
521
563
|
break;
|
|
522
564
|
}
|
|
@@ -527,6 +569,37 @@ function processExpressionForProperties(expr, registry, optionalPropertyTriples)
|
|
|
527
569
|
break;
|
|
528
570
|
}
|
|
529
571
|
}
|
|
572
|
+
/**
|
|
573
|
+
* Compute which bindings are mandatory for a top-level FILTER to keep a row.
|
|
574
|
+
* AND makes either side required; OR only keeps bindings required by every branch.
|
|
575
|
+
*/
|
|
576
|
+
function collectRequiredBindingKeys(expr) {
|
|
577
|
+
switch (expr.kind) {
|
|
578
|
+
case 'property_expr':
|
|
579
|
+
return new Set([bindingKey(expr.sourceAlias, expr.property)]);
|
|
580
|
+
case 'context_property_expr':
|
|
581
|
+
return new Set([bindingKey(contextAliasKey(expr.contextIri), expr.property)]);
|
|
582
|
+
case 'binary_expr':
|
|
583
|
+
return mergeKeySets(collectRequiredBindingKeys(expr.left), collectRequiredBindingKeys(expr.right));
|
|
584
|
+
case 'function_expr':
|
|
585
|
+
return mergeKeySets(...expr.args.map((arg) => collectRequiredBindingKeys(arg)));
|
|
586
|
+
case 'not_expr':
|
|
587
|
+
return collectRequiredBindingKeys(expr.expression);
|
|
588
|
+
case 'logical_expr': {
|
|
589
|
+
const childSets = expr.expressions.map((sub) => collectRequiredBindingKeys(sub));
|
|
590
|
+
if (expr.operator === 'and') {
|
|
591
|
+
return mergeKeySets(...childSets);
|
|
592
|
+
}
|
|
593
|
+
return intersectKeySets(childSets);
|
|
594
|
+
}
|
|
595
|
+
case 'aggregate_expr':
|
|
596
|
+
case 'exists_expr':
|
|
597
|
+
case 'literal_expr':
|
|
598
|
+
case 'reference_expr':
|
|
599
|
+
case 'alias_expr':
|
|
600
|
+
return new Set();
|
|
601
|
+
}
|
|
602
|
+
}
|
|
530
603
|
// ---------------------------------------------------------------------------
|
|
531
604
|
// Expression conversion
|
|
532
605
|
// ---------------------------------------------------------------------------
|