Skip to content

Commit 888b542

Browse files
Merge pull request #38 from zazuko/simplify-aggregate
Simplify aggregate
2 parents 6d8688c + 05f3700 commit 888b542

14 files changed

+619
-445
lines changed

projects/blueprint/src/app/core/service/graph/aggregate/aggregate.service.ts

+95-438
Large diffs are not rendered by default.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { GraphPointer } from "clownface";
2+
3+
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link";
4+
5+
export abstract class CompositionToCompositionLinkFactory {
6+
abstract creteLink(node: GraphPointer): ICompositionToCompositionLink;
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { GraphPointer } from "clownface";
2+
3+
import { CompositionToCompositionLinkFactory } from "./composition-to-composition-link-factory";
4+
import { CompositionToCompositionLink, ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link";
5+
6+
export class IncomingCompositionToCompositionLinkFactory extends CompositionToCompositionLinkFactory {
7+
8+
creteLink(node: GraphPointer): ICompositionToCompositionLink {
9+
10+
return new CompositionToCompositionLink(node).invert();
11+
}
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import { GraphPointer } from "clownface";
2+
import { CompositionToCompositionLinkFactory } from "./composition-to-composition-link-factory";
3+
import { CompositionToCompositionLink, ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link";
4+
5+
export class OutgoingCompositionToCompositionLinkFactory extends CompositionToCompositionLinkFactory {
6+
7+
creteLink(node: GraphPointer): ICompositionToCompositionLink {
8+
return new CompositionToCompositionLink(node);
9+
}
10+
}

projects/blueprint/src/app/core/service/graph/aggregate/factory/composition-to-node-link-factory/composition-to-node-link-factory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,5 @@ import { GraphPointer } from "clownface";
33
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link";
44

55
export abstract class CompositionToNodeLinkFactory {
6-
abstract createCompositionToNodeLink(node: GraphPointer): ICompositionToNodeLink;
6+
abstract creteLink(node: GraphPointer): ICompositionToNodeLink;
77
}

projects/blueprint/src/app/core/service/graph/aggregate/factory/composition-to-node-link-factory/incoming-composition-to-node-link-factory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { CompositionToNodeLinkFactory } from "./composition-to-node-link-factory
55

66
export class IncomingCompositionToNodeLinkFactory extends CompositionToNodeLinkFactory {
77

8-
createCompositionToNodeLink(node: GraphPointer): ICompositionToNodeLink {
8+
creteLink(node: GraphPointer): ICompositionToNodeLink {
99

1010
return new CompositionToNodeLink(node).invert();
1111
}

projects/blueprint/src/app/core/service/graph/aggregate/factory/composition-to-node-link-factory/outgoing-composition-to-node-link-factory.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { CompositionToNodeLinkFactory } from "./composition-to-node-link-factory
44

55
export class OutgoingCompositionToNodeLinkFactory extends CompositionToNodeLinkFactory {
66

7-
createCompositionToNodeLink(node: GraphPointer): CompositionToNodeLink {
7+
creteLink(node: GraphPointer): CompositionToNodeLink {
88
return new CompositionToNodeLink(node);
99
}
1010
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link";
2+
3+
export abstract class CompositionToCompositionQueryStrategy {
4+
abstract filter(links: ICompositionToCompositionLink[], classIris: string[]): ICompositionToCompositionLink[];
5+
abstract createQuery(link: ICompositionToCompositionLink, subject: string): string[];
6+
}
7+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link";
2+
import { CompositionToCompositionQueryStrategy } from "./composition-to-composition-query-strategy";
3+
4+
5+
6+
7+
export class CompositionToCompositionRootOfSourceStrategy extends CompositionToCompositionQueryStrategy {
8+
filter(links: ICompositionToCompositionLink[], classIris: string[]): ICompositionToCompositionLink[] {
9+
return links.filter(link => {
10+
const sourceComposition = link.sourceComposition;
11+
if (sourceComposition === null) {
12+
return false;
13+
}
14+
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri);
15+
return classIris.includes(root?.targetClassIri);
16+
});
17+
}
18+
19+
createQuery(link: ICompositionToCompositionLink, subject: string): string[] {
20+
console.log('%cCompositionToCompositionRootOfSourceStrategy query', 'color: cyan', link.label);
21+
return this.#createQueryForRootOfSourceAggregate(link, subject);
22+
}
23+
24+
#createQueryForRootOfSourceAggregate(link: ICompositionToCompositionLink, subject: string): string[] {
25+
return ['query'];
26+
}
27+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
import { blueprint, rdf, rdfs } from "@blueprint/ontology";
2+
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link";
3+
import { CompositionToNodeQueryStrategy } from "./composition-to-node-query-strategy";
4+
5+
/**
6+
* ConnectionPointOfSourceStrategy
7+
* The subject class is a connection point of the source composition. That means we are in the middle of the Hierarchy and we link to
8+
* the target node.
9+
*/
10+
export class CompositionToNodeConnectionPointStrategy implements CompositionToNodeQueryStrategy {
11+
filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[] {
12+
return links.filter(link => {
13+
const sourceComposition = link.sourceComposition;
14+
if (sourceComposition === null) {
15+
return false;
16+
}
17+
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri);
18+
const memberClasses = sourceComposition.connectionPoints.filter(memberNode => classIris.includes(memberNode.targetClassIri) && memberNode.targetClassIri !== root.targetClassIri);
19+
return memberClasses.length > 0;
20+
});
21+
}
22+
23+
createQuery(link: ICompositionToNodeLink, subject: string): string[] {
24+
console.log('%cConnectionPointOfSourceStrategy query', 'color: green');
25+
26+
return this.#createQueryForSourceConnectionPoint(link, subject);
27+
}
28+
29+
30+
#createQueryForSourceConnectionPoint(link: ICompositionToNodeLink, subject: string): string[] {
31+
const pathFromLink = link.path;
32+
const queries = pathFromLink.flatMap((path, outerIndex) => {
33+
const pathToTarget = [...path];
34+
35+
const body = pathToTarget.map((pathElement, index) => {
36+
if (index === 0 && index === pathToTarget.length - 1) {
37+
// it's the start and the end of the path
38+
return `
39+
# first and last path element - form link
40+
<${subject}> a <${pathElement.sourceClassIri}> .
41+
<${subject}> ${pathElement.path} ?result .
42+
?result a <${pathElement.targetClassIri}> .
43+
44+
VALUES ?resultP {
45+
${rdf.typePrefixed}
46+
${rdfs.labelPrefixed}
47+
}
48+
?result ?resultP ?resultO .
49+
<${subject}> ?resultP ?subjectO .
50+
51+
`;
52+
}
53+
if (index === pathToTarget.length - 1 && index !== 0) {
54+
// last element
55+
if (index === (pathToTarget.length - 1)) {
56+
console.log('connector is last ')
57+
return `
58+
# connector last
59+
?element_${outerIndex}_${index} ${pathElement.path} ?result .
60+
?result a <${pathElement.targetClassIri}> .
61+
VALUES ?resultP {
62+
${rdf.typePrefixed}
63+
${rdfs.labelPrefixed}
64+
}
65+
?result ?resultP ?resultO .
66+
`;
67+
}
68+
69+
console.log('pathElement connector is in the middle');
70+
console.log('pathElement', index);
71+
return `
72+
# connector in the middle
73+
?element_${index} ${pathElement.path} ?element_${index + 1} .
74+
?element_${index + 1} a <${pathElement.targetClassIri}> .
75+
VALUES ?connectionPointP {
76+
${rdfs.labelPrefixed}
77+
${rdf.typePrefixed}
78+
}
79+
?element_${index + 1} ?connectionPointP ?connectionPointO .
80+
`;
81+
82+
}
83+
if (index === 0) {
84+
return `
85+
# first path element - form link
86+
<${subject}> a <${pathElement.sourceClassIri}> .
87+
<${subject}> ${pathElement.path} ?element_${index + 1} .
88+
?element_${index + 1} a <${pathElement.targetClassIri}> .
89+
`;
90+
}
91+
return `
92+
# middle path element - form composition
93+
?element_${index} ${pathElement.path} ?element_${index + 1} .
94+
?element_${index + 1} a <${pathElement.targetClassIri}> .
95+
`;
96+
}).join('\n');
97+
98+
const query = `
99+
${rdf.sparqlPrefix()}
100+
${rdfs.sparqlPrefix()}
101+
${blueprint.sparqlPrefix()}
102+
103+
CONSTRUCT {
104+
<${link.iri}> ${blueprint.resultPrefixed} <${link.iri}/${outerIndex}> .
105+
<${link.iri}/${outerIndex}> a ${blueprint.CompositionLinkResultPrefixed} .
106+
<${link.iri}/${outerIndex}> ${blueprint.resultPrefixed} ?result .
107+
<${link.iri}/${outerIndex}> ${rdfs.labelPrefixed} "${link.label}" .
108+
?result ?resultP ?resultO .
109+
<${subject}> ?resultP ?subjectO .
110+
111+
?result ${blueprint.sourcePrefixed} <${subject}>
112+
113+
} WHERE {
114+
{
115+
${body}
116+
}
117+
}`;
118+
return [query];
119+
120+
});
121+
return queries;
122+
}
123+
124+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link";
2+
3+
export abstract class CompositionToNodeQueryStrategy {
4+
abstract filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[];
5+
abstract createQuery(link: ICompositionToNodeLink, subject: string): string[];
6+
}
7+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
import { blueprint, rdf, rdfs } from "@blueprint/ontology";
2+
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link";
3+
import { CompositionToNodeQueryStrategy } from "./composition-to-node-query-strategy";
4+
5+
/**
6+
* CompositionToNodeRootStrategy
7+
* The subject class is the root of the source composition. That means we are on top of the Hierarchy and we link to
8+
* the target node.
9+
*/
10+
export class CompositionToNodeRootStrategy extends CompositionToNodeQueryStrategy {
11+
filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[] {
12+
return links.filter(link => {
13+
const sourceComposition = link.sourceComposition;
14+
if (sourceComposition === null) {
15+
return false;
16+
}
17+
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri);
18+
return classIris.includes(root?.targetClassIri);
19+
});
20+
}
21+
22+
createQuery(link: ICompositionToNodeLink, subject: string): string[] {
23+
console.log('%cCompositionToNodeRootStrategy query', 'color: red', link.label);
24+
return this.#createQueryForRootOfSourceAggregate(link, subject);
25+
}
26+
27+
#createQueryForRootOfSourceAggregate(link: ICompositionToNodeLink, subject: string): string[] {
28+
const linkTargetNodeClass = link.targetNodeIri;
29+
const linkSourceComposition = link.sourceComposition;
30+
31+
if (linkSourceComposition === null) {
32+
console.warn('No source composition');
33+
return [];
34+
}
35+
const q = link.path.flatMap(pathFromLink => {
36+
const firstPathElement = pathFromLink[0];
37+
38+
const connectionPoints = linkSourceComposition.connectionPoints.filter(connectorNode => connectorNode.targetClassIri === firstPathElement.sourceClassIri);
39+
if (connectionPoints.length === 0) {
40+
console.warn('No connection points');
41+
return [];
42+
}
43+
44+
// the subject is the root node of the source of the link -> all links are from this to the targetNode
45+
// that means we are always source of the link
46+
const lastPathElement = pathFromLink[pathFromLink.length - 1];
47+
if (lastPathElement.targetClassIri !== linkTargetNodeClass) {
48+
console.warn(`Last path element is not the target class for link <${link.iri}>. This is a configuration error.`);
49+
return [];
50+
}
51+
52+
const sourceConnectors = connectionPoints.filter(connectorNode => connectorNode.targetClassIri === firstPathElement.sourceClassIri);
53+
if (sourceConnectors.length === 0) {
54+
console.warn(`No connector class <${firstPathElement.sourceClassIri}> for link <${link.iri}>. This is a configuration error.`);
55+
return [];
56+
}
57+
58+
const queries = sourceConnectors.flatMap((connectionPoint, outerIndex) => {
59+
const pathFromRoot = connectionPoint.pathFromRoot;
60+
const linkPath = pathFromLink;
61+
const path = [...pathFromRoot, ...linkPath];
62+
63+
const body = path.map((pathElement, index) => {
64+
if (index === pathFromRoot.length - 1 && pathFromRoot.length != 0) {
65+
if (index === 0) {
66+
return `
67+
<${subject}> a <${pathElement.sourceClassIri}> .
68+
<${subject}> ${pathElement.path} ?element_${outerIndex}_${index + 1} .
69+
VALUES ?connectionPointP {
70+
${rdfs.labelPrefixed}
71+
${rdf.typePrefixed}
72+
}
73+
?element_${outerIndex}_${index + 1} ?connectionPointP ?connectionPointO .
74+
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> .
75+
`;
76+
} else {
77+
return `
78+
?element_${outerIndex}_${index} ${pathElement.path} ?element_${outerIndex}_${index + 1} .
79+
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> .
80+
VALUES ?connectionPointP {
81+
${rdfs.labelPrefixed}
82+
${rdf.typePrefixed}
83+
}
84+
?element_${outerIndex}_${index + 1} ?connectionPointP ?connectionPointO .
85+
`;
86+
}
87+
}
88+
89+
if (index === 0) {
90+
if (pathFromRoot.length === 0) {
91+
return `
92+
# first path element - form link
93+
<${subject}> a <${pathElement.sourceClassIri}> .
94+
<${subject}> ${pathElement.path} ?result .
95+
?result a <${pathElement.targetClassIri}> .
96+
97+
VALUES ?resultP {
98+
${rdf.typePrefixed}
99+
${rdfs.labelPrefixed}
100+
}
101+
?result ?resultP ?resultO .
102+
BIND(?result as ?connectionPointO)
103+
BIND(?result as ?element_0_0)
104+
105+
`;
106+
}
107+
return `
108+
<${subject}> a <${pathElement.sourceClassIri}> .
109+
<${subject}> ${pathElement.path} ?element_${outerIndex}_${index + 1} .
110+
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> .
111+
`;
112+
}
113+
if (index === path.length - 1) {
114+
return `
115+
?element_${outerIndex}_${index} ${pathElement.path} ?result .
116+
?result a <${pathElement.targetClassIri}> .
117+
118+
VALUES ?resultP {
119+
${rdf.typePrefixed}
120+
${rdfs.labelPrefixed}
121+
}
122+
?result ?resultP ?resultO .
123+
`;
124+
}
125+
return `
126+
?element_${outerIndex}_${index} ${pathElement.path} ?element_${outerIndex}_${index + 1} .
127+
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> .
128+
`;
129+
}).join('\n');
130+
131+
132+
const query = `
133+
${rdf.sparqlPrefix()}
134+
${rdfs.sparqlPrefix()}
135+
${blueprint.sparqlPrefix()}
136+
137+
CONSTRUCT {
138+
<${link.iri}> ${blueprint.resultPrefixed} <${link.iri}/${outerIndex}> .
139+
<${link.iri}/${outerIndex}> a ${blueprint.CompositionLinkResultPrefixed} .
140+
<${link.iri}/${outerIndex}> ${blueprint.resultPrefixed} ?result .
141+
<${link.iri}/${outerIndex}> ${rdfs.labelPrefixed} "${link.label}" .
142+
?result ?resultP ?resultO .
143+
?element_${outerIndex}_${pathFromRoot.length} ?connectionPointP ?connectionPointO .
144+
?result ${blueprint.sourcePrefixed} ?element_${outerIndex}_${pathFromRoot.length} .
145+
} WHERE {
146+
${body}
147+
}`;
148+
return query;
149+
});
150+
return queries;
151+
});
152+
return q;
153+
}
154+
}

0 commit comments

Comments
 (0)