@undefineds.co/xpod 0.3.18 → 0.3.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/config/bun.json +57 -11
- package/config/cloud.json +14 -12
- package/config/local.json +16 -14
- package/config/xpod.json +47 -9
- package/dist/api/matrix/PodMatrixStore.d.ts +4 -7
- package/dist/api/matrix/PodMatrixStore.js +116 -148
- package/dist/api/matrix/PodMatrixStore.js.map +1 -1
- package/dist/api/matrix/types.d.ts +2 -0
- package/dist/api/matrix/types.js.map +1 -1
- package/dist/components/components.jsonld +3 -0
- package/dist/components/context.jsonld +71 -32
- package/dist/http/SubgraphSparqlHttpHandler.d.ts +1 -0
- package/dist/http/SubgraphSparqlHttpHandler.js +27 -4
- package/dist/http/SubgraphSparqlHttpHandler.js.map +1 -1
- package/dist/http/SubgraphSparqlHttpHandler.jsonld +4 -0
- package/dist/http/vector/VectorHttpHandler.d.ts +5 -1
- package/dist/http/vector/VectorHttpHandler.js +5 -5
- package/dist/http/vector/VectorHttpHandler.js.map +1 -1
- package/dist/http/vector/VectorHttpHandler.jsonld +40 -28
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -4
- package/dist/index.js.map +1 -1
- package/dist/runtime/Proxy.d.ts +3 -0
- package/dist/runtime/Proxy.js +31 -7
- package/dist/runtime/Proxy.js.map +1 -1
- package/dist/storage/SparqlUpdateResourceStore.js +94 -33
- package/dist/storage/SparqlUpdateResourceStore.js.map +1 -1
- package/dist/storage/accessors/MixDataAccessor.d.ts +22 -5
- package/dist/storage/accessors/MixDataAccessor.js +376 -61
- package/dist/storage/accessors/MixDataAccessor.js.map +1 -1
- package/dist/storage/accessors/MixDataAccessor.jsonld +73 -5
- package/dist/storage/accessors/QuadstoreSparqlDataAccessor.js +32 -10
- package/dist/storage/accessors/QuadstoreSparqlDataAccessor.js.map +1 -1
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js +28 -6
- package/dist/storage/accessors/QuintStoreSparqlDataAccessor.js.map +1 -1
- package/dist/storage/accessors/SolidRdfDataAccessor.d.ts +45 -0
- package/dist/storage/accessors/SolidRdfDataAccessor.js +277 -0
- package/dist/storage/accessors/SolidRdfDataAccessor.js.map +1 -0
- package/dist/storage/accessors/SolidRdfDataAccessor.jsonld +161 -0
- package/dist/storage/rdf/Rdf3xIndex.d.ts +122 -0
- package/dist/storage/rdf/Rdf3xIndex.js +2695 -0
- package/dist/storage/rdf/Rdf3xIndex.js.map +1 -0
- package/dist/storage/rdf/Rdf3xIndex.jsonld +528 -0
- package/dist/storage/rdf/Rdf3xSchema.d.ts +20 -0
- package/dist/storage/rdf/Rdf3xSchema.js +65 -0
- package/dist/storage/rdf/Rdf3xSchema.js.map +1 -0
- package/dist/storage/rdf/RdfLocalQueryEngine.d.ts +10 -4
- package/dist/storage/rdf/RdfLocalQueryEngine.js +607 -127
- package/dist/storage/rdf/RdfLocalQueryEngine.js.map +1 -1
- package/dist/storage/rdf/RdfQuadIndex.d.ts +12 -1
- package/dist/storage/rdf/RdfQuadIndex.js +152 -22
- package/dist/storage/rdf/RdfQuadIndex.js.map +1 -1
- package/dist/storage/rdf/RdfQuadIndex.jsonld +36 -4
- package/dist/storage/rdf/RdfSparqlAdapter.d.ts +20 -2
- package/dist/storage/rdf/RdfSparqlAdapter.js +364 -40
- package/dist/storage/rdf/RdfSparqlAdapter.js.map +1 -1
- package/dist/storage/rdf/RdfSparqlAdapter.jsonld +60 -0
- package/dist/storage/rdf/RdfTermDictionary.d.ts +8 -0
- package/dist/storage/rdf/RdfTermDictionary.js +141 -70
- package/dist/storage/rdf/RdfTermDictionary.js.map +1 -1
- package/dist/storage/rdf/RdfTermDictionary.jsonld +24 -0
- package/dist/storage/rdf/RdfTextIndex.js +10 -3
- package/dist/storage/rdf/RdfTextIndex.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.d.ts +15 -6
- package/dist/storage/rdf/SolidRdfEngine.js +218 -25
- package/dist/storage/rdf/SolidRdfEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfEngine.jsonld +70 -7
- package/dist/storage/rdf/SolidRdfSparqlEngine.d.ts +11 -7
- package/dist/storage/rdf/SolidRdfSparqlEngine.js +60 -47
- package/dist/storage/rdf/SolidRdfSparqlEngine.js.map +1 -1
- package/dist/storage/rdf/SolidRdfSparqlEngine.jsonld +9 -5
- package/dist/storage/rdf/index.d.ts +2 -2
- package/dist/storage/rdf/index.js +3 -3
- package/dist/storage/rdf/index.js.map +1 -1
- package/dist/storage/rdf/models-benchmark.d.ts +12 -1
- package/dist/storage/rdf/models-benchmark.js +549 -32
- package/dist/storage/rdf/models-benchmark.js.map +1 -1
- package/dist/storage/rdf/types.d.ts +81 -7
- package/dist/storage/rdf/types.js.map +1 -1
- package/dist/storage/sparql/CompatibilitySparqlEngine.d.ts +36 -0
- package/dist/storage/sparql/CompatibilitySparqlEngine.js +96 -0
- package/dist/storage/sparql/CompatibilitySparqlEngine.js.map +1 -0
- package/dist/storage/sparql/CompatibilitySparqlEngine.jsonld +123 -0
- package/dist/storage/sparql/CompatibilitySparqlEngineImpl.d.ts +35 -0
- package/dist/storage/sparql/CompatibilitySparqlEngineImpl.js +112 -0
- package/dist/storage/sparql/CompatibilitySparqlEngineImpl.js.map +1 -0
- package/dist/storage/sparql/SubgraphQueryEngine.d.ts +1 -36
- package/dist/storage/sparql/SubgraphQueryEngine.js +2 -115
- package/dist/storage/sparql/SubgraphQueryEngine.js.map +1 -1
- package/dist/storage/sparql/SubgraphQueryEngine.jsonld +1 -124
- package/dist/terminal/AclPermissionService.d.ts +2 -1
- package/dist/terminal/AclPermissionService.js +26 -3
- package/dist/terminal/AclPermissionService.js.map +1 -1
- package/dist/terminal/TerminalSessionManager.js +25 -3
- package/dist/terminal/TerminalSessionManager.js.map +1 -1
- package/package.json +1 -1
- package/dist/storage/rdf/Rdf3xTripleIndex.d.ts +0 -55
- package/dist/storage/rdf/Rdf3xTripleIndex.js +0 -1235
- package/dist/storage/rdf/Rdf3xTripleIndex.js.map +0 -1
|
@@ -50,19 +50,46 @@ class RdfLocalQueryEngine {
|
|
|
50
50
|
const joinBasicAggregatePushdown = this.joinBasicAggregatePushdown(query, requiredPatterns, requiredFilters, aggregates);
|
|
51
51
|
let groupedAggregatePushed = false;
|
|
52
52
|
if (countPushdown) {
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
const
|
|
53
|
+
const distinctKeys = countPushdown.distinctKeys ?? [];
|
|
54
|
+
const distinctKey = distinctKeys.length === 1 ? distinctKeys[0] : undefined;
|
|
55
|
+
const distinctTupleKeys = distinctKeys.length > 1 ? distinctKeys : undefined;
|
|
56
|
+
const useRdf3xPrimary = this.canUseRdf3xPrimaryScan(countPushdown.pattern) && !distinctTupleKeys;
|
|
57
|
+
let rdf3xCount;
|
|
58
|
+
if (useRdf3xPrimary) {
|
|
59
|
+
const rdf3xPattern = toRdf3xTriplePattern(countPushdown.pattern);
|
|
60
|
+
if (distinctKey) {
|
|
61
|
+
rdf3xCount = this.rdf3xPrimaryIndex.countDistinct(rdf3xPattern, distinctKey);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
const rdf3xCountScan = this.rdf3xPrimaryIndex.scan(rdf3xPattern, { limit: 0 });
|
|
65
|
+
rdf3xCount = {
|
|
66
|
+
count: rdf3xCountScan.metrics.matchedRows,
|
|
67
|
+
metrics: rdf3xCountScan.metrics,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const countEstimate = !useRdf3xPrimary && distinctTupleKeys
|
|
72
|
+
? this.index.countDistinctTuple(countPushdown.pattern, distinctTupleKeys)
|
|
73
|
+
: !useRdf3xPrimary && distinctKey
|
|
74
|
+
? this.index.countDistinct(countPushdown.pattern, distinctKey)
|
|
75
|
+
: undefined;
|
|
76
|
+
const count = rdf3xCount?.count ?? countEstimate?.rows ?? this.index.count(countPushdown.pattern);
|
|
57
77
|
const result = countLiteral(count);
|
|
58
78
|
metrics.scannedRows = count;
|
|
59
79
|
metrics.joinedRows = count;
|
|
60
80
|
metrics.returnedRows = 1;
|
|
61
81
|
metrics.durationMs = Date.now() - start;
|
|
62
|
-
metrics.indexChoices.push('count');
|
|
82
|
+
metrics.indexChoices.push(useRdf3xPrimary ? rdf3xCount.metrics.indexChoice : 'count');
|
|
63
83
|
metrics.filtersPushedDown += countPushdown.pushedDownFilters;
|
|
64
|
-
|
|
65
|
-
|
|
84
|
+
if (rdf3xCount) {
|
|
85
|
+
metrics.plan.push(...storagePlanMarkers(rdf3xCount.metrics.queryPlan));
|
|
86
|
+
metrics.plan.push(`${distinctKey ? 'Rdf3xPrimaryCountDistinct' : 'Rdf3xPrimaryCount'}(${describePattern(requiredPatterns[0])})`);
|
|
87
|
+
metrics.plan.push(distinctKey ? 'Aggregate(count-distinct-rdf3x-primary)' : 'Aggregate(count-rdf3x-primary)');
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
metrics.plan.push(`IndexCount(${describePattern(requiredPatterns[0])})`);
|
|
91
|
+
metrics.plan.push(distinctTupleKeys ? 'Aggregate(count-distinct-tuple-index)' : distinctKey ? 'Aggregate(count-distinct-index)' : 'Aggregate(count-index)');
|
|
92
|
+
}
|
|
66
93
|
return {
|
|
67
94
|
bindings: [{ [countPushdown.as]: result }],
|
|
68
95
|
count,
|
|
@@ -70,9 +97,14 @@ class RdfLocalQueryEngine {
|
|
|
70
97
|
};
|
|
71
98
|
}
|
|
72
99
|
if (joinCountPushdown) {
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
100
|
+
const useRdf3xPrimary = this.canUseRdf3xPrimaryJoin(joinCountPushdown.patterns);
|
|
101
|
+
const scan = useRdf3xPrimary
|
|
102
|
+
? this.rdf3xPrimaryIndex.countJoinPatterns(joinCountPushdown.patterns, {
|
|
103
|
+
aggregates,
|
|
104
|
+
})
|
|
105
|
+
: this.index.countJoinPatterns(joinCountPushdown.patterns, {
|
|
106
|
+
aggregates,
|
|
107
|
+
});
|
|
76
108
|
const firstAggregate = aggregates[0];
|
|
77
109
|
const firstCount = firstAggregate ? Number(scan.bindings[0]?.[firstAggregate.as]?.value ?? 0) : 0;
|
|
78
110
|
metrics.scannedRows = scan.metrics.matchedRows;
|
|
@@ -81,11 +113,11 @@ class RdfLocalQueryEngine {
|
|
|
81
113
|
metrics.durationMs = Date.now() - start;
|
|
82
114
|
metrics.indexChoices.push(scan.metrics.indexChoice);
|
|
83
115
|
metrics.filtersPushedDown += joinCountPushdown.pushedDownFilters;
|
|
84
|
-
if (joinCountPushdown.reorderPlan) {
|
|
116
|
+
if (!useRdf3xPrimary && joinCountPushdown.reorderPlan) {
|
|
85
117
|
metrics.plan.push(joinCountPushdown.reorderPlan);
|
|
86
118
|
}
|
|
87
119
|
metrics.plan.push(...storagePlanMarkers(scan.metrics.queryPlan));
|
|
88
|
-
metrics.plan.push(
|
|
120
|
+
metrics.plan.push(`${useRdf3xPrimary ? 'Rdf3xPrimaryJoinCount' : 'IndexJoinCount'}(${joinCountPushdown.patterns.map((source) => describePatternSource(source)).join('|')})`);
|
|
89
121
|
metrics.plan.push(aggregatePlan(aggregates, false));
|
|
90
122
|
metrics.plan.push(aggregates.some((aggregate) => aggregate.distinct)
|
|
91
123
|
? 'Aggregate(join-count-distinct-index)'
|
|
@@ -97,20 +129,25 @@ class RdfLocalQueryEngine {
|
|
|
97
129
|
};
|
|
98
130
|
}
|
|
99
131
|
if (joinBasicAggregatePushdown) {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
132
|
+
const useRdf3xPrimary = this.canUseRdf3xPrimaryJoin(joinBasicAggregatePushdown.patterns);
|
|
133
|
+
const scan = useRdf3xPrimary
|
|
134
|
+
? this.rdf3xPrimaryIndex.aggregateJoinPatterns(joinBasicAggregatePushdown.patterns, {
|
|
135
|
+
aggregates,
|
|
136
|
+
})
|
|
137
|
+
: this.index.aggregateJoinPatterns(joinBasicAggregatePushdown.patterns, {
|
|
138
|
+
aggregates,
|
|
139
|
+
});
|
|
103
140
|
metrics.scannedRows = scan.metrics.matchedRows;
|
|
104
141
|
metrics.joinedRows = scan.metrics.matchedRows;
|
|
105
142
|
metrics.returnedRows = scan.bindings.length;
|
|
106
143
|
metrics.durationMs = Date.now() - start;
|
|
107
144
|
metrics.indexChoices.push(scan.metrics.indexChoice);
|
|
108
145
|
metrics.filtersPushedDown += joinBasicAggregatePushdown.pushedDownFilters;
|
|
109
|
-
if (joinBasicAggregatePushdown.reorderPlan) {
|
|
146
|
+
if (!useRdf3xPrimary && joinBasicAggregatePushdown.reorderPlan) {
|
|
110
147
|
metrics.plan.push(joinBasicAggregatePushdown.reorderPlan);
|
|
111
148
|
}
|
|
112
149
|
metrics.plan.push(...storagePlanMarkers(scan.metrics.queryPlan));
|
|
113
|
-
metrics.plan.push(
|
|
150
|
+
metrics.plan.push(`${useRdf3xPrimary ? 'Rdf3xPrimaryJoinAggregate' : 'IndexJoinAggregate'}(${joinBasicAggregatePushdown.patterns.map((source) => describePatternSource(source)).join('|')})`);
|
|
114
151
|
metrics.plan.push(aggregatePlan(aggregates, false));
|
|
115
152
|
metrics.plan.push(aggregates.length > 1
|
|
116
153
|
? 'Aggregate(join-basic-multi-index)'
|
|
@@ -123,31 +160,35 @@ class RdfLocalQueryEngine {
|
|
|
123
160
|
const remainingSources = buildRequiredSources(requiredPatterns, query);
|
|
124
161
|
const requiredBgpPushdown = this.requiredBgpPushdown(query, requiredPatterns, requiredFilters);
|
|
125
162
|
if (groupAggregatePushdown) {
|
|
163
|
+
const rdf3xGroupAggregatePatterns = groupAggregatePushdown.countOnly
|
|
164
|
+
? groupAggregatePushdown.patterns
|
|
165
|
+
: stripRdf3xNumericAggregateGuards(groupAggregatePushdown.patterns, aggregates);
|
|
166
|
+
const useRdf3xPrimary = this.canUseRdf3xPrimaryJoin(rdf3xGroupAggregatePatterns);
|
|
167
|
+
const groupOptions = {
|
|
168
|
+
groupBy: query.groupBy ?? [],
|
|
169
|
+
aggregates,
|
|
170
|
+
...(groupAggregatePushdown.having ? { having: groupAggregatePushdown.having } : {}),
|
|
171
|
+
...(groupAggregatePushdown.orderPushed ? { orderBy: query.orderBy } : {}),
|
|
172
|
+
...(groupAggregatePushdown.paginationPushed && query.limit !== undefined ? { limit: Math.max(0, query.limit) } : {}),
|
|
173
|
+
...(groupAggregatePushdown.paginationPushed && query.offset !== undefined ? { offset: Math.max(0, query.offset) } : {}),
|
|
174
|
+
};
|
|
126
175
|
const scan = groupAggregatePushdown.countOnly
|
|
127
|
-
?
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
:
|
|
136
|
-
groupBy: query.groupBy ?? [],
|
|
137
|
-
aggregates,
|
|
138
|
-
...(groupAggregatePushdown.having ? { having: groupAggregatePushdown.having } : {}),
|
|
139
|
-
...(groupAggregatePushdown.orderPushed ? { orderBy: query.orderBy } : {}),
|
|
140
|
-
...(groupAggregatePushdown.paginationPushed && query.limit !== undefined ? { limit: Math.max(0, query.limit) } : {}),
|
|
141
|
-
...(groupAggregatePushdown.paginationPushed && query.offset !== undefined ? { offset: Math.max(0, query.offset) } : {}),
|
|
142
|
-
});
|
|
143
|
-
const groupPlanPrefix = groupAggregatePushdown.countOnly ? 'IndexGroupCount' : 'IndexGroupAggregate';
|
|
176
|
+
? useRdf3xPrimary
|
|
177
|
+
? this.rdf3xPrimaryIndex.groupCountJoinPatterns(rdf3xGroupAggregatePatterns, groupOptions)
|
|
178
|
+
: this.index.groupCountJoinPatterns(groupAggregatePushdown.patterns, groupOptions)
|
|
179
|
+
: useRdf3xPrimary
|
|
180
|
+
? this.rdf3xPrimaryIndex.groupAggregateJoinPatterns(rdf3xGroupAggregatePatterns, groupOptions)
|
|
181
|
+
: this.index.groupAggregateJoinPatterns(groupAggregatePushdown.patterns, groupOptions);
|
|
182
|
+
const groupPlanPrefix = useRdf3xPrimary
|
|
183
|
+
? groupAggregatePushdown.countOnly ? 'Rdf3xPrimaryGroupCount' : 'Rdf3xPrimaryGroupAggregate'
|
|
184
|
+
: groupAggregatePushdown.countOnly ? 'IndexGroupCount' : 'IndexGroupAggregate';
|
|
144
185
|
bindings = scan.bindings;
|
|
145
186
|
metrics.scannedRows += scan.metrics.matchedRows;
|
|
146
187
|
metrics.joinedRows = scan.metrics.matchedRows;
|
|
147
188
|
metrics.indexChoices.push(scan.metrics.indexChoice);
|
|
148
189
|
metrics.filtersPushedDown += groupAggregatePushdown.pushedDownFilters;
|
|
149
190
|
metrics.filtersPushedDown += groupAggregatePushdown.pushedDownHaving;
|
|
150
|
-
if (groupAggregatePushdown.reorderPlan) {
|
|
191
|
+
if (!useRdf3xPrimary && groupAggregatePushdown.reorderPlan) {
|
|
151
192
|
metrics.plan.push(groupAggregatePushdown.reorderPlan);
|
|
152
193
|
}
|
|
153
194
|
metrics.plan.push(...storagePlanMarkers(scan.metrics.queryPlan));
|
|
@@ -175,6 +216,7 @@ class RdfLocalQueryEngine {
|
|
|
175
216
|
const scanOptions = {
|
|
176
217
|
...(requiredBgpPushdown.project ? { project: requiredBgpPushdown.project } : {}),
|
|
177
218
|
...(requiredBgpPushdown.distinctPushed ? { distinct: true } : {}),
|
|
219
|
+
...(requiredBgpPushdown.values ? { values: requiredBgpPushdown.values } : {}),
|
|
178
220
|
...(requiredBgpPushdown.orderPushed ? { orderBy: query.orderBy } : {}),
|
|
179
221
|
...(requiredBgpPushdown.paginationPushed && query.limit !== undefined ? { limit: Math.max(0, query.limit) } : {}),
|
|
180
222
|
...(requiredBgpPushdown.paginationPushed && query.offset !== undefined ? { offset: Math.max(0, query.offset) } : {}),
|
|
@@ -343,14 +385,17 @@ class RdfLocalQueryEngine {
|
|
|
343
385
|
const sourceVariables = variablesInRequiredSource(source);
|
|
344
386
|
const connected = sourceVariables.length === 0
|
|
345
387
|
|| sourceVariables.some((variableName) => boundVariables.has(variableName));
|
|
388
|
+
const estimate = this.estimateSource(source, bindings, filters, metrics);
|
|
346
389
|
return {
|
|
347
390
|
index,
|
|
348
391
|
disconnectedPenalty: hasBoundVariables && !connected ? 1 : 0,
|
|
349
|
-
estimatedRows:
|
|
392
|
+
estimatedRows: estimate.rows,
|
|
393
|
+
estimatedCostRows: estimate.costRows,
|
|
350
394
|
rank: this.sourceRank(source, sampleBinding),
|
|
351
395
|
};
|
|
352
396
|
});
|
|
353
397
|
choices.sort((left, right) => (left.disconnectedPenalty - right.disconnectedPenalty
|
|
398
|
+
|| left.estimatedCostRows - right.estimatedCostRows
|
|
354
399
|
|| left.estimatedRows - right.estimatedRows
|
|
355
400
|
|| left.rank - right.rank
|
|
356
401
|
|| left.index - right.index));
|
|
@@ -424,33 +469,33 @@ class RdfLocalQueryEngine {
|
|
|
424
469
|
})
|
|
425
470
|
.map((slot) => ({ key: slot.key, variable: slot.value.variable }));
|
|
426
471
|
}
|
|
427
|
-
|
|
472
|
+
estimateSource(source, bindings, filters, metrics) {
|
|
428
473
|
if (source.kind === 'pattern') {
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
return
|
|
474
|
+
const rows = source.tupleValues
|
|
475
|
+
? this.estimateTuplePatternRows(source, bindings, filters, metrics)
|
|
476
|
+
: this.estimatePatternRows(source.pattern, bindings, filters, metrics);
|
|
477
|
+
return { rows, costRows: rows };
|
|
433
478
|
}
|
|
434
479
|
if (source.kind === 'values') {
|
|
435
|
-
|
|
480
|
+
const rows = source.source.rows.length * Math.max(1, bindings.length);
|
|
481
|
+
return { rows, costRows: rows };
|
|
436
482
|
}
|
|
437
483
|
const sample = (bindings.length > 0 ? bindings : [{}]).slice(0, PLANNER_SAMPLE_BINDINGS);
|
|
438
484
|
if (!this.searchSourceHasBoundVariables(source, sample)) {
|
|
439
|
-
|
|
440
|
-
const estimate = source.kind === 'text'
|
|
441
|
-
? this.estimateTextSearchRows(source)
|
|
442
|
-
: this.estimateVectorSearchRows(source);
|
|
443
|
-
return estimate * Math.max(1, bindings.length);
|
|
485
|
+
return this.estimateUnboundSearchSource(source, bindings, metrics);
|
|
444
486
|
}
|
|
445
487
|
const sourceVariable = source.pattern.source;
|
|
446
488
|
const boundSourceEstimate = sourceVariable && this.canUseBoundSourceSearch(sample, source, sourceVariable)
|
|
447
489
|
? this.estimateSearchRowsByBoundSource(source, sample, metrics)
|
|
448
490
|
: undefined;
|
|
449
491
|
if (boundSourceEstimate !== undefined) {
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
return
|
|
492
|
+
const rows = bindings.length > sample.length && sample.length > 0
|
|
493
|
+
? Math.ceil(boundSourceEstimate * (bindings.length / sample.length))
|
|
494
|
+
: boundSourceEstimate;
|
|
495
|
+
return { rows, costRows: rows };
|
|
496
|
+
}
|
|
497
|
+
if (hasSearchWindow(source)) {
|
|
498
|
+
return this.estimateUnboundSearchSource(source, bindings, metrics);
|
|
454
499
|
}
|
|
455
500
|
const results = source.kind === 'text'
|
|
456
501
|
? this.textSearchResults(source)
|
|
@@ -467,9 +512,30 @@ class RdfLocalQueryEngine {
|
|
|
467
512
|
}
|
|
468
513
|
}
|
|
469
514
|
if (bindings.length > sample.length && sample.length > 0) {
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
515
|
+
const estimatedRows = Math.ceil(rows * (bindings.length / sample.length));
|
|
516
|
+
return { rows: estimatedRows, costRows: estimatedRows };
|
|
517
|
+
}
|
|
518
|
+
return { rows, costRows: rows };
|
|
519
|
+
}
|
|
520
|
+
estimateUnboundSearchSource(source, bindings, metrics) {
|
|
521
|
+
metrics.searchCardinalityEstimates = (metrics.searchCardinalityEstimates ?? 0) + 1;
|
|
522
|
+
const rows = source.kind === 'text'
|
|
523
|
+
? this.estimateTextSearchRows(source)
|
|
524
|
+
: this.estimateVectorSearchRows(source);
|
|
525
|
+
const bindingCount = Math.max(1, bindings.length);
|
|
526
|
+
if (!hasSearchWindow(source)) {
|
|
527
|
+
const estimatedRows = rows * bindingCount;
|
|
528
|
+
return { rows: estimatedRows, costRows: estimatedRows };
|
|
529
|
+
}
|
|
530
|
+
metrics.searchCardinalityEstimates = (metrics.searchCardinalityEstimates ?? 0) + 1;
|
|
531
|
+
const candidateRows = source.kind === 'text'
|
|
532
|
+
? this.estimateTextSearchRows(source, undefined, false)
|
|
533
|
+
: this.estimateVectorSearchRows(source, undefined, false);
|
|
534
|
+
const estimatedRows = rows * bindingCount;
|
|
535
|
+
return {
|
|
536
|
+
rows: estimatedRows,
|
|
537
|
+
costRows: candidateRows + estimatedRows,
|
|
538
|
+
};
|
|
473
539
|
}
|
|
474
540
|
estimateTuplePatternRows(source, bindings, filters, metrics) {
|
|
475
541
|
const sample = (bindings.length > 0 ? bindings : [{}]).slice(0, PLANNER_SAMPLE_BINDINGS);
|
|
@@ -530,18 +596,18 @@ class RdfLocalQueryEngine {
|
|
|
530
596
|
}
|
|
531
597
|
return sawBoundSource ? rows : undefined;
|
|
532
598
|
}
|
|
533
|
-
estimateTextSearchRows(source, exactSource) {
|
|
599
|
+
estimateTextSearchRows(source, exactSource, includeWindow = true) {
|
|
534
600
|
if (!this.textIndex) {
|
|
535
601
|
throw new Error('RdfLocalQuery textSearch requires a configured RdfTextIndex');
|
|
536
602
|
}
|
|
537
|
-
const options = this.textSearchOptions(source.pattern, exactSource);
|
|
603
|
+
const options = this.textSearchOptions(source.pattern, exactSource, includeWindow);
|
|
538
604
|
return options ? this.textIndex.estimateSearchCardinality(options).rows : 0;
|
|
539
605
|
}
|
|
540
|
-
estimateVectorSearchRows(source, exactSource) {
|
|
606
|
+
estimateVectorSearchRows(source, exactSource, includeWindow = true) {
|
|
541
607
|
if (!this.vectorIndex) {
|
|
542
608
|
throw new Error('RdfLocalQuery vectorSearch requires a configured RdfVectorIndex');
|
|
543
609
|
}
|
|
544
|
-
const options = this.vectorSearchOptions(source.pattern, exactSource);
|
|
610
|
+
const options = this.vectorSearchOptions(source.pattern, exactSource, includeWindow);
|
|
545
611
|
return options ? this.vectorIndex.estimateSearchCardinality(options).rows : 0;
|
|
546
612
|
}
|
|
547
613
|
boundVariables(bindings) {
|
|
@@ -589,15 +655,16 @@ class RdfLocalQueryEngine {
|
|
|
589
655
|
joinRequiredSource(input, source, filters, metrics, singleScanPushdown) {
|
|
590
656
|
switch (source.kind) {
|
|
591
657
|
case 'pattern': {
|
|
592
|
-
const
|
|
593
|
-
|
|
658
|
+
const result = this.joinPattern(input, source, filters, metrics, false, singleScanPushdown?.options);
|
|
659
|
+
const scanPlan = requiredSourceScanPlan(result.scanBackend);
|
|
660
|
+
metrics.plan.push(`${scanPlan}(${describePattern(source.pattern)})`);
|
|
594
661
|
if (singleScanPushdown?.orderPushed) {
|
|
595
|
-
metrics.plan.push(
|
|
662
|
+
metrics.plan.push(`${scanPlanOrder(scanPlan)}(${describeScanOrder(singleScanPushdown.options)})`);
|
|
596
663
|
}
|
|
597
664
|
if (singleScanPushdown?.paginationPushed) {
|
|
598
|
-
metrics.plan.push(
|
|
665
|
+
metrics.plan.push(scanPlanLimit(scanPlan));
|
|
599
666
|
}
|
|
600
|
-
return bindings;
|
|
667
|
+
return result.bindings;
|
|
601
668
|
}
|
|
602
669
|
case 'text': {
|
|
603
670
|
const bindings = this.joinTextSearch(input, source, metrics);
|
|
@@ -636,10 +703,18 @@ class RdfLocalQueryEngine {
|
|
|
636
703
|
return [binding];
|
|
637
704
|
}
|
|
638
705
|
}
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
706
|
+
const grouped = (optionalGroup.values?.length ?? 0) === 0
|
|
707
|
+
? this.joinPatternGroupRdf3x(matches, optionalGroup.patterns, optionalFilters, metrics, true)
|
|
708
|
+
: undefined;
|
|
709
|
+
if (grouped) {
|
|
710
|
+
matches = grouped.bindings;
|
|
711
|
+
}
|
|
712
|
+
else {
|
|
713
|
+
for (const pattern of optionalGroup.patterns) {
|
|
714
|
+
matches = this.joinPattern(matches, { kind: 'pattern', pattern, originalIndex: -1 }, optionalFilters, metrics, true).bindings;
|
|
715
|
+
if (matches.length === 0) {
|
|
716
|
+
return [binding];
|
|
717
|
+
}
|
|
643
718
|
}
|
|
644
719
|
}
|
|
645
720
|
for (const unionGroup of optionalGroup.unions ?? []) {
|
|
@@ -702,8 +777,26 @@ class RdfLocalQueryEngine {
|
|
|
702
777
|
if (matches.length === 0) {
|
|
703
778
|
continue;
|
|
704
779
|
}
|
|
705
|
-
|
|
706
|
-
|
|
780
|
+
const grouped = (branch.values?.length ?? 0) === 0
|
|
781
|
+
? this.joinPatternGroupRdf3x(matches, branch.patterns, branchFilters, metrics, false)
|
|
782
|
+
: undefined;
|
|
783
|
+
if (grouped) {
|
|
784
|
+
matches = grouped.bindings;
|
|
785
|
+
}
|
|
786
|
+
else {
|
|
787
|
+
for (const pattern of branch.patterns) {
|
|
788
|
+
matches = this.joinPattern(matches, { kind: 'pattern', pattern, originalIndex: -1 }, branchFilters, metrics, false).bindings;
|
|
789
|
+
if (matches.length === 0) {
|
|
790
|
+
break;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
if (matches.length === 0) {
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
for (const unionGroup of branch.unions ?? []) {
|
|
798
|
+
matches = this.joinUnionGroup(matches, unionGroup.branches, branchFilters, metrics);
|
|
799
|
+
metrics.plan.push(`UnionNested(${unionGroup.branches.map((nestedBranch) => nestedBranch.patterns.map(describePattern).join(',')).join('|')})`);
|
|
707
800
|
if (matches.length === 0) {
|
|
708
801
|
break;
|
|
709
802
|
}
|
|
@@ -757,10 +850,18 @@ class RdfLocalQueryEngine {
|
|
|
757
850
|
for (const binding of input) {
|
|
758
851
|
let matches = [binding];
|
|
759
852
|
matches = this.applyDependentValues(matches, minusGroup.values, metrics, 'Minus');
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
853
|
+
const grouped = (minusGroup.values?.length ?? 0) === 0
|
|
854
|
+
? this.joinPatternGroupRdf3x(matches, minusGroup.patterns, filters, metrics, false)
|
|
855
|
+
: undefined;
|
|
856
|
+
if (grouped) {
|
|
857
|
+
matches = grouped.bindings;
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
for (const pattern of minusGroup.patterns) {
|
|
861
|
+
matches = this.joinPattern(matches, { kind: 'pattern', pattern, originalIndex: -1 }, filters, metrics, false).bindings;
|
|
862
|
+
if (matches.length === 0) {
|
|
863
|
+
break;
|
|
864
|
+
}
|
|
764
865
|
}
|
|
765
866
|
}
|
|
766
867
|
if (matches.length > 0) {
|
|
@@ -798,10 +899,18 @@ class RdfLocalQueryEngine {
|
|
|
798
899
|
for (const binding of input) {
|
|
799
900
|
let matches = [binding];
|
|
800
901
|
matches = this.applyDependentValues(matches, existsGroup.values, metrics, 'Exists');
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
902
|
+
const grouped = (existsGroup.values?.length ?? 0) === 0
|
|
903
|
+
? this.joinPatternGroupRdf3x(matches, existsGroup.patterns, filters, metrics, false)
|
|
904
|
+
: undefined;
|
|
905
|
+
if (grouped) {
|
|
906
|
+
matches = grouped.bindings;
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
for (const pattern of existsGroup.patterns) {
|
|
910
|
+
matches = this.joinPattern(matches, { kind: 'pattern', pattern, originalIndex: -1 }, filters, metrics, false).bindings;
|
|
911
|
+
if (matches.length === 0) {
|
|
912
|
+
break;
|
|
913
|
+
}
|
|
805
914
|
}
|
|
806
915
|
}
|
|
807
916
|
if (matches.length > 0) {
|
|
@@ -833,8 +942,60 @@ class RdfLocalQueryEngine {
|
|
|
833
942
|
}
|
|
834
943
|
return output;
|
|
835
944
|
}
|
|
945
|
+
joinPatternGroupRdf3x(input, patterns, filters, metrics, optional) {
|
|
946
|
+
if (!this.rdf3xPrimaryIndex || patterns.length < 2) {
|
|
947
|
+
return undefined;
|
|
948
|
+
}
|
|
949
|
+
const compiledByBinding = input.map((binding) => {
|
|
950
|
+
const compiled = patterns.map((pattern) => this.compileJoinPatternForBinding(pattern, binding, filters));
|
|
951
|
+
return {
|
|
952
|
+
binding,
|
|
953
|
+
patterns: compiled,
|
|
954
|
+
};
|
|
955
|
+
});
|
|
956
|
+
if (compiledByBinding.some((entry) => (entry.patterns.some((pattern) => !pattern)
|
|
957
|
+
|| !this.canUseRdf3xPrimaryJoin(entry.patterns)))) {
|
|
958
|
+
return undefined;
|
|
959
|
+
}
|
|
960
|
+
const output = [];
|
|
961
|
+
for (const entry of compiledByBinding) {
|
|
962
|
+
const compiled = entry.patterns;
|
|
963
|
+
const scan = this.rdf3xPrimaryIndex.joinPatterns(compiled);
|
|
964
|
+
metrics.scannedRows += scan.metrics.matchedRows;
|
|
965
|
+
metrics.indexChoices.push(scan.metrics.indexChoice);
|
|
966
|
+
metrics.filtersPushedDown += uniqueNumbers(compiled.flatMap((pattern) => pattern.pattern.pushedDownFilterIndexes)).length;
|
|
967
|
+
metrics.plan.push(...storagePlanMarkers(scan.metrics.queryPlan));
|
|
968
|
+
const remainingFilters = filtersWithoutIndexes(filters, uniqueNumbers(compiled.flatMap((pattern) => pattern.pattern.pushedDownFilterIndexes)));
|
|
969
|
+
const before = output.length;
|
|
970
|
+
for (const row of scan.bindings) {
|
|
971
|
+
const next = mergeBindingRows(entry.binding, row);
|
|
972
|
+
if (next && this.matchesNewlyBoundFilters(next, entry.binding, remainingFilters)) {
|
|
973
|
+
output.push(next);
|
|
974
|
+
}
|
|
975
|
+
}
|
|
976
|
+
if (optional && output.length === before) {
|
|
977
|
+
output.push(entry.binding);
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
return {
|
|
981
|
+
bindings: output,
|
|
982
|
+
scanBackend: 'rdf3x',
|
|
983
|
+
};
|
|
984
|
+
}
|
|
985
|
+
compileJoinPatternForBinding(pattern, binding, filters) {
|
|
986
|
+
const variables = {};
|
|
987
|
+
for (const key of TERM_KEYS) {
|
|
988
|
+
const value = pattern[key];
|
|
989
|
+
if (isVariable(value) && !binding[value.variable]) {
|
|
990
|
+
variables[key] = value.variable;
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
const compiled = this.compilePattern(pattern, binding, filters);
|
|
994
|
+
return compiled ? { pattern: compiled, variables } : undefined;
|
|
995
|
+
}
|
|
836
996
|
joinPattern(input, source, filters, metrics, optional, scanOptions) {
|
|
837
997
|
const output = [];
|
|
998
|
+
const backends = new Set();
|
|
838
999
|
const { pattern } = source;
|
|
839
1000
|
for (const binding of input) {
|
|
840
1001
|
const compiled = this.compilePattern(pattern, binding, filters);
|
|
@@ -845,9 +1006,8 @@ class RdfLocalQueryEngine {
|
|
|
845
1006
|
continue;
|
|
846
1007
|
}
|
|
847
1008
|
const tupleValues = this.tupleValuesForBinding(source, binding);
|
|
848
|
-
const scan = tupleValues
|
|
849
|
-
|
|
850
|
-
: this.index.scan(compiled, scanOptions);
|
|
1009
|
+
const scan = this.scanCompiledPattern(compiled, tupleValues, scanOptions);
|
|
1010
|
+
backends.add(scan.metrics.engine === 'solid-rdf3x' ? 'rdf3x' : 'index');
|
|
851
1011
|
metrics.scannedRows += scan.metrics.matchedRows;
|
|
852
1012
|
metrics.indexChoices.push(scan.metrics.indexChoice);
|
|
853
1013
|
metrics.filtersPushedDown += compiled.pushedDownFilters;
|
|
@@ -860,11 +1020,32 @@ class RdfLocalQueryEngine {
|
|
|
860
1020
|
}
|
|
861
1021
|
}
|
|
862
1022
|
}
|
|
863
|
-
return
|
|
1023
|
+
return {
|
|
1024
|
+
bindings: output,
|
|
1025
|
+
scanBackend: backends.size === 0
|
|
1026
|
+
? 'none'
|
|
1027
|
+
: backends.size === 1
|
|
1028
|
+
? [...backends][0]
|
|
1029
|
+
: 'mixed',
|
|
1030
|
+
};
|
|
1031
|
+
}
|
|
1032
|
+
scanCompiledPattern(compiled, tupleValues, scanOptions) {
|
|
1033
|
+
if (this.canUseRdf3xPrimaryScan(compiled, scanOptions)) {
|
|
1034
|
+
const rdf3xPattern = toRdf3xTriplePattern(compiled);
|
|
1035
|
+
return tupleValues
|
|
1036
|
+
? this.rdf3xPrimaryIndex.scanWithTupleConstraints(rdf3xPattern, tupleValues, toRdf3xScanOptions(scanOptions))
|
|
1037
|
+
: this.rdf3xPrimaryIndex.scan(rdf3xPattern, toRdf3xScanOptions(scanOptions));
|
|
1038
|
+
}
|
|
1039
|
+
return tupleValues
|
|
1040
|
+
? this.index.scanWithTupleConstraints(compiled, tupleValues, scanOptions)
|
|
1041
|
+
: this.index.scan(compiled, scanOptions);
|
|
1042
|
+
}
|
|
1043
|
+
canUseRdf3xPrimaryScan(pattern, scanOptions) {
|
|
1044
|
+
return Boolean(this.rdf3xPrimaryIndex)
|
|
1045
|
+
&& isRdf3xCompatiblePattern(pattern);
|
|
864
1046
|
}
|
|
865
1047
|
requiredBgpPushdown(query, requiredPatterns, filters) {
|
|
866
1048
|
if (requiredPatterns.length < 1
|
|
867
|
-
|| (query.values?.length ?? 0) > 0
|
|
868
1049
|
|| (query.textSearch?.length ?? 0) > 0
|
|
869
1050
|
|| (query.vectorSearch?.length ?? 0) > 0
|
|
870
1051
|
|| (query.unions?.length ?? 0) > 0
|
|
@@ -881,6 +1062,12 @@ class RdfLocalQueryEngine {
|
|
|
881
1062
|
if (!this.canPushRequiredBgpFilters(requiredPatterns, filters)) {
|
|
882
1063
|
return undefined;
|
|
883
1064
|
}
|
|
1065
|
+
const values = query.values?.length
|
|
1066
|
+
? this.requiredBgpValuesPushdown(query.values, requiredPatterns)
|
|
1067
|
+
: undefined;
|
|
1068
|
+
if ((query.values?.length ?? 0) > 0 && !values) {
|
|
1069
|
+
return undefined;
|
|
1070
|
+
}
|
|
884
1071
|
const distinctProject = query.distinct
|
|
885
1072
|
? this.requiredBgpDistinctProject(query, requiredPatterns, filters)
|
|
886
1073
|
: undefined;
|
|
@@ -899,9 +1086,13 @@ class RdfLocalQueryEngine {
|
|
|
899
1086
|
if (compiled.some((entry) => !entry)) {
|
|
900
1087
|
return undefined;
|
|
901
1088
|
}
|
|
1089
|
+
if (values && !this.canUseRdf3xPrimaryJoin(compiled)) {
|
|
1090
|
+
return undefined;
|
|
1091
|
+
}
|
|
902
1092
|
const reordered = this.reorderJoinPatterns(requiredPatterns, compiled, filters);
|
|
903
1093
|
return {
|
|
904
1094
|
patterns: reordered.patterns,
|
|
1095
|
+
...(values ? { values } : {}),
|
|
905
1096
|
...(reordered.reorderPlan ? { reorderPlan: reordered.reorderPlan } : {}),
|
|
906
1097
|
orderPushed,
|
|
907
1098
|
paginationPushed: query.limit !== undefined || query.offset !== undefined,
|
|
@@ -910,6 +1101,21 @@ class RdfLocalQueryEngine {
|
|
|
910
1101
|
pushedDownFilters: filters.length,
|
|
911
1102
|
};
|
|
912
1103
|
}
|
|
1104
|
+
requiredBgpValuesPushdown(values, requiredPatterns) {
|
|
1105
|
+
const visibleVariables = new Set(requiredPatterns.flatMap((pattern) => variablesInPattern(pattern)));
|
|
1106
|
+
for (const source of values) {
|
|
1107
|
+
if (source.variables.length === 0 || new Set(source.variables).size !== source.variables.length) {
|
|
1108
|
+
return undefined;
|
|
1109
|
+
}
|
|
1110
|
+
if (source.variables.some((variableName) => !visibleVariables.has(variableName))) {
|
|
1111
|
+
return undefined;
|
|
1112
|
+
}
|
|
1113
|
+
if (source.rows.some((row) => source.variables.some((variableName) => !row[variableName]))) {
|
|
1114
|
+
return undefined;
|
|
1115
|
+
}
|
|
1116
|
+
}
|
|
1117
|
+
return values;
|
|
1118
|
+
}
|
|
913
1119
|
requiredBgpDistinctProject(query, requiredPatterns, filters) {
|
|
914
1120
|
const visibleVariables = new Set(requiredPatterns.flatMap((pattern) => variablesInPattern(pattern)));
|
|
915
1121
|
const projectedVariables = uniqueStrings(query.select && query.select.length > 0
|
|
@@ -1170,18 +1376,18 @@ class RdfLocalQueryEngine {
|
|
|
1170
1376
|
}
|
|
1171
1377
|
return results;
|
|
1172
1378
|
}
|
|
1173
|
-
textSearchOptions(pattern, exactSource) {
|
|
1379
|
+
textSearchOptions(pattern, exactSource, includeWindow = true) {
|
|
1174
1380
|
return {
|
|
1175
1381
|
query: pattern.query,
|
|
1176
1382
|
source: exactSource,
|
|
1177
1383
|
workspace: pattern.scope?.workspace,
|
|
1178
1384
|
sourcePrefix: pattern.scope?.sourcePrefix,
|
|
1179
|
-
limit: pattern.limit,
|
|
1180
|
-
offset: pattern.offset,
|
|
1385
|
+
limit: includeWindow ? pattern.limit : undefined,
|
|
1386
|
+
offset: includeWindow ? pattern.offset : undefined,
|
|
1181
1387
|
orderBy: pattern.orderBy,
|
|
1182
1388
|
};
|
|
1183
1389
|
}
|
|
1184
|
-
vectorSearchOptions(pattern, exactSource) {
|
|
1390
|
+
vectorSearchOptions(pattern, exactSource, includeWindow = true) {
|
|
1185
1391
|
return {
|
|
1186
1392
|
embedding: pattern.embedding,
|
|
1187
1393
|
metric: pattern.metric,
|
|
@@ -1189,8 +1395,8 @@ class RdfLocalQueryEngine {
|
|
|
1189
1395
|
source: exactSource,
|
|
1190
1396
|
workspace: pattern.scope?.workspace,
|
|
1191
1397
|
sourcePrefix: pattern.scope?.sourcePrefix,
|
|
1192
|
-
limit: pattern.limit,
|
|
1193
|
-
offset: pattern.offset,
|
|
1398
|
+
limit: includeWindow ? pattern.limit : undefined,
|
|
1399
|
+
offset: includeWindow ? pattern.offset : undefined,
|
|
1194
1400
|
threshold: pattern.threshold,
|
|
1195
1401
|
orderBy: pattern.orderBy,
|
|
1196
1402
|
};
|
|
@@ -1230,6 +1436,19 @@ class RdfLocalQueryEngine {
|
|
|
1230
1436
|
const value = this.evaluateBindExpression(expression.expression, binding);
|
|
1231
1437
|
return value ? n3_1.DataFactory.literal(value.value.toLocaleUpperCase('en-US')) : undefined;
|
|
1232
1438
|
}
|
|
1439
|
+
case 'coalesce': {
|
|
1440
|
+
for (const item of expression.expressions) {
|
|
1441
|
+
const value = this.evaluateBindExpression(item, binding);
|
|
1442
|
+
if (value) {
|
|
1443
|
+
return value;
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
return undefined;
|
|
1447
|
+
}
|
|
1448
|
+
case 'if':
|
|
1449
|
+
return this.matchesFilters(binding, expression.condition)
|
|
1450
|
+
? this.evaluateBindExpression(expression.then, binding)
|
|
1451
|
+
: this.evaluateBindExpression(expression.else, binding);
|
|
1233
1452
|
case 'substring': {
|
|
1234
1453
|
const value = this.evaluateBindExpression(expression.expression, binding);
|
|
1235
1454
|
const startTerm = this.evaluateBindExpression(expression.start, binding);
|
|
@@ -1261,6 +1480,22 @@ class RdfLocalQueryEngine {
|
|
|
1261
1480
|
return undefined;
|
|
1262
1481
|
}
|
|
1263
1482
|
}
|
|
1483
|
+
case 'strdt': {
|
|
1484
|
+
const lexical = this.evaluateBindExpression(expression.lexical, binding);
|
|
1485
|
+
const datatype = this.evaluateBindExpression(expression.datatype, binding);
|
|
1486
|
+
if (!lexical || !datatype || datatype.termType !== 'NamedNode') {
|
|
1487
|
+
return undefined;
|
|
1488
|
+
}
|
|
1489
|
+
return n3_1.DataFactory.literal(lexical.value, n3_1.DataFactory.namedNode(datatype.value));
|
|
1490
|
+
}
|
|
1491
|
+
case 'strlang': {
|
|
1492
|
+
const lexical = this.evaluateBindExpression(expression.lexical, binding);
|
|
1493
|
+
const language = this.evaluateBindExpression(expression.language, binding);
|
|
1494
|
+
if (!lexical || !language) {
|
|
1495
|
+
return undefined;
|
|
1496
|
+
}
|
|
1497
|
+
return n3_1.DataFactory.literal(lexical.value, language.value);
|
|
1498
|
+
}
|
|
1264
1499
|
default: {
|
|
1265
1500
|
const exhaustive = expression;
|
|
1266
1501
|
throw new Error(`Unsupported RDF local BIND expression: ${JSON.stringify(exhaustive)}`);
|
|
@@ -1329,10 +1564,10 @@ class RdfLocalQueryEngine {
|
|
|
1329
1564
|
if (aggregate.variable && !variablesInPattern(pattern).includes(aggregate.variable)) {
|
|
1330
1565
|
return undefined;
|
|
1331
1566
|
}
|
|
1332
|
-
const
|
|
1333
|
-
? this.
|
|
1567
|
+
const distinctKeys = aggregate.distinct
|
|
1568
|
+
? this.distinctCountKeys(pattern, aggregate.variable, aggregate.distinctVariables)
|
|
1334
1569
|
: undefined;
|
|
1335
|
-
if (aggregate.distinct && !
|
|
1570
|
+
if (aggregate.distinct && (!distinctKeys || distinctKeys.length === 0)) {
|
|
1336
1571
|
return undefined;
|
|
1337
1572
|
}
|
|
1338
1573
|
if (!this.canPushAllFiltersForPattern(pattern, filters)) {
|
|
@@ -1343,7 +1578,7 @@ class RdfLocalQueryEngine {
|
|
|
1343
1578
|
? {
|
|
1344
1579
|
as: aggregate.as,
|
|
1345
1580
|
pattern: compiled,
|
|
1346
|
-
|
|
1581
|
+
distinctKeys,
|
|
1347
1582
|
pushedDownFilters: compiled.pushedDownFilters,
|
|
1348
1583
|
}
|
|
1349
1584
|
: undefined;
|
|
@@ -1540,15 +1775,30 @@ class RdfLocalQueryEngine {
|
|
|
1540
1775
|
}
|
|
1541
1776
|
return compiled;
|
|
1542
1777
|
}
|
|
1543
|
-
|
|
1544
|
-
if (
|
|
1545
|
-
|
|
1778
|
+
distinctCountKeys(pattern, variableName, distinctVariables) {
|
|
1779
|
+
if (variableName) {
|
|
1780
|
+
const keys = TERM_KEYS.filter((key) => {
|
|
1781
|
+
const value = pattern[key];
|
|
1782
|
+
return isVariable(value) && value.variable === variableName;
|
|
1783
|
+
});
|
|
1784
|
+
return keys.length === 1 ? keys : undefined;
|
|
1785
|
+
}
|
|
1786
|
+
if (distinctVariables) {
|
|
1787
|
+
const keys = [];
|
|
1788
|
+
for (const distinctVariable of distinctVariables) {
|
|
1789
|
+
const key = termKeyForVariable(pattern, distinctVariable);
|
|
1790
|
+
if (!key) {
|
|
1791
|
+
return undefined;
|
|
1792
|
+
}
|
|
1793
|
+
keys.push(key);
|
|
1794
|
+
}
|
|
1795
|
+
return uniquePatternKeys(keys);
|
|
1546
1796
|
}
|
|
1547
1797
|
const keys = TERM_KEYS.filter((key) => {
|
|
1548
1798
|
const value = pattern[key];
|
|
1549
|
-
return isVariable(value)
|
|
1799
|
+
return isVariable(value);
|
|
1550
1800
|
});
|
|
1551
|
-
return keys
|
|
1801
|
+
return uniquePatternKeys(keys);
|
|
1552
1802
|
}
|
|
1553
1803
|
scanOrderForPattern(pattern, orderBy) {
|
|
1554
1804
|
if (orderBy.length === 0) {
|
|
@@ -1647,12 +1897,12 @@ class RdfLocalQueryEngine {
|
|
|
1647
1897
|
}
|
|
1648
1898
|
return next;
|
|
1649
1899
|
}
|
|
1650
|
-
countBindings(bindings, variable, distinct) {
|
|
1900
|
+
countBindings(bindings, variable, distinct, distinctVariables) {
|
|
1651
1901
|
if (!distinct) {
|
|
1652
1902
|
return variable ? bindings.filter((binding) => binding[variable]).length : bindings.length;
|
|
1653
1903
|
}
|
|
1654
1904
|
if (!variable) {
|
|
1655
|
-
return new Set(bindings.map((binding) => bindingKey(binding))).size;
|
|
1905
|
+
return new Set(bindings.map((binding) => bindingKey(binding, distinctVariables))).size;
|
|
1656
1906
|
}
|
|
1657
1907
|
return new Set(bindings
|
|
1658
1908
|
.map((binding) => binding[variable])
|
|
@@ -1664,7 +1914,7 @@ class RdfLocalQueryEngine {
|
|
|
1664
1914
|
let firstCount = 0;
|
|
1665
1915
|
aggregates.forEach((aggregate, index) => {
|
|
1666
1916
|
const count = aggregate.type === 'count'
|
|
1667
|
-
? this.countBindings(bindings, aggregate.variable, aggregate.distinct)
|
|
1917
|
+
? this.countBindings(bindings, aggregate.variable, aggregate.distinct, aggregate.distinctVariables)
|
|
1668
1918
|
: 0;
|
|
1669
1919
|
if (index === 0) {
|
|
1670
1920
|
firstCount = count;
|
|
@@ -1710,7 +1960,7 @@ class RdfLocalQueryEngine {
|
|
|
1710
1960
|
}
|
|
1711
1961
|
aggregateLiteral(bindings, aggregate) {
|
|
1712
1962
|
if (aggregate.type === 'count') {
|
|
1713
|
-
return countLiteral(this.countBindings(bindings, aggregate.variable, aggregate.distinct));
|
|
1963
|
+
return countLiteral(this.countBindings(bindings, aggregate.variable, aggregate.distinct, aggregate.distinctVariables));
|
|
1714
1964
|
}
|
|
1715
1965
|
const values = this.numericAggregateValues(bindings, aggregate.variable, aggregate.distinct);
|
|
1716
1966
|
if (values.length === 0) {
|
|
@@ -1927,24 +2177,46 @@ class RdfLocalQueryEngine {
|
|
|
1927
2177
|
const text = filterStringValue(value, comparisonValue);
|
|
1928
2178
|
return typeof filter.value === 'string' && text.startsWith(filter.value);
|
|
1929
2179
|
}
|
|
2180
|
+
case '$notStartsWith': {
|
|
2181
|
+
const text = filterStringValue(value, comparisonValue);
|
|
2182
|
+
return typeof filter.value === 'string' && !text.startsWith(filter.value);
|
|
2183
|
+
}
|
|
1930
2184
|
case '$contains': {
|
|
1931
2185
|
const text = filterStringValue(value, comparisonValue);
|
|
1932
2186
|
return typeof filter.value === 'string' && text.includes(filter.value);
|
|
1933
2187
|
}
|
|
2188
|
+
case '$notContains': {
|
|
2189
|
+
const text = filterStringValue(value, comparisonValue);
|
|
2190
|
+
return typeof filter.value === 'string' && !text.includes(filter.value);
|
|
2191
|
+
}
|
|
1934
2192
|
case '$endsWith': {
|
|
1935
2193
|
const text = filterStringValue(value, comparisonValue);
|
|
1936
2194
|
return typeof filter.value === 'string' && text.endsWith(filter.value);
|
|
1937
2195
|
}
|
|
2196
|
+
case '$notEndsWith': {
|
|
2197
|
+
const text = filterStringValue(value, comparisonValue);
|
|
2198
|
+
return typeof filter.value === 'string' && !text.endsWith(filter.value);
|
|
2199
|
+
}
|
|
1938
2200
|
case '$regex': {
|
|
1939
2201
|
const text = filterStringValue(value, comparisonValue);
|
|
1940
2202
|
return typeof filter.value === 'string' && new RegExp(filter.value, filter.flags).test(text);
|
|
1941
2203
|
}
|
|
2204
|
+
case '$notRegex': {
|
|
2205
|
+
const text = filterStringValue(value, comparisonValue);
|
|
2206
|
+
return typeof filter.value === 'string' && !new RegExp(filter.value, filter.flags).test(text);
|
|
2207
|
+
}
|
|
1942
2208
|
case '$termType':
|
|
1943
2209
|
return typeof filter.value === 'string' && matchesTermType(value, filter.value);
|
|
2210
|
+
case '$notTermType':
|
|
2211
|
+
return typeof filter.value === 'string' && !matchesTermType(value, filter.value);
|
|
1944
2212
|
case '$sameTerm': {
|
|
1945
2213
|
const right = filter.variable2 ? binding[filter.variable2] : filter.value;
|
|
1946
2214
|
return Boolean(right && (0, types_1.isTerm)(right) && sameTerm(value, right));
|
|
1947
2215
|
}
|
|
2216
|
+
case '$notSameTerm': {
|
|
2217
|
+
const right = filter.variable2 ? binding[filter.variable2] : filter.value;
|
|
2218
|
+
return Boolean(right && (0, types_1.isTerm)(right) && !sameTerm(value, right));
|
|
2219
|
+
}
|
|
1948
2220
|
case '$lang':
|
|
1949
2221
|
return typeof filter.value === 'string'
|
|
1950
2222
|
&& value.termType === 'Literal'
|
|
@@ -1953,10 +2225,20 @@ class RdfLocalQueryEngine {
|
|
|
1953
2225
|
return typeof filter.value === 'string'
|
|
1954
2226
|
&& value.termType === 'Literal'
|
|
1955
2227
|
&& value.language !== filter.value;
|
|
2228
|
+
case '$langIn':
|
|
2229
|
+
return value.termType === 'Literal'
|
|
2230
|
+
&& (filter.values ?? []).some((candidate) => typeof candidate === 'string' && value.language === candidate);
|
|
2231
|
+
case '$notLangIn':
|
|
2232
|
+
return value.termType === 'Literal'
|
|
2233
|
+
&& !(filter.values ?? []).some((candidate) => typeof candidate === 'string' && value.language === candidate);
|
|
1956
2234
|
case '$langMatches':
|
|
1957
2235
|
return typeof filter.value === 'string'
|
|
1958
2236
|
&& value.termType === 'Literal'
|
|
1959
2237
|
&& langMatches(value.language, filter.value);
|
|
2238
|
+
case '$notLangMatches':
|
|
2239
|
+
return typeof filter.value === 'string'
|
|
2240
|
+
&& value.termType === 'Literal'
|
|
2241
|
+
&& !langMatches(value.language, filter.value);
|
|
1960
2242
|
case '$datatype':
|
|
1961
2243
|
return filter.value !== undefined
|
|
1962
2244
|
&& value.termType === 'Literal'
|
|
@@ -1965,6 +2247,12 @@ class RdfLocalQueryEngine {
|
|
|
1965
2247
|
return filter.value !== undefined
|
|
1966
2248
|
&& value.termType === 'Literal'
|
|
1967
2249
|
&& !sameTermOrLexical(value.datatype, filter.value);
|
|
2250
|
+
case '$datatypeIn':
|
|
2251
|
+
return value.termType === 'Literal'
|
|
2252
|
+
&& (filter.values ?? []).some((candidate) => sameTermOrLexical(value.datatype, candidate));
|
|
2253
|
+
case '$notDatatypeIn':
|
|
2254
|
+
return value.termType === 'Literal'
|
|
2255
|
+
&& !(filter.values ?? []).some((candidate) => sameTermOrLexical(value.datatype, candidate));
|
|
1968
2256
|
default: {
|
|
1969
2257
|
const exhaustive = filter.operator;
|
|
1970
2258
|
throw new Error(`Unsupported RDF local query filter operator: ${exhaustive}`);
|
|
@@ -2094,6 +2382,9 @@ function variablesInRequiredSource(source) {
|
|
|
2094
2382
|
function uniqueStrings(values) {
|
|
2095
2383
|
return [...new Set(values)];
|
|
2096
2384
|
}
|
|
2385
|
+
function uniqueNumbers(values) {
|
|
2386
|
+
return [...new Set(values)];
|
|
2387
|
+
}
|
|
2097
2388
|
function filtersWithoutIndexes(filters, indexes) {
|
|
2098
2389
|
if (indexes.length === 0) {
|
|
2099
2390
|
return filters;
|
|
@@ -2113,6 +2404,17 @@ function joinValuesSource(input, source) {
|
|
|
2113
2404
|
}
|
|
2114
2405
|
return output;
|
|
2115
2406
|
}
|
|
2407
|
+
function mergeBindingRows(left, right) {
|
|
2408
|
+
const next = { ...left };
|
|
2409
|
+
for (const [variableName, term] of Object.entries(right)) {
|
|
2410
|
+
const existing = next[variableName];
|
|
2411
|
+
if (existing && !sameTerm(existing, term)) {
|
|
2412
|
+
return null;
|
|
2413
|
+
}
|
|
2414
|
+
next[variableName] = term;
|
|
2415
|
+
}
|
|
2416
|
+
return next;
|
|
2417
|
+
}
|
|
2116
2418
|
function mergeTupleValuesBinding(binding, variables, row) {
|
|
2117
2419
|
const next = { ...binding };
|
|
2118
2420
|
for (const variableName of variables) {
|
|
@@ -2372,10 +2674,13 @@ function compareBindings(left, right, orderBy) {
|
|
|
2372
2674
|
}
|
|
2373
2675
|
return 0;
|
|
2374
2676
|
}
|
|
2375
|
-
function bindingKey(binding) {
|
|
2376
|
-
return Object.keys(binding)
|
|
2677
|
+
function bindingKey(binding, variables) {
|
|
2678
|
+
return [...(variables ?? Object.keys(binding))]
|
|
2377
2679
|
.sort()
|
|
2378
|
-
.map((key) =>
|
|
2680
|
+
.map((key) => {
|
|
2681
|
+
const term = binding[key];
|
|
2682
|
+
return `${key}=${term ? (0, n3_1.termToId)(term) : '__UNBOUND__'}`;
|
|
2683
|
+
})
|
|
2379
2684
|
.join('\u001f');
|
|
2380
2685
|
}
|
|
2381
2686
|
function distinctBindings(bindings) {
|
|
@@ -2510,6 +2815,10 @@ function describeBindExpression(expression) {
|
|
|
2510
2815
|
return `LCASE(${describeBindExpression(expression.expression)})`;
|
|
2511
2816
|
case 'upperCase':
|
|
2512
2817
|
return `UCASE(${describeBindExpression(expression.expression)})`;
|
|
2818
|
+
case 'coalesce':
|
|
2819
|
+
return `COALESCE(${expression.expressions.map(describeBindExpression).join(',')})`;
|
|
2820
|
+
case 'if':
|
|
2821
|
+
return `IF(${expression.condition.map(describeFilter).join('&')},${describeBindExpression(expression.then)},${describeBindExpression(expression.else)})`;
|
|
2513
2822
|
case 'substring':
|
|
2514
2823
|
return `SUBSTR(${[
|
|
2515
2824
|
describeBindExpression(expression.expression),
|
|
@@ -2520,6 +2829,10 @@ function describeBindExpression(expression) {
|
|
|
2520
2829
|
return `CONCAT(${expression.expressions.map(describeBindExpression).join(',')})`;
|
|
2521
2830
|
case 'iri':
|
|
2522
2831
|
return `IRI(${describeBindExpression(expression.expression)})`;
|
|
2832
|
+
case 'strdt':
|
|
2833
|
+
return `STRDT(${describeBindExpression(expression.lexical)},${describeBindExpression(expression.datatype)})`;
|
|
2834
|
+
case 'strlang':
|
|
2835
|
+
return `STRLANG(${describeBindExpression(expression.lexical)},${describeBindExpression(expression.language)})`;
|
|
2523
2836
|
default: {
|
|
2524
2837
|
const exhaustive = expression;
|
|
2525
2838
|
return JSON.stringify(exhaustive);
|
|
@@ -2561,61 +2874,225 @@ function storagePlanMarkers(queryPlan) {
|
|
|
2561
2874
|
|| entry.startsWith('JoinGroupAggregateHaving(')
|
|
2562
2875
|
|| entry.startsWith('JoinGroupAggregateNumeric(')));
|
|
2563
2876
|
}
|
|
2877
|
+
function requiredSourceScanPlan(backend) {
|
|
2878
|
+
switch (backend) {
|
|
2879
|
+
case 'rdf3x':
|
|
2880
|
+
return 'Rdf3xPrimaryScan';
|
|
2881
|
+
case 'mixed':
|
|
2882
|
+
return 'MixedScan';
|
|
2883
|
+
case 'index':
|
|
2884
|
+
case 'none':
|
|
2885
|
+
return 'IndexScan';
|
|
2886
|
+
default: {
|
|
2887
|
+
const exhaustive = backend;
|
|
2888
|
+
throw new Error(`Unsupported RDF required source scan backend: ${exhaustive}`);
|
|
2889
|
+
}
|
|
2890
|
+
}
|
|
2891
|
+
}
|
|
2892
|
+
function scanPlanOrder(plan) {
|
|
2893
|
+
switch (plan) {
|
|
2894
|
+
case 'Rdf3xPrimaryScan':
|
|
2895
|
+
return 'Rdf3xPrimaryOrder';
|
|
2896
|
+
case 'MixedScan':
|
|
2897
|
+
return 'MixedOrder';
|
|
2898
|
+
case 'IndexScan':
|
|
2899
|
+
return 'IndexOrder';
|
|
2900
|
+
default: {
|
|
2901
|
+
const exhaustive = plan;
|
|
2902
|
+
throw new Error(`Unsupported RDF scan plan for order marker: ${exhaustive}`);
|
|
2903
|
+
}
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
function scanPlanLimit(plan) {
|
|
2907
|
+
switch (plan) {
|
|
2908
|
+
case 'Rdf3xPrimaryScan':
|
|
2909
|
+
return 'Rdf3xPrimaryLimit';
|
|
2910
|
+
case 'MixedScan':
|
|
2911
|
+
return 'MixedLimit';
|
|
2912
|
+
case 'IndexScan':
|
|
2913
|
+
return 'IndexLimit';
|
|
2914
|
+
default: {
|
|
2915
|
+
const exhaustive = plan;
|
|
2916
|
+
throw new Error(`Unsupported RDF scan plan for limit marker: ${exhaustive}`);
|
|
2917
|
+
}
|
|
2918
|
+
}
|
|
2919
|
+
}
|
|
2920
|
+
function toRdf3xScanOptions(options) {
|
|
2921
|
+
if (!options) {
|
|
2922
|
+
return undefined;
|
|
2923
|
+
}
|
|
2924
|
+
return {
|
|
2925
|
+
...(options.order ? { order: options.order } : {}),
|
|
2926
|
+
...(options.orderDirections ? { orderDirections: options.orderDirections } : {}),
|
|
2927
|
+
...(options.reverse ? { reverse: true } : {}),
|
|
2928
|
+
...(options.limit !== undefined ? { limit: options.limit } : {}),
|
|
2929
|
+
...(options.offset !== undefined ? { offset: options.offset } : {}),
|
|
2930
|
+
};
|
|
2931
|
+
}
|
|
2932
|
+
function toRdf3xTriplePattern(pattern) {
|
|
2933
|
+
const result = {};
|
|
2934
|
+
for (const key of TERM_KEYS) {
|
|
2935
|
+
const value = pattern[key];
|
|
2936
|
+
if (!value) {
|
|
2937
|
+
continue;
|
|
2938
|
+
}
|
|
2939
|
+
if ((0, types_1.isTerm)(value)) {
|
|
2940
|
+
result[key] = value;
|
|
2941
|
+
continue;
|
|
2942
|
+
}
|
|
2943
|
+
if (isRdf3xTermInPattern(value)) {
|
|
2944
|
+
result[key] = value;
|
|
2945
|
+
continue;
|
|
2946
|
+
}
|
|
2947
|
+
if (isRdf3xTermNotInPattern(value)) {
|
|
2948
|
+
result[key] = value;
|
|
2949
|
+
continue;
|
|
2950
|
+
}
|
|
2951
|
+
if (isRdf3xCompatibleOperatorPattern(key, value)) {
|
|
2952
|
+
result[key] = value;
|
|
2953
|
+
continue;
|
|
2954
|
+
}
|
|
2955
|
+
throw new Error(`RDF-3X primary scan cannot compile unsupported ${key} pattern`);
|
|
2956
|
+
}
|
|
2957
|
+
return result;
|
|
2958
|
+
}
|
|
2959
|
+
function stripRdf3xNumericAggregateGuards(patterns, aggregates) {
|
|
2960
|
+
const numericVariables = new Set(aggregates
|
|
2961
|
+
.filter((aggregate) => aggregate.type !== 'count')
|
|
2962
|
+
.map((aggregate) => aggregate.variable)
|
|
2963
|
+
.filter((variableName) => Boolean(variableName)));
|
|
2964
|
+
if (numericVariables.size === 0) {
|
|
2965
|
+
return patterns;
|
|
2966
|
+
}
|
|
2967
|
+
return patterns.map((entry) => {
|
|
2968
|
+
let changed = false;
|
|
2969
|
+
const pattern = {
|
|
2970
|
+
...entry.pattern,
|
|
2971
|
+
pushedDownFilters: entry.pattern.pushedDownFilters,
|
|
2972
|
+
pushedDownFilterIndexes: [...entry.pattern.pushedDownFilterIndexes],
|
|
2973
|
+
};
|
|
2974
|
+
for (const key of TERM_KEYS) {
|
|
2975
|
+
const variableName = entry.variables[key];
|
|
2976
|
+
if (!variableName || !numericVariables.has(variableName)) {
|
|
2977
|
+
continue;
|
|
2978
|
+
}
|
|
2979
|
+
const value = pattern[key];
|
|
2980
|
+
if (!value || (0, types_1.isTerm)(value) || typeof value !== 'object') {
|
|
2981
|
+
continue;
|
|
2982
|
+
}
|
|
2983
|
+
if (value.$termType !== 'numeric') {
|
|
2984
|
+
continue;
|
|
2985
|
+
}
|
|
2986
|
+
const stripped = { ...value };
|
|
2987
|
+
delete stripped.$termType;
|
|
2988
|
+
if (Object.keys(stripped).length === 0) {
|
|
2989
|
+
delete pattern[key];
|
|
2990
|
+
}
|
|
2991
|
+
else {
|
|
2992
|
+
pattern[key] = stripped;
|
|
2993
|
+
}
|
|
2994
|
+
changed = true;
|
|
2995
|
+
}
|
|
2996
|
+
return changed ? { pattern, variables: entry.variables } : entry;
|
|
2997
|
+
});
|
|
2998
|
+
}
|
|
2564
2999
|
function isRdf3xCompatiblePattern(pattern) {
|
|
2565
3000
|
return TERM_KEYS.every((key) => {
|
|
2566
3001
|
const value = pattern[key];
|
|
2567
3002
|
if (!value || (0, types_1.isTerm)(value)) {
|
|
2568
3003
|
return true;
|
|
2569
3004
|
}
|
|
2570
|
-
if (
|
|
3005
|
+
if (isRdf3xTermInPattern(value)) {
|
|
2571
3006
|
return true;
|
|
2572
3007
|
}
|
|
2573
|
-
if (
|
|
3008
|
+
if (isRdf3xTermNotInPattern(value)) {
|
|
3009
|
+
return true;
|
|
3010
|
+
}
|
|
3011
|
+
if (isRdf3xCompatibleOperatorPattern(key, value)) {
|
|
2574
3012
|
return true;
|
|
2575
3013
|
}
|
|
2576
3014
|
return false;
|
|
2577
3015
|
});
|
|
2578
3016
|
}
|
|
2579
|
-
function
|
|
3017
|
+
function isRdf3xTermInPattern(value) {
|
|
2580
3018
|
return value !== null
|
|
2581
3019
|
&& typeof value === 'object'
|
|
3020
|
+
&& !('termType' in value)
|
|
2582
3021
|
&& Object.keys(value).length === 1
|
|
2583
|
-
&&
|
|
2584
|
-
&&
|
|
3022
|
+
&& Array.isArray(value.$in)
|
|
3023
|
+
&& (value.$in).length > 0
|
|
3024
|
+
&& (value.$in).every((entry) => (0, types_1.isTerm)(entry));
|
|
2585
3025
|
}
|
|
2586
|
-
function
|
|
3026
|
+
function isRdf3xTermNotInPattern(value) {
|
|
3027
|
+
return value !== null
|
|
3028
|
+
&& typeof value === 'object'
|
|
3029
|
+
&& !('termType' in value)
|
|
3030
|
+
&& Object.keys(value).length === 1
|
|
3031
|
+
&& Array.isArray(value.$notIn)
|
|
3032
|
+
&& (value.$notIn).length > 0
|
|
3033
|
+
&& (value.$notIn).every((entry) => (0, types_1.isTerm)(entry));
|
|
3034
|
+
}
|
|
3035
|
+
function isRdf3xCompatibleOperatorPattern(key, value) {
|
|
2587
3036
|
if (value === null || typeof value !== 'object' || 'termType' in value) {
|
|
2588
3037
|
return false;
|
|
2589
3038
|
}
|
|
2590
|
-
const
|
|
2591
|
-
|
|
3039
|
+
const allowed = new Set([
|
|
3040
|
+
'$in',
|
|
3041
|
+
'$notIn',
|
|
3042
|
+
'$termType',
|
|
3043
|
+
'$language',
|
|
3044
|
+
'$notLanguage',
|
|
3045
|
+
'$langMatches',
|
|
3046
|
+
'$datatype',
|
|
3047
|
+
'$notDatatype',
|
|
3048
|
+
...(key === 'graph' ? ['$startsWith'] : []),
|
|
3049
|
+
...(key === 'object' ? ['$gt', '$gte', '$lt', '$lte', '$contains', '$endsWith'] : []),
|
|
3050
|
+
]);
|
|
3051
|
+
if (Object.keys(value).length === 0 || Object.keys(value).some((operator) => !allowed.has(operator))) {
|
|
2592
3052
|
return false;
|
|
2593
3053
|
}
|
|
2594
|
-
|
|
2595
|
-
|
|
2596
|
-
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
|
|
2600
|
-
|
|
2601
|
-
|
|
3054
|
+
const operators = value;
|
|
3055
|
+
if (operators.$in !== undefined && !isRdf3xTermInPattern({ $in: operators.$in }))
|
|
3056
|
+
return false;
|
|
3057
|
+
if (operators.$notIn !== undefined && !isRdf3xTermNotInPattern({ $notIn: operators.$notIn }))
|
|
3058
|
+
return false;
|
|
3059
|
+
if (operators.$startsWith !== undefined && typeof operators.$startsWith !== 'string')
|
|
3060
|
+
return false;
|
|
3061
|
+
if (operators.$termType !== undefined && !['iri', 'blank', 'literal', 'numeric'].includes(operators.$termType))
|
|
3062
|
+
return false;
|
|
3063
|
+
for (const languageOperator of ['$language', '$notLanguage', '$langMatches']) {
|
|
3064
|
+
if (operators[languageOperator] !== undefined && typeof operators[languageOperator] !== 'string')
|
|
3065
|
+
return false;
|
|
3066
|
+
}
|
|
3067
|
+
for (const datatypeOperator of ['$datatype', '$notDatatype']) {
|
|
3068
|
+
const datatype = operators[datatypeOperator];
|
|
3069
|
+
if (datatype !== undefined && (!(0, types_1.isTerm)(datatype) || datatype.termType !== 'NamedNode'))
|
|
2602
3070
|
return false;
|
|
3071
|
+
}
|
|
3072
|
+
if (key === 'object') {
|
|
3073
|
+
for (const rangeOperator of ['$gt', '$gte', '$lt', '$lte']) {
|
|
3074
|
+
const rangeValue = operators[rangeOperator];
|
|
3075
|
+
if (rangeValue !== undefined && !isRdf3xObjectRangeValue(rangeValue))
|
|
3076
|
+
return false;
|
|
3077
|
+
}
|
|
3078
|
+
for (const textOperator of ['$contains', '$endsWith']) {
|
|
3079
|
+
if (operators[textOperator] !== undefined && typeof operators[textOperator] !== 'string')
|
|
3080
|
+
return false;
|
|
2603
3081
|
}
|
|
2604
3082
|
}
|
|
2605
|
-
return
|
|
3083
|
+
return true;
|
|
2606
3084
|
}
|
|
2607
|
-
function
|
|
3085
|
+
function isRdf3xObjectRangeValue(value) {
|
|
2608
3086
|
if (typeof value === 'number') {
|
|
2609
|
-
return Number.isFinite(value)
|
|
3087
|
+
return Number.isFinite(value);
|
|
2610
3088
|
}
|
|
2611
3089
|
if (typeof value === 'string') {
|
|
2612
|
-
|
|
2613
|
-
return Number.isFinite(parsed) ? parsed : undefined;
|
|
3090
|
+
return true;
|
|
2614
3091
|
}
|
|
2615
3092
|
if ((0, types_1.isTerm)(value)) {
|
|
2616
|
-
return
|
|
3093
|
+
return true;
|
|
2617
3094
|
}
|
|
2618
|
-
return
|
|
3095
|
+
return false;
|
|
2619
3096
|
}
|
|
2620
3097
|
function bindTextSearchResult(binding, pattern, result) {
|
|
2621
3098
|
const next = { ...binding };
|
|
@@ -2702,4 +3179,7 @@ function aggregatePlan(aggregates, grouped) {
|
|
|
2702
3179
|
? 'Aggregate(count-multi-distinct)'
|
|
2703
3180
|
: 'Aggregate(count-multi)';
|
|
2704
3181
|
}
|
|
3182
|
+
function uniquePatternKeys(values) {
|
|
3183
|
+
return TERM_KEYS.filter((key) => values.includes(key));
|
|
3184
|
+
}
|
|
2705
3185
|
//# sourceMappingURL=RdfLocalQueryEngine.js.map
|