jinaga-server 1.1.3 → 1.1.4
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/dist/postgres/specification-result-sql.d.ts +0 -1
- package/dist/postgres/specification-result-sql.js +109 -92
- package/dist/postgres/specification-result-sql.js.map +1 -1
- package/docs/sql/SQLFromSpecification.md +394 -0
- package/docs/sql/example.sql +49 -0
- package/docs/sql/first-path.gv +9 -0
- package/docs/sql/first-path.png +0 -0
- package/docs/sql/model.gv +20 -0
- package/docs/sql/model.png +0 -0
- package/package.json +1 -1
- package/src/postgres/specification-result-sql.ts +150 -106
- package/test/postgres/postgresReadSpec.ts +289 -0
|
@@ -15,7 +15,6 @@ import { FactTypeMap, getFactTypeId, getRoleId, RoleMap } from "./maps";
|
|
|
15
15
|
|
|
16
16
|
interface SpecificationLabel {
|
|
17
17
|
name: string;
|
|
18
|
-
type: string;
|
|
19
18
|
index: number;
|
|
20
19
|
}
|
|
21
20
|
interface FactDescription {
|
|
@@ -28,9 +27,11 @@ interface EdgeDescription {
|
|
|
28
27
|
successorFactIndex: number;
|
|
29
28
|
roleParameter: number;
|
|
30
29
|
}
|
|
31
|
-
interface
|
|
30
|
+
interface ExistentialConditionDescription {
|
|
31
|
+
exists: boolean;
|
|
32
|
+
inputs: InputDescription[];
|
|
32
33
|
edges: EdgeDescription[];
|
|
33
|
-
|
|
34
|
+
existentialConditions: ExistentialConditionDescription[];
|
|
34
35
|
}
|
|
35
36
|
interface SpecificationSqlQuery {
|
|
36
37
|
sql: string;
|
|
@@ -41,8 +42,6 @@ interface SpecificationSqlQuery {
|
|
|
41
42
|
interface InputDescription {
|
|
42
43
|
label: string;
|
|
43
44
|
factIndex: number;
|
|
44
|
-
factTypeId: number;
|
|
45
|
-
factHash: string;
|
|
46
45
|
factTypeParameter: number;
|
|
47
46
|
factHashParameter: number;
|
|
48
47
|
}
|
|
@@ -52,8 +51,8 @@ interface OutputDescription {
|
|
|
52
51
|
factIndex: number;
|
|
53
52
|
}
|
|
54
53
|
|
|
55
|
-
function countEdges(
|
|
56
|
-
return
|
|
54
|
+
function countEdges(existentialConditions: ExistentialConditionDescription[]): number {
|
|
55
|
+
return existentialConditions.reduce((count, c) => count + c.edges.length + countEdges(c.existentialConditions),
|
|
57
56
|
0);
|
|
58
57
|
}
|
|
59
58
|
|
|
@@ -69,7 +68,7 @@ class QueryDescription {
|
|
|
69
68
|
private readonly outputs: OutputDescription[],
|
|
70
69
|
private readonly facts: FactDescription[],
|
|
71
70
|
private readonly edges: EdgeDescription[],
|
|
72
|
-
private readonly
|
|
71
|
+
private readonly existentialConditions: ExistentialConditionDescription[] = []
|
|
73
72
|
) {}
|
|
74
73
|
|
|
75
74
|
public withParameter(parameter: string | number): { query: QueryDescription; parameterIndex: number; } {
|
|
@@ -80,27 +79,57 @@ class QueryDescription {
|
|
|
80
79
|
this.outputs,
|
|
81
80
|
this.facts,
|
|
82
81
|
this.edges,
|
|
83
|
-
this.
|
|
82
|
+
this.existentialConditions
|
|
84
83
|
);
|
|
85
84
|
return { query, parameterIndex };
|
|
86
85
|
}
|
|
87
86
|
|
|
88
|
-
public withInputParameter(label: string): QueryDescription {
|
|
87
|
+
public withInputParameter(label: Label, factTypeId: number, factHash: string, path: number[]): { queryDescription: QueryDescription, factDescription: FactDescription } {
|
|
89
88
|
const factTypeParameter = this.parameters.length + 1;
|
|
90
89
|
const factHashParameter = factTypeParameter + 1;
|
|
91
|
-
const
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
:
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
90
|
+
const factIndex = this.facts.length + 1;
|
|
91
|
+
const factDescription: FactDescription = {
|
|
92
|
+
factIndex: factIndex,
|
|
93
|
+
type: label.type
|
|
94
|
+
};
|
|
95
|
+
const facts = [
|
|
96
|
+
...this.facts,
|
|
97
|
+
factDescription
|
|
98
|
+
]
|
|
99
|
+
const input: InputDescription = {
|
|
100
|
+
label: label.name,
|
|
101
|
+
factIndex,
|
|
102
|
+
factTypeParameter,
|
|
103
|
+
factHashParameter
|
|
104
|
+
};
|
|
105
|
+
const parameters = this.parameters.concat(factTypeId, factHash);
|
|
106
|
+
if (path.length === 0) {
|
|
107
|
+
const inputs = [
|
|
108
|
+
...this.inputs,
|
|
109
|
+
input
|
|
110
|
+
];
|
|
111
|
+
const queryDescription = new QueryDescription(
|
|
112
|
+
inputs,
|
|
113
|
+
parameters,
|
|
114
|
+
this.outputs,
|
|
115
|
+
facts,
|
|
116
|
+
this.edges,
|
|
117
|
+
this.existentialConditions
|
|
118
|
+
);
|
|
119
|
+
return { queryDescription, factDescription };
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const existentialConditions = existentialsWithInput(this.existentialConditions, input, path);
|
|
123
|
+
const queryDescription = new QueryDescription(
|
|
124
|
+
this.inputs,
|
|
125
|
+
parameters,
|
|
126
|
+
this.outputs,
|
|
127
|
+
facts,
|
|
128
|
+
this.edges,
|
|
129
|
+
existentialConditions
|
|
130
|
+
);
|
|
131
|
+
return { queryDescription, factDescription };
|
|
132
|
+
}
|
|
104
133
|
}
|
|
105
134
|
|
|
106
135
|
public withFact(type: string): { query: QueryDescription; factIndex: number; } {
|
|
@@ -112,7 +141,7 @@ class QueryDescription {
|
|
|
112
141
|
this.outputs,
|
|
113
142
|
this.facts.concat(fact),
|
|
114
143
|
this.edges,
|
|
115
|
-
this.
|
|
144
|
+
this.existentialConditions
|
|
116
145
|
);
|
|
117
146
|
return { query, factIndex };
|
|
118
147
|
}
|
|
@@ -125,14 +154,14 @@ class QueryDescription {
|
|
|
125
154
|
this.outputs.concat(output),
|
|
126
155
|
this.facts,
|
|
127
156
|
this.edges,
|
|
128
|
-
this.
|
|
157
|
+
this.existentialConditions
|
|
129
158
|
);
|
|
130
159
|
return query;
|
|
131
160
|
}
|
|
132
161
|
|
|
133
162
|
public withEdge(predecessorFactIndex: number, successorFactIndex: number, roleParameter: number, path: number[]) {
|
|
134
163
|
const edge = {
|
|
135
|
-
edgeIndex: this.edges.length + countEdges(this.
|
|
164
|
+
edgeIndex: this.edges.length + countEdges(this.existentialConditions) + 1,
|
|
136
165
|
predecessorFactIndex,
|
|
137
166
|
successorFactIndex,
|
|
138
167
|
roleParameter
|
|
@@ -144,7 +173,7 @@ class QueryDescription {
|
|
|
144
173
|
this.outputs,
|
|
145
174
|
this.facts,
|
|
146
175
|
this.edges.concat(edge),
|
|
147
|
-
this.
|
|
176
|
+
this.existentialConditions
|
|
148
177
|
)
|
|
149
178
|
: new QueryDescription(
|
|
150
179
|
this.inputs,
|
|
@@ -152,20 +181,20 @@ class QueryDescription {
|
|
|
152
181
|
this.outputs,
|
|
153
182
|
this.facts,
|
|
154
183
|
this.edges,
|
|
155
|
-
|
|
184
|
+
existentialsWithEdge(this.existentialConditions, edge, path)
|
|
156
185
|
);
|
|
157
186
|
return query;
|
|
158
187
|
}
|
|
159
188
|
|
|
160
|
-
public
|
|
161
|
-
const {
|
|
189
|
+
public withExistentialCondition(exists: boolean, path: number[]): { query: QueryDescription; path: number[]; } {
|
|
190
|
+
const { existentialConditions: newExistentialConditions, path: newPath } = existentialsWithNewCondition(this.existentialConditions, exists, path);
|
|
162
191
|
const query = new QueryDescription(
|
|
163
192
|
this.inputs,
|
|
164
193
|
this.parameters,
|
|
165
194
|
this.outputs,
|
|
166
195
|
this.facts,
|
|
167
196
|
this.edges,
|
|
168
|
-
|
|
197
|
+
newExistentialConditions
|
|
169
198
|
);
|
|
170
199
|
return { query, path: newPath };
|
|
171
200
|
}
|
|
@@ -197,16 +226,15 @@ class QueryDescription {
|
|
|
197
226
|
const writtenFactIndexes = new Set<number>().add(firstFactIndex);
|
|
198
227
|
const joins: string[] = generateJoins(this.edges, writtenFactIndexes);
|
|
199
228
|
const inputWhereClauses = this.inputs
|
|
200
|
-
.filter(input => input.factTypeParameter !== 0)
|
|
201
229
|
.map(input => `f${input.factIndex}.fact_type_id = $${input.factTypeParameter} AND f${input.factIndex}.hash = $${input.factHashParameter}`)
|
|
202
230
|
.join(" AND ");
|
|
203
|
-
const
|
|
204
|
-
.map(
|
|
231
|
+
const existentialWhereClauses = this.existentialConditions
|
|
232
|
+
.map(existentialCondition => ` AND ${existentialCondition.exists ? "EXISTS" : "NOT EXISTS"} (${generateExistentialWhereClause(existentialCondition, writtenFactIndexes)})`)
|
|
205
233
|
.join("");
|
|
206
234
|
const orderByClause = this.outputs
|
|
207
235
|
.map(output => `f${output.factIndex}.fact_id ASC`)
|
|
208
236
|
.join(", ");
|
|
209
|
-
const sql = `SELECT ${columns} FROM public.fact f${firstFactIndex}${joins.join("")} WHERE ${inputWhereClauses}${
|
|
237
|
+
const sql = `SELECT ${columns} FROM public.fact f${firstFactIndex}${joins.join("")} WHERE ${inputWhereClauses}${existentialWhereClauses} ORDER BY ${orderByClause}`;
|
|
210
238
|
return {
|
|
211
239
|
sql,
|
|
212
240
|
parameters: this.parameters,
|
|
@@ -220,50 +248,75 @@ class QueryDescription {
|
|
|
220
248
|
}
|
|
221
249
|
}
|
|
222
250
|
|
|
223
|
-
function
|
|
251
|
+
function existentialsWithInput(existentialConditions: ExistentialConditionDescription[], input: InputDescription, path: number[]): ExistentialConditionDescription[] {
|
|
224
252
|
if (path.length === 1) {
|
|
225
|
-
return
|
|
253
|
+
return existentialConditions.map((c, i) => i === path[0] ?
|
|
226
254
|
{
|
|
227
|
-
|
|
228
|
-
|
|
255
|
+
...c,
|
|
256
|
+
inputs: [...c.inputs, input]
|
|
229
257
|
} :
|
|
230
258
|
c
|
|
231
259
|
);
|
|
232
260
|
}
|
|
233
261
|
else {
|
|
234
|
-
return
|
|
262
|
+
return existentialConditions.map((c, i) => i === path[0] ?
|
|
235
263
|
{
|
|
236
|
-
|
|
237
|
-
|
|
264
|
+
...c,
|
|
265
|
+
existentialConditions: existentialsWithInput(c.existentialConditions, input, path.slice(1))
|
|
238
266
|
} :
|
|
239
267
|
c
|
|
240
268
|
);
|
|
241
269
|
}
|
|
242
270
|
}
|
|
243
271
|
|
|
244
|
-
function
|
|
272
|
+
function existentialsWithEdge(existentialConditions: ExistentialConditionDescription[], edge: EdgeDescription, path: number[]): ExistentialConditionDescription[] {
|
|
273
|
+
if (path.length === 1) {
|
|
274
|
+
return existentialConditions.map((c, i) => i === path[0] ?
|
|
275
|
+
{
|
|
276
|
+
...c,
|
|
277
|
+
edges: [...c.edges, edge]
|
|
278
|
+
} :
|
|
279
|
+
c
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
else {
|
|
283
|
+
return existentialConditions.map((c, i) => i === path[0] ?
|
|
284
|
+
{
|
|
285
|
+
...c,
|
|
286
|
+
existentialConditions: existentialsWithEdge(c.existentialConditions, edge, path.slice(1))
|
|
287
|
+
} :
|
|
288
|
+
c
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function existentialsWithNewCondition(existentialConditions: ExistentialConditionDescription[], exists: boolean, path: number[]): { existentialConditions: ExistentialConditionDescription[]; path: number[]; } {
|
|
245
294
|
if (path.length === 0) {
|
|
246
|
-
path = [
|
|
247
|
-
|
|
248
|
-
...
|
|
295
|
+
path = [existentialConditions.length];
|
|
296
|
+
existentialConditions = [
|
|
297
|
+
...existentialConditions,
|
|
249
298
|
{
|
|
299
|
+
exists: exists,
|
|
300
|
+
inputs: [],
|
|
250
301
|
edges: [],
|
|
251
|
-
|
|
302
|
+
existentialConditions: []
|
|
252
303
|
}
|
|
253
304
|
];
|
|
254
|
-
return {
|
|
305
|
+
return { existentialConditions: existentialConditions, path };
|
|
255
306
|
}
|
|
256
307
|
else {
|
|
257
|
-
const {
|
|
258
|
-
|
|
308
|
+
const { existentialConditions: newExistentialConditions, path: newPath } = existentialsWithNewCondition(existentialConditions[path[0]].existentialConditions, exists, path.slice(1));
|
|
309
|
+
existentialConditions = existentialConditions.map((c, i) => i === path[0] ?
|
|
259
310
|
{
|
|
311
|
+
exists: c.exists,
|
|
312
|
+
inputs: c.inputs,
|
|
260
313
|
edges: c.edges,
|
|
261
|
-
|
|
314
|
+
existentialConditions: newExistentialConditions
|
|
262
315
|
} :
|
|
263
316
|
c
|
|
264
317
|
);
|
|
265
318
|
path = [path[0], ...newPath];
|
|
266
|
-
return {
|
|
319
|
+
return { existentialConditions: existentialConditions, path };
|
|
267
320
|
}
|
|
268
321
|
}
|
|
269
322
|
|
|
@@ -311,8 +364,8 @@ function generateJoins(edges: EdgeDescription[], writtenFactIndexes: Set<number>
|
|
|
311
364
|
return joins;
|
|
312
365
|
}
|
|
313
366
|
|
|
314
|
-
function
|
|
315
|
-
const firstEdge =
|
|
367
|
+
function generateExistentialWhereClause(existentialCondition: ExistentialConditionDescription, outerFactIndexes: Set<number>): string {
|
|
368
|
+
const firstEdge = existentialCondition.edges[0];
|
|
316
369
|
const writtenFactIndexes = new Set<number>(outerFactIndexes);
|
|
317
370
|
const firstJoin: string[] = [];
|
|
318
371
|
const whereClause: string[] = [];
|
|
@@ -346,9 +399,15 @@ function generateNotExistsWhereClause(notExistsWhereClause: NotExistsConditionDe
|
|
|
346
399
|
else {
|
|
347
400
|
throw new Error("Neither predecessor nor successor fact has been written");
|
|
348
401
|
}
|
|
349
|
-
const tailJoins: string[] = generateJoins(
|
|
402
|
+
const tailJoins: string[] = generateJoins(existentialCondition.edges.slice(1), writtenFactIndexes);
|
|
350
403
|
const joins = firstJoin.concat(tailJoins);
|
|
351
|
-
|
|
404
|
+
const inputWhereClauses = existentialCondition.inputs
|
|
405
|
+
.map(input => ` AND f${input.factIndex}.fact_type_id = $${input.factTypeParameter} AND f${input.factIndex}.hash = $${input.factHashParameter}`)
|
|
406
|
+
.join("");
|
|
407
|
+
const existentialWhereClauses = existentialCondition.existentialConditions
|
|
408
|
+
.map(e => ` AND ${e.exists ? "EXISTS" : "NOT EXISTS"} (${generateExistentialWhereClause(e, writtenFactIndexes)})`)
|
|
409
|
+
.join("");
|
|
410
|
+
return `SELECT 1 FROM public.edge e${firstEdge.edgeIndex}${joins.join("")} WHERE ${whereClause.join(" AND ")}${inputWhereClauses}${existentialWhereClauses}`;
|
|
352
411
|
}
|
|
353
412
|
|
|
354
413
|
type FactByIdentifier = {
|
|
@@ -562,37 +621,16 @@ class ResultDescriptionBuilder {
|
|
|
562
621
|
}
|
|
563
622
|
}
|
|
564
623
|
|
|
565
|
-
// Allocate a fact table for each given.
|
|
566
|
-
// While the fact type and hash parameters are zero, the join will not be written.
|
|
567
|
-
const inputs: InputDescription[] = specification.given
|
|
568
|
-
.map((label, i) => ({
|
|
569
|
-
label: label.name,
|
|
570
|
-
factIndex: i+1,
|
|
571
|
-
factTypeId: getFactTypeId(this.factTypes, label.type),
|
|
572
|
-
factHash: start[i].hash,
|
|
573
|
-
factTypeParameter: 0,
|
|
574
|
-
factHashParameter: 0
|
|
575
|
-
}));
|
|
576
|
-
const facts: FactDescription[] = specification.given
|
|
577
|
-
.map((label, i) => ({
|
|
578
|
-
factIndex: i+1,
|
|
579
|
-
type: label.type
|
|
580
|
-
}));
|
|
581
|
-
const givenFacts = specification.given.reduce((knownFacts, label, i) => ({
|
|
582
|
-
...knownFacts,
|
|
583
|
-
[label.name]: facts[i]
|
|
584
|
-
}), {} as FactByIdentifier);
|
|
585
|
-
|
|
586
624
|
// The QueryDescription is an immutable data type.
|
|
587
625
|
// Initialize it with the inputs and facts.
|
|
588
626
|
// The DescriptionBuilder will branch at various points, and
|
|
589
627
|
// build on the current query description along each branch.
|
|
590
|
-
const initialQueryDescription = new QueryDescription(
|
|
591
|
-
return this.createResultDescription(initialQueryDescription, specification.matches, specification.childProjections,
|
|
628
|
+
const initialQueryDescription = new QueryDescription([], [], [], [], [], []);
|
|
629
|
+
return this.createResultDescription(initialQueryDescription, specification.given, start, specification.matches, specification.childProjections, {}, []);
|
|
592
630
|
}
|
|
593
631
|
|
|
594
|
-
private createResultDescription(queryDescription: QueryDescription, matches: Match[], childProjections: ChildProjections, knownFacts: FactByIdentifier, path: number[]): ResultDescription {
|
|
595
|
-
({ queryDescription, knownFacts } = this.addEdges(queryDescription, knownFacts, path, matches));
|
|
632
|
+
private createResultDescription(queryDescription: QueryDescription, given: Label[], start: FactReference[], matches: Match[], childProjections: ChildProjections, knownFacts: FactByIdentifier, path: number[]): ResultDescription {
|
|
633
|
+
({ queryDescription, knownFacts } = this.addEdges(queryDescription, given, start, knownFacts, path, matches));
|
|
596
634
|
if (!queryDescription.isSatisfiable()) {
|
|
597
635
|
// Abort the branch if the query is not satisfiable
|
|
598
636
|
return {
|
|
@@ -608,7 +646,7 @@ class ResultDescriptionBuilder {
|
|
|
608
646
|
const elementProjections = childProjections
|
|
609
647
|
.filter(projection => projection.type === "field" || projection.type === "hash") as ElementProjection[];
|
|
610
648
|
for (const child of specificationProjections) {
|
|
611
|
-
const childResultDescription = this.createResultDescription(queryDescription, child.matches, child.childProjections, knownFacts, []);
|
|
649
|
+
const childResultDescription = this.createResultDescription(queryDescription, given, start, child.matches, child.childProjections, knownFacts, []);
|
|
612
650
|
childResultDescriptions.push({
|
|
613
651
|
name: child.name,
|
|
614
652
|
...childResultDescription
|
|
@@ -629,31 +667,24 @@ class ResultDescriptionBuilder {
|
|
|
629
667
|
}
|
|
630
668
|
}
|
|
631
669
|
|
|
632
|
-
private addEdges(queryDescription: QueryDescription, knownFacts: FactByIdentifier, path: number[], matches: Match[]): { queryDescription: QueryDescription, knownFacts: FactByIdentifier } {
|
|
670
|
+
private addEdges(queryDescription: QueryDescription, given: Label[], start: FactReference[], knownFacts: FactByIdentifier, path: number[], matches: Match[]): { queryDescription: QueryDescription, knownFacts: FactByIdentifier } {
|
|
633
671
|
for (const match of matches) {
|
|
634
672
|
for (const condition of match.conditions) {
|
|
635
673
|
if (condition.type === "path") {
|
|
636
|
-
({queryDescription, knownFacts} = this.addPathCondition(queryDescription, knownFacts, path, match.unknown, "", condition));
|
|
674
|
+
({queryDescription, knownFacts} = this.addPathCondition(queryDescription, given, start, knownFacts, path, match.unknown, "", condition));
|
|
637
675
|
}
|
|
638
676
|
else if (condition.type === "existential") {
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
}
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
// If the negative existential condition is not satisfiable, then
|
|
652
|
-
// that means that the condition will always be true.
|
|
653
|
-
// We can therefore skip the branch for the negative existential condition.
|
|
654
|
-
if (queryDescriptionConditional.isSatisfiable()) {
|
|
655
|
-
queryDescription = queryDescriptionConditional;
|
|
656
|
-
}
|
|
677
|
+
// Apply the where clause and continue with the tuple where it is true.
|
|
678
|
+
// The path describes which not-exists condition we are currently building on.
|
|
679
|
+
// Because the path is not empty, labeled facts will be included in the output.
|
|
680
|
+
const { query: queryDescriptionWithExistential, path: conditionalPath } = queryDescription.withExistentialCondition(condition.exists, path);
|
|
681
|
+
const { queryDescription: queryDescriptionConditional } = this.addEdges(queryDescriptionWithExistential, given, start, knownFacts, conditionalPath, condition.matches);
|
|
682
|
+
|
|
683
|
+
// If the negative existential condition is not satisfiable, then
|
|
684
|
+
// that means that the condition will always be true.
|
|
685
|
+
// We can therefore skip the branch for the negative existential condition.
|
|
686
|
+
if (queryDescriptionConditional.isSatisfiable()) {
|
|
687
|
+
queryDescription = queryDescriptionConditional;
|
|
657
688
|
}
|
|
658
689
|
}
|
|
659
690
|
if (!queryDescription.isSatisfiable()) {
|
|
@@ -670,11 +701,24 @@ class ResultDescriptionBuilder {
|
|
|
670
701
|
};
|
|
671
702
|
}
|
|
672
703
|
|
|
673
|
-
private addPathCondition(queryDescription: QueryDescription, knownFacts: FactByIdentifier, path: number[], unknown: Label, prefix: string, condition: PathCondition): { queryDescription: QueryDescription, knownFacts: FactByIdentifier } {
|
|
704
|
+
private addPathCondition(queryDescription: QueryDescription, given: Label[], start: FactReference[], knownFacts: FactByIdentifier, path: number[], unknown: Label, prefix: string, condition: PathCondition): { queryDescription: QueryDescription, knownFacts: FactByIdentifier } {
|
|
674
705
|
// If no input parameter has been allocated, allocate one now.
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
706
|
+
if (!knownFacts.hasOwnProperty(condition.labelRight)) {
|
|
707
|
+
const givenIndex = given.findIndex(given => given.name === condition.labelRight);
|
|
708
|
+
if (givenIndex < 0) {
|
|
709
|
+
throw new Error(`No input parameter found for label ${condition.labelRight}`);
|
|
710
|
+
}
|
|
711
|
+
const { queryDescription: newQueryDescription, factDescription } = queryDescription.withInputParameter(
|
|
712
|
+
given[givenIndex],
|
|
713
|
+
getFactTypeId(this.factTypes, start[givenIndex].type),
|
|
714
|
+
start[givenIndex].hash,
|
|
715
|
+
path
|
|
716
|
+
);
|
|
717
|
+
queryDescription = newQueryDescription;
|
|
718
|
+
knownFacts = {
|
|
719
|
+
...knownFacts,
|
|
720
|
+
[condition.labelRight]: factDescription
|
|
721
|
+
};
|
|
678
722
|
}
|
|
679
723
|
|
|
680
724
|
// Determine whether we have already written the output.
|