jinaga-server 2.3.1 → 2.3.3
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/authentication/authentication-device.d.ts +2 -1
- package/dist/authentication/authentication-device.js +12 -3
- package/dist/authentication/authentication-device.js.map +1 -1
- package/dist/http/router.js +13 -1
- package/dist/http/router.js.map +1 -1
- package/dist/postgres/postgres-store.d.ts +2 -2
- package/dist/postgres/postgres-store.js +19 -4
- package/dist/postgres/postgres-store.js.map +1 -1
- package/dist/postgres/specification-result-sql.d.ts +23 -4
- package/dist/postgres/specification-result-sql.js +103 -32
- package/dist/postgres/specification-result-sql.js.map +1 -1
- package/dist/postgres/specification-sql.js +1 -5
- package/dist/postgres/specification-sql.js.map +1 -1
- package/docs/sql/SQL Generation.ipynb +2427 -0
- package/package.json +2 -2
- package/src/authentication/authentication-device.ts +11 -3
- package/src/http/router.ts +18 -1
- package/src/postgres/postgres-store.ts +28 -6
- package/src/postgres/specification-result-sql.ts +149 -37
- package/src/postgres/specification-sql.ts +1 -10
- package/test/postgres/postgresReadSpec.ts +63 -3
- package/docs/registrations.sql +0 -22
- package/docs/sql/example.sql +0 -49
- package/docs/sql-generator.gv +0 -25
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "jinaga-server",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.3",
|
|
4
4
|
"description": "Application-agnostic back end for web applications",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
},
|
|
44
44
|
"dependencies": {
|
|
45
45
|
"express": "^4.18.1",
|
|
46
|
-
"jinaga": "^5.3.
|
|
46
|
+
"jinaga": "^5.3.1",
|
|
47
47
|
"node-forge": "^1.3.1",
|
|
48
48
|
"passport": "^0.6.0",
|
|
49
49
|
"pg": "^8.7.1"
|
|
@@ -6,13 +6,13 @@ export class AuthenticationDevice implements Authentication {
|
|
|
6
6
|
private authorizationEngine: AuthorizationEngine | null;
|
|
7
7
|
|
|
8
8
|
constructor(
|
|
9
|
-
|
|
9
|
+
private store: Storage,
|
|
10
10
|
private keystore: Keystore,
|
|
11
11
|
authorizationRules: AuthorizationRules | null,
|
|
12
12
|
private localDeviceIdentity: UserIdentity
|
|
13
13
|
) {
|
|
14
14
|
this.authorizationEngine = authorizationRules &&
|
|
15
|
-
new AuthorizationEngine(authorizationRules,
|
|
15
|
+
new AuthorizationEngine(authorizationRules, store);
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async login(): Promise<LoginResponse> {
|
|
@@ -20,7 +20,15 @@ export class AuthenticationDevice implements Authentication {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
async local(): Promise<FactRecord> {
|
|
23
|
-
|
|
23
|
+
const deviceFact = await this.keystore.getOrCreateDeviceFact(this.localDeviceIdentity);
|
|
24
|
+
const envelopes = [
|
|
25
|
+
<FactEnvelope>{
|
|
26
|
+
fact: deviceFact,
|
|
27
|
+
signatures: []
|
|
28
|
+
}
|
|
29
|
+
];
|
|
30
|
+
await this.store.save(envelopes);
|
|
31
|
+
return deviceFact;
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
async authorize(envelopes: FactEnvelope[]): Promise<FactEnvelope[]> {
|
package/src/http/router.ts
CHANGED
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
LoadMessage,
|
|
13
13
|
LoadResponse,
|
|
14
14
|
ProfileMessage,
|
|
15
|
+
ProjectedResult,
|
|
15
16
|
QueryMessage,
|
|
16
17
|
QueryResponse,
|
|
17
18
|
SaveMessage,
|
|
@@ -274,7 +275,7 @@ export class HttpRouter {
|
|
|
274
275
|
|
|
275
276
|
const userIdentity = serializeUserIdentity(user);
|
|
276
277
|
const results = await this.authorization.read(userIdentity, start, specification);
|
|
277
|
-
return results;
|
|
278
|
+
return extractResults(results);
|
|
278
279
|
}
|
|
279
280
|
|
|
280
281
|
private async write(user: RequestUser, input: string): Promise<void> {
|
|
@@ -383,3 +384,19 @@ function urlSafeHash(feed: Feed): string {
|
|
|
383
384
|
const base64 = computeObjectHash(feed);
|
|
384
385
|
return base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
|
|
385
386
|
}
|
|
387
|
+
|
|
388
|
+
function extractResults(obj: any): any {
|
|
389
|
+
if (Array.isArray(obj)) {
|
|
390
|
+
const projectedResults: ProjectedResult[] = obj;
|
|
391
|
+
return projectedResults.map(r => extractResults(r.result));
|
|
392
|
+
}
|
|
393
|
+
else if (typeof obj === "object") {
|
|
394
|
+
return Object.keys(obj).reduce((acc, key) => ({
|
|
395
|
+
...acc,
|
|
396
|
+
[key]: extractResults(obj[key])
|
|
397
|
+
}), {});
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
return obj;
|
|
401
|
+
}
|
|
402
|
+
}
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
getAllRolesFromFeed,
|
|
17
17
|
Join,
|
|
18
18
|
PredecessorCollection,
|
|
19
|
+
ProjectedResult,
|
|
19
20
|
PropertyCondition,
|
|
20
21
|
Query,
|
|
21
22
|
Specification,
|
|
@@ -51,7 +52,7 @@ import {
|
|
|
51
52
|
PublicKeyMap,
|
|
52
53
|
RoleMap
|
|
53
54
|
} from "./maps";
|
|
54
|
-
import { ResultSetTree, resultSqlFromSpecification, SqlQueryTree } from "./specification-result-sql";
|
|
55
|
+
import { ResultSetFact, ResultSetRow, ResultSetTree, resultSqlFromSpecification, SqlQueryTree } from "./specification-result-sql";
|
|
55
56
|
import { sqlFromFeed } from "./specification-sql";
|
|
56
57
|
import { sqlFromSteps } from "./sql";
|
|
57
58
|
|
|
@@ -232,7 +233,7 @@ export class PostgresStore implements Storage {
|
|
|
232
233
|
}
|
|
233
234
|
}
|
|
234
235
|
|
|
235
|
-
async read(start: FactReference[], specification: Specification): Promise<
|
|
236
|
+
async read(start: FactReference[], specification: Specification): Promise<ProjectedResult[]> {
|
|
236
237
|
const factTypes = await this.loadFactTypesFromSpecification(specification);
|
|
237
238
|
const roleMap = await this.loadRolesFromSpecification(specification, factTypes);
|
|
238
239
|
|
|
@@ -250,7 +251,14 @@ export class PostgresStore implements Storage {
|
|
|
250
251
|
const resultSets = await this.connectionFactory.with(async (connection) => {
|
|
251
252
|
return await executeQueryTree(sqlQueryTree, connection);
|
|
252
253
|
});
|
|
253
|
-
|
|
254
|
+
|
|
255
|
+
// Find the references for fact projections
|
|
256
|
+
const factReferences: FactReference[] = composer.findFactReferences(resultSets);
|
|
257
|
+
|
|
258
|
+
// Load the references into a fact tree
|
|
259
|
+
const factRecords = await this.load(factReferences);
|
|
260
|
+
|
|
261
|
+
return composer.compose(resultSets, factRecords);
|
|
254
262
|
}
|
|
255
263
|
|
|
256
264
|
async feed(feed: Feed, bookmark: string): Promise<FactFeed> {
|
|
@@ -478,17 +486,31 @@ export class PostgresStore implements Storage {
|
|
|
478
486
|
}
|
|
479
487
|
|
|
480
488
|
getMruDate(specificationHash: string): Promise<Date | null> {
|
|
481
|
-
|
|
489
|
+
return Promise.resolve(null);
|
|
482
490
|
}
|
|
483
491
|
|
|
484
492
|
setMruDate(specificationHash: string, mruDate: Date): Promise<void> {
|
|
485
|
-
|
|
493
|
+
return Promise.resolve();
|
|
486
494
|
}
|
|
487
495
|
}
|
|
488
496
|
|
|
489
497
|
async function executeQueryTree(sqlQueryTree: SqlQueryTree, connection: PoolClient): Promise<ResultSetTree> {
|
|
490
498
|
const sqlQuery = sqlQueryTree.sqlQuery;
|
|
491
|
-
const { rows } = await connection.query(sqlQuery.sql, sqlQuery.parameters);
|
|
499
|
+
const { rows: dataRows } = await connection.query(sqlQuery.sql, sqlQuery.parameters);
|
|
500
|
+
const rows: ResultSetRow[] = dataRows.map(dataRow => {
|
|
501
|
+
const row = sqlQuery.labels.reduce((acc, label) => {
|
|
502
|
+
const fact: ResultSetFact = {
|
|
503
|
+
hash: dataRow[`hash${label.index}`],
|
|
504
|
+
factId: dataRow[`id${label.index}`],
|
|
505
|
+
data: dataRow[`data${label.index}`]
|
|
506
|
+
};
|
|
507
|
+
return {
|
|
508
|
+
...acc,
|
|
509
|
+
[label.index]: fact
|
|
510
|
+
};
|
|
511
|
+
}, {} as ResultSetRow);
|
|
512
|
+
return row;
|
|
513
|
+
});
|
|
492
514
|
const resultSets: ResultSetTree = {
|
|
493
515
|
resultSet: rows,
|
|
494
516
|
childResultSets: []
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import {
|
|
2
2
|
ComponentProjection,
|
|
3
3
|
FactProjection,
|
|
4
|
+
FactRecord,
|
|
4
5
|
FactReference,
|
|
5
6
|
FieldProjection,
|
|
6
7
|
HashProjection,
|
|
8
|
+
hydrateFromTree,
|
|
7
9
|
Label,
|
|
8
10
|
Match,
|
|
9
11
|
PathCondition,
|
|
12
|
+
PredecessorCollection,
|
|
13
|
+
ProjectedResult,
|
|
10
14
|
Projection,
|
|
15
|
+
ReferencesByName,
|
|
11
16
|
SingularProjection,
|
|
12
17
|
Specification,
|
|
13
18
|
SpecificationProjection
|
|
@@ -18,6 +23,7 @@ import { ensureGetFactTypeId, FactTypeMap, getFactTypeId, getRoleId, RoleMap } f
|
|
|
18
23
|
interface SpecificationLabel {
|
|
19
24
|
name: string;
|
|
20
25
|
index: number;
|
|
26
|
+
type: string;
|
|
21
27
|
}
|
|
22
28
|
interface FactDescription {
|
|
23
29
|
type: string;
|
|
@@ -43,6 +49,7 @@ interface SpecificationSqlQuery {
|
|
|
43
49
|
};
|
|
44
50
|
interface InputDescription {
|
|
45
51
|
label: string;
|
|
52
|
+
type: string;
|
|
46
53
|
factIndex: number;
|
|
47
54
|
factTypeParameter: number;
|
|
48
55
|
factHashParameter: number;
|
|
@@ -100,6 +107,7 @@ class QueryDescription {
|
|
|
100
107
|
]
|
|
101
108
|
const input: InputDescription = {
|
|
102
109
|
label: label.name,
|
|
110
|
+
type: label.type,
|
|
103
111
|
factIndex,
|
|
104
112
|
factTypeParameter,
|
|
105
113
|
factHashParameter
|
|
@@ -218,8 +226,9 @@ class QueryDescription {
|
|
|
218
226
|
}
|
|
219
227
|
|
|
220
228
|
generateResultSqlQuery(schema: string): SpecificationSqlQuery {
|
|
221
|
-
const
|
|
222
|
-
|
|
229
|
+
const allLabels = [ ...this.inputs, ...this.outputs ];
|
|
230
|
+
const columns = allLabels
|
|
231
|
+
.map(label => `f${label.factIndex}.hash as hash${label.factIndex}, f${label.factIndex}.fact_id as id${label.factIndex}, f${label.factIndex}.data as data${label.factIndex}`)
|
|
223
232
|
.join(", ");
|
|
224
233
|
const firstEdge = this.edges[0];
|
|
225
234
|
const predecessorFact = this.inputs.find(i => i.factIndex === firstEdge.predecessorFactIndex);
|
|
@@ -240,10 +249,10 @@ class QueryDescription {
|
|
|
240
249
|
return {
|
|
241
250
|
sql,
|
|
242
251
|
parameters: this.parameters,
|
|
243
|
-
labels:
|
|
244
|
-
name:
|
|
245
|
-
type:
|
|
246
|
-
index:
|
|
252
|
+
labels: allLabels.map(label => ({
|
|
253
|
+
name: label.label,
|
|
254
|
+
type: label.type,
|
|
255
|
+
index: label.factIndex
|
|
247
256
|
})),
|
|
248
257
|
bookmark: "[]"
|
|
249
258
|
};
|
|
@@ -420,6 +429,7 @@ interface ResultDescription {
|
|
|
420
429
|
queryDescription: QueryDescription;
|
|
421
430
|
resultProjection: Projection;
|
|
422
431
|
childResultDescriptions: NamedResultDescription[];
|
|
432
|
+
givenTuple: ReferencesByName;
|
|
423
433
|
}
|
|
424
434
|
|
|
425
435
|
interface NamedResultDescription extends ResultDescription {
|
|
@@ -428,12 +438,13 @@ interface NamedResultDescription extends ResultDescription {
|
|
|
428
438
|
|
|
429
439
|
interface IdentifiedResults {
|
|
430
440
|
factIds: number[];
|
|
441
|
+
tuple: ReferencesByName;
|
|
431
442
|
result: any;
|
|
432
443
|
}
|
|
433
444
|
|
|
434
445
|
interface ChildResults {
|
|
435
446
|
parentFactIds: number[];
|
|
436
|
-
results:
|
|
447
|
+
results: ProjectedResult[];
|
|
437
448
|
}
|
|
438
449
|
|
|
439
450
|
export interface SqlQueryTree {
|
|
@@ -445,8 +456,23 @@ interface NamedSqlQueryTree extends SqlQueryTree {
|
|
|
445
456
|
name: string;
|
|
446
457
|
}
|
|
447
458
|
|
|
459
|
+
export interface ResultSetData {
|
|
460
|
+
fields: { [field: string]: any };
|
|
461
|
+
predecessors: PredecessorCollection;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
export interface ResultSetFact {
|
|
465
|
+
hash: string;
|
|
466
|
+
factId: number;
|
|
467
|
+
data: ResultSetData;
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
export interface ResultSetRow {
|
|
471
|
+
[factIndex: number]: ResultSetFact;
|
|
472
|
+
}
|
|
473
|
+
|
|
448
474
|
export interface ResultSetTree {
|
|
449
|
-
resultSet:
|
|
475
|
+
resultSet: ResultSetRow[];
|
|
450
476
|
childResultSets: NamedResultSetTree[];
|
|
451
477
|
}
|
|
452
478
|
|
|
@@ -459,6 +485,7 @@ export class ResultComposer {
|
|
|
459
485
|
private readonly sqlQuery: SpecificationSqlQuery,
|
|
460
486
|
private readonly resultProjection: Projection,
|
|
461
487
|
private readonly parentFactIdLength: number,
|
|
488
|
+
private readonly givenTuple: ReferencesByName,
|
|
462
489
|
private readonly childResultComposers: NamedResultComposer[]
|
|
463
490
|
) { }
|
|
464
491
|
|
|
@@ -476,10 +503,56 @@ export class ResultComposer {
|
|
|
476
503
|
};
|
|
477
504
|
}
|
|
478
505
|
|
|
479
|
-
public
|
|
506
|
+
public findFactReferences(
|
|
480
507
|
resultSets: ResultSetTree
|
|
481
|
-
):
|
|
482
|
-
|
|
508
|
+
): FactReference[] {
|
|
509
|
+
let factReferences: FactReference[] = [];
|
|
510
|
+
|
|
511
|
+
// Add the fact references for selected facts.
|
|
512
|
+
const projection = this.resultProjection;
|
|
513
|
+
if (projection.type === 'fact') {
|
|
514
|
+
const projectionFactReferences = this.factReferencesForProjection(projection, resultSets);
|
|
515
|
+
factReferences = factReferences.concat(projectionFactReferences);
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
// Add the fact references for composites.
|
|
519
|
+
if (projection.type === 'composite') {
|
|
520
|
+
for (const component of projection.components) {
|
|
521
|
+
if (component.type === 'fact') {
|
|
522
|
+
const projectionFactReferences = this.factReferencesForProjection(component, resultSets);
|
|
523
|
+
factReferences = factReferences.concat(projectionFactReferences);
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Recursively add the fact references for child result sets.
|
|
529
|
+
for (const childResultSet of resultSets.childResultSets) {
|
|
530
|
+
const childResultComposer = this.childResultComposers.find(c =>
|
|
531
|
+
c.name === childResultSet.name);
|
|
532
|
+
if (childResultComposer) {
|
|
533
|
+
const childFactReferences = childResultComposer.resultComposer
|
|
534
|
+
.findFactReferences(childResultSet);
|
|
535
|
+
factReferences = factReferences.concat(childFactReferences);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
return factReferences;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
private factReferencesForProjection(projection: FactProjection, resultSets: ResultSetTree) {
|
|
543
|
+
const label = this.getLabel(projection.label);
|
|
544
|
+
const factReferences = resultSets.resultSet.map(row => ({
|
|
545
|
+
type: label.type,
|
|
546
|
+
hash: row[label.index].hash
|
|
547
|
+
}));
|
|
548
|
+
return factReferences;
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
public compose(
|
|
552
|
+
resultSets: ResultSetTree,
|
|
553
|
+
factRecords: FactRecord[]
|
|
554
|
+
): ProjectedResult[] {
|
|
555
|
+
const childResults = this.composeInternal(resultSets, factRecords);
|
|
483
556
|
if (childResults.length === 0) {
|
|
484
557
|
return [];
|
|
485
558
|
}
|
|
@@ -489,7 +562,8 @@ export class ResultComposer {
|
|
|
489
562
|
}
|
|
490
563
|
|
|
491
564
|
private composeInternal(
|
|
492
|
-
resultSets: ResultSetTree
|
|
565
|
+
resultSets: ResultSetTree,
|
|
566
|
+
factRecords: FactRecord[]
|
|
493
567
|
): ChildResults[] {
|
|
494
568
|
const rows = resultSets.resultSet;
|
|
495
569
|
if (rows.length === 0) {
|
|
@@ -499,7 +573,8 @@ export class ResultComposer {
|
|
|
499
573
|
// Project all rows and their identifiers
|
|
500
574
|
const identifiedResults: IdentifiedResults[] = rows.map(row => ({
|
|
501
575
|
factIds: this.identifierOf(row),
|
|
502
|
-
|
|
576
|
+
tuple: this.tupleOf(row),
|
|
577
|
+
result: this.projectionOf(row, factRecords)
|
|
503
578
|
}));
|
|
504
579
|
|
|
505
580
|
// Compose child results
|
|
@@ -510,7 +585,7 @@ export class ResultComposer {
|
|
|
510
585
|
const availableNames = resultSets.childResultSets.map(childResultSet => childResultSet.name);
|
|
511
586
|
throw new Error(`Child result set ${childResultComposer.name} not found in (${availableNames.join(", ")})`);
|
|
512
587
|
}
|
|
513
|
-
const composedResults = childResultComposer.resultComposer.composeInternal(childResultSet);
|
|
588
|
+
const composedResults = childResultComposer.resultComposer.composeInternal(childResultSet, factRecords);
|
|
514
589
|
|
|
515
590
|
// Add the child results
|
|
516
591
|
let index = 0;
|
|
@@ -530,11 +605,17 @@ export class ResultComposer {
|
|
|
530
605
|
// Group the results by their parent identifiers
|
|
531
606
|
const childResults: ChildResults[] = [];
|
|
532
607
|
let parentFactIds: number[] = identifiedResults[0].factIds.slice(0, this.parentFactIdLength);
|
|
533
|
-
let results:
|
|
608
|
+
let results: ProjectedResult[] = [{
|
|
609
|
+
tuple: identifiedResults[0].tuple,
|
|
610
|
+
result: identifiedResults[0].result
|
|
611
|
+
}];
|
|
534
612
|
for (const identifiedResult of identifiedResults.slice(1)) {
|
|
535
613
|
const nextParentFactIds = identifiedResult.factIds.slice(0, this.parentFactIdLength);
|
|
536
614
|
if (idsEqual(nextParentFactIds, parentFactIds)) {
|
|
537
|
-
results.push(
|
|
615
|
+
results.push({
|
|
616
|
+
tuple: identifiedResult.tuple,
|
|
617
|
+
result: identifiedResult.result
|
|
618
|
+
});
|
|
538
619
|
}
|
|
539
620
|
else {
|
|
540
621
|
childResults.push({
|
|
@@ -542,7 +623,10 @@ export class ResultComposer {
|
|
|
542
623
|
results
|
|
543
624
|
});
|
|
544
625
|
parentFactIds = nextParentFactIds;
|
|
545
|
-
results = [
|
|
626
|
+
results = [{
|
|
627
|
+
tuple: identifiedResult.tuple,
|
|
628
|
+
result: identifiedResult.result
|
|
629
|
+
}];
|
|
546
630
|
}
|
|
547
631
|
}
|
|
548
632
|
childResults.push({
|
|
@@ -552,11 +636,22 @@ export class ResultComposer {
|
|
|
552
636
|
return childResults;
|
|
553
637
|
}
|
|
554
638
|
|
|
555
|
-
private identifierOf(row:
|
|
556
|
-
return this.sqlQuery.labels.map(label => row[
|
|
639
|
+
private identifierOf(row: ResultSetRow): number[] {
|
|
640
|
+
return this.sqlQuery.labels.map(label => row[label.index].factId);
|
|
557
641
|
}
|
|
558
642
|
|
|
559
|
-
private
|
|
643
|
+
private tupleOf(row: ResultSetRow): ReferencesByName {
|
|
644
|
+
const tuple = this.sqlQuery.labels.reduce((acc, label) => ({
|
|
645
|
+
...acc,
|
|
646
|
+
[label.name]: {
|
|
647
|
+
type: label.type,
|
|
648
|
+
hash: row[label.index].hash,
|
|
649
|
+
}
|
|
650
|
+
}), this.givenTuple);
|
|
651
|
+
return tuple;
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
private projectionOf(row: ResultSetRow, factRecords: FactRecord[]): any {
|
|
560
655
|
if (this.resultProjection.type === "field") {
|
|
561
656
|
return this.fieldValue(this.resultProjection, row);
|
|
562
657
|
}
|
|
@@ -568,13 +663,13 @@ export class ResultComposer {
|
|
|
568
663
|
.slice(this.parentFactIdLength)
|
|
569
664
|
.reduce((acc, label) => ({
|
|
570
665
|
...acc,
|
|
571
|
-
[label.name]: row[
|
|
666
|
+
[label.name]: row[label.index].data.fields
|
|
572
667
|
}), {})
|
|
573
668
|
}
|
|
574
669
|
else {
|
|
575
670
|
return this.resultProjection.components.reduce((acc, component) => ({
|
|
576
671
|
...acc,
|
|
577
|
-
[component.name]: this.elementValue(component, row)
|
|
672
|
+
[component.name]: this.elementValue(component, row, factRecords)
|
|
578
673
|
}), {});
|
|
579
674
|
}
|
|
580
675
|
}
|
|
@@ -582,7 +677,7 @@ export class ResultComposer {
|
|
|
582
677
|
return this.hashValue(this.resultProjection, row);
|
|
583
678
|
}
|
|
584
679
|
else if (this.resultProjection.type === "fact") {
|
|
585
|
-
return this.factValue(this.resultProjection, row);
|
|
680
|
+
return this.factValue(this.resultProjection, row, factRecords);
|
|
586
681
|
}
|
|
587
682
|
else {
|
|
588
683
|
const _exhaustiveCheck: never = this.resultProjection;
|
|
@@ -590,18 +685,23 @@ export class ResultComposer {
|
|
|
590
685
|
}
|
|
591
686
|
}
|
|
592
687
|
|
|
593
|
-
private elementValue(projection: ComponentProjection, row:
|
|
688
|
+
private elementValue(projection: ComponentProjection, row: ResultSetRow, factRecords: FactRecord[]): any {
|
|
594
689
|
if (projection.type === "field") {
|
|
595
690
|
const label = this.getLabel(projection.label);
|
|
596
|
-
return row[
|
|
691
|
+
return row[label.index].data.fields[projection.field];
|
|
597
692
|
}
|
|
598
693
|
else if (projection.type === "hash") {
|
|
599
694
|
const label = this.getLabel(projection.label);
|
|
600
|
-
return row[
|
|
695
|
+
return row[label.index].hash;
|
|
601
696
|
}
|
|
602
697
|
else if (projection.type === "fact") {
|
|
603
698
|
const label = this.getLabel(projection.label);
|
|
604
|
-
|
|
699
|
+
const factReference = {
|
|
700
|
+
type: label.type,
|
|
701
|
+
hash: row[label.index].hash,
|
|
702
|
+
};
|
|
703
|
+
const [fact] = hydrateFromTree([factReference], factRecords);
|
|
704
|
+
return fact;
|
|
605
705
|
}
|
|
606
706
|
else if (projection.type === "specification") {
|
|
607
707
|
// This should have already been taken care of
|
|
@@ -613,19 +713,24 @@ export class ResultComposer {
|
|
|
613
713
|
}
|
|
614
714
|
}
|
|
615
715
|
|
|
616
|
-
private fieldValue(projection: FieldProjection, row:
|
|
716
|
+
private fieldValue(projection: FieldProjection, row: ResultSetRow): any {
|
|
617
717
|
const label = this.getLabel(projection.label);
|
|
618
|
-
return row[
|
|
718
|
+
return row[label.index].data.fields[projection.field];
|
|
619
719
|
}
|
|
620
720
|
|
|
621
|
-
private hashValue(projection: HashProjection, row:
|
|
721
|
+
private hashValue(projection: HashProjection, row: ResultSetRow): any {
|
|
622
722
|
const label = this.getLabel(projection.label);
|
|
623
|
-
return row[
|
|
723
|
+
return row[label.index].hash;
|
|
624
724
|
}
|
|
625
725
|
|
|
626
|
-
private factValue(projection: FactProjection, row:
|
|
726
|
+
private factValue(projection: FactProjection, row: ResultSetRow, factRecords: FactRecord[]): any {
|
|
627
727
|
const label = this.getLabel(projection.label);
|
|
628
|
-
|
|
728
|
+
const factReference: FactReference = {
|
|
729
|
+
type: label.type,
|
|
730
|
+
hash: row[label.index].hash
|
|
731
|
+
};
|
|
732
|
+
const [fact] = hydrateFromTree([factReference], factRecords);
|
|
733
|
+
return fact;
|
|
629
734
|
}
|
|
630
735
|
|
|
631
736
|
private getLabel(name: string) {
|
|
@@ -669,6 +774,10 @@ class ResultDescriptionBuilder {
|
|
|
669
774
|
}
|
|
670
775
|
|
|
671
776
|
private createResultDescription(queryDescription: QueryDescription, given: Label[], start: FactReference[], matches: Match[], projection: Projection, knownFacts: FactByLabel, path: number[]): ResultDescription {
|
|
777
|
+
const givenTuple = given.reduce((acc, label, index) => ({
|
|
778
|
+
...acc,
|
|
779
|
+
[label.name]: start[index]
|
|
780
|
+
}), {} as ReferencesByName);
|
|
672
781
|
({ queryDescription, knownFacts } = this.addEdges(queryDescription, given, start, knownFacts, path, matches));
|
|
673
782
|
if (!queryDescription.isSatisfiable()) {
|
|
674
783
|
// Abort the branch if the query is not satisfiable
|
|
@@ -678,7 +787,8 @@ class ResultDescriptionBuilder {
|
|
|
678
787
|
type: "composite",
|
|
679
788
|
components: []
|
|
680
789
|
},
|
|
681
|
-
childResultDescriptions: []
|
|
790
|
+
childResultDescriptions: [],
|
|
791
|
+
givenTuple
|
|
682
792
|
}
|
|
683
793
|
}
|
|
684
794
|
const childResultDescriptions: NamedResultDescription[] = [];
|
|
@@ -700,14 +810,16 @@ class ResultDescriptionBuilder {
|
|
|
700
810
|
type: "composite",
|
|
701
811
|
components: singularProjections
|
|
702
812
|
},
|
|
703
|
-
childResultDescriptions
|
|
813
|
+
childResultDescriptions,
|
|
814
|
+
givenTuple
|
|
704
815
|
};
|
|
705
816
|
}
|
|
706
817
|
else {
|
|
707
818
|
return {
|
|
708
819
|
queryDescription,
|
|
709
820
|
resultProjection: projection,
|
|
710
|
-
childResultDescriptions: []
|
|
821
|
+
childResultDescriptions: [],
|
|
822
|
+
givenTuple
|
|
711
823
|
}
|
|
712
824
|
}
|
|
713
825
|
}
|
|
@@ -887,5 +999,5 @@ function createResultComposer(description: ResultDescription, parentFactIdLength
|
|
|
887
999
|
name: child.name,
|
|
888
1000
|
resultComposer: createResultComposer(child, description.queryDescription.outputLength(), schema)
|
|
889
1001
|
}));
|
|
890
|
-
return new ResultComposer(sqlQuery, resultProjection, parentFactIdLength, childResultComposers);
|
|
1002
|
+
return new ResultComposer(sqlQuery, resultProjection, parentFactIdLength, description.givenTuple, childResultComposers);
|
|
891
1003
|
}
|
|
@@ -25,8 +25,6 @@ interface SpecificationSqlQuery {
|
|
|
25
25
|
|
|
26
26
|
interface InputDescription {
|
|
27
27
|
factIndex: number;
|
|
28
|
-
factTypeId: number;
|
|
29
|
-
factHash: string;
|
|
30
28
|
factTypeParameter: number;
|
|
31
29
|
factHashParameter: number;
|
|
32
30
|
}
|
|
@@ -42,7 +40,6 @@ interface EdgeDescription {
|
|
|
42
40
|
}
|
|
43
41
|
interface NotExistsConditionDescription {
|
|
44
42
|
edges: EdgeDescription[];
|
|
45
|
-
notExistsConditions: NotExistsConditionDescription[];
|
|
46
43
|
}
|
|
47
44
|
class QueryDescription {
|
|
48
45
|
constructor(
|
|
@@ -284,9 +281,7 @@ class DescriptionBuilder {
|
|
|
284
281
|
const factHashParameter = addParameter(input.factHash);
|
|
285
282
|
return {
|
|
286
283
|
factIndex: input.factIndex,
|
|
287
|
-
factTypeId,
|
|
288
284
|
factTypeParameter,
|
|
289
|
-
factHash: input.factHash,
|
|
290
285
|
factHashParameter
|
|
291
286
|
};
|
|
292
287
|
}
|
|
@@ -332,13 +327,9 @@ class DescriptionBuilder {
|
|
|
332
327
|
const edges = condition.edges.map(edge =>
|
|
333
328
|
this.buildEdgeDescription(feed, edge, addParameter)
|
|
334
329
|
);
|
|
335
|
-
const notExistsConditions = condition.notExistsConditions.map(condition =>
|
|
336
|
-
this.buildNotExistsConditionDescription(feed, condition, addParameter)
|
|
337
|
-
);
|
|
338
330
|
|
|
339
331
|
return {
|
|
340
|
-
edges
|
|
341
|
-
notExistsConditions
|
|
332
|
+
edges
|
|
342
333
|
}
|
|
343
334
|
}
|
|
344
335
|
}
|