-
Notifications
You must be signed in to change notification settings - Fork 3
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from zazuko/simplify-aggregate
Simplify aggregate
- Loading branch information
Showing
14 changed files
with
619 additions
and
445 deletions.
There are no files selected for viewing
533 changes: 95 additions & 438 deletions
533
projects/blueprint/src/app/core/service/graph/aggregate/aggregate.service.ts
Large diffs are not rendered by default.
Oops, something went wrong.
7 changes: 7 additions & 0 deletions
7
...actory/composition-to-composition-link-factory/composition-to-composition-link-factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { GraphPointer } from "clownface"; | ||
|
||
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link"; | ||
|
||
export abstract class CompositionToCompositionLinkFactory { | ||
abstract creteLink(node: GraphPointer): ICompositionToCompositionLink; | ||
} |
12 changes: 12 additions & 0 deletions
12
...mposition-to-composition-link-factory/incoming-composition-to-composition-link-factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
import { GraphPointer } from "clownface"; | ||
|
||
import { CompositionToCompositionLinkFactory } from "./composition-to-composition-link-factory"; | ||
import { CompositionToCompositionLink, ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link"; | ||
|
||
export class IncomingCompositionToCompositionLinkFactory extends CompositionToCompositionLinkFactory { | ||
|
||
creteLink(node: GraphPointer): ICompositionToCompositionLink { | ||
|
||
return new CompositionToCompositionLink(node).invert(); | ||
} | ||
} |
10 changes: 10 additions & 0 deletions
10
...mposition-to-composition-link-factory/outgoing-composition-to-composition-link-factory.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import { GraphPointer } from "clownface"; | ||
import { CompositionToCompositionLinkFactory } from "./composition-to-composition-link-factory"; | ||
import { CompositionToCompositionLink, ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link"; | ||
|
||
export class OutgoingCompositionToCompositionLinkFactory extends CompositionToCompositionLinkFactory { | ||
|
||
creteLink(node: GraphPointer): ICompositionToCompositionLink { | ||
return new CompositionToCompositionLink(node); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
7 changes: 7 additions & 0 deletions
7
...aggregate/strategy/composition-to-compostion/composition-to-composition-query-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link"; | ||
|
||
export abstract class CompositionToCompositionQueryStrategy { | ||
abstract filter(links: ICompositionToCompositionLink[], classIris: string[]): ICompositionToCompositionLink[]; | ||
abstract createQuery(link: ICompositionToCompositionLink, subject: string): string[]; | ||
} | ||
|
27 changes: 27 additions & 0 deletions
27
.../strategy/composition-to-compostion/composition-to-composition-root-of-source-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
import { ICompositionToCompositionLink } from "../../model/composition/composition-to-composition-link"; | ||
import { CompositionToCompositionQueryStrategy } from "./composition-to-composition-query-strategy"; | ||
|
||
|
||
|
||
|
||
export class CompositionToCompositionRootOfSourceStrategy extends CompositionToCompositionQueryStrategy { | ||
filter(links: ICompositionToCompositionLink[], classIris: string[]): ICompositionToCompositionLink[] { | ||
return links.filter(link => { | ||
const sourceComposition = link.sourceComposition; | ||
if (sourceComposition === null) { | ||
return false; | ||
} | ||
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri); | ||
return classIris.includes(root?.targetClassIri); | ||
}); | ||
} | ||
|
||
createQuery(link: ICompositionToCompositionLink, subject: string): string[] { | ||
console.log('%cCompositionToCompositionRootOfSourceStrategy query', 'color: cyan', link.label); | ||
return this.#createQueryForRootOfSourceAggregate(link, subject); | ||
} | ||
|
||
#createQueryForRootOfSourceAggregate(link: ICompositionToCompositionLink, subject: string): string[] { | ||
return ['query']; | ||
} | ||
} |
124 changes: 124 additions & 0 deletions
124
...h/aggregate/strategy/composition-to-node/composition-to-node-connection-point-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
import { blueprint, rdf, rdfs } from "@blueprint/ontology"; | ||
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link"; | ||
import { CompositionToNodeQueryStrategy } from "./composition-to-node-query-strategy"; | ||
|
||
/** | ||
* ConnectionPointOfSourceStrategy | ||
* 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 | ||
* the target node. | ||
*/ | ||
export class CompositionToNodeConnectionPointStrategy implements CompositionToNodeQueryStrategy { | ||
filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[] { | ||
return links.filter(link => { | ||
const sourceComposition = link.sourceComposition; | ||
if (sourceComposition === null) { | ||
return false; | ||
} | ||
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri); | ||
const memberClasses = sourceComposition.connectionPoints.filter(memberNode => classIris.includes(memberNode.targetClassIri) && memberNode.targetClassIri !== root.targetClassIri); | ||
return memberClasses.length > 0; | ||
}); | ||
} | ||
|
||
createQuery(link: ICompositionToNodeLink, subject: string): string[] { | ||
console.log('%cConnectionPointOfSourceStrategy query', 'color: green'); | ||
|
||
return this.#createQueryForSourceConnectionPoint(link, subject); | ||
} | ||
|
||
|
||
#createQueryForSourceConnectionPoint(link: ICompositionToNodeLink, subject: string): string[] { | ||
const pathFromLink = link.path; | ||
const queries = pathFromLink.flatMap((path, outerIndex) => { | ||
const pathToTarget = [...path]; | ||
|
||
const body = pathToTarget.map((pathElement, index) => { | ||
if (index === 0 && index === pathToTarget.length - 1) { | ||
// it's the start and the end of the path | ||
return ` | ||
# first and last path element - form link | ||
<${subject}> a <${pathElement.sourceClassIri}> . | ||
<${subject}> ${pathElement.path} ?result . | ||
?result a <${pathElement.targetClassIri}> . | ||
VALUES ?resultP { | ||
${rdf.typePrefixed} | ||
${rdfs.labelPrefixed} | ||
} | ||
?result ?resultP ?resultO . | ||
<${subject}> ?resultP ?subjectO . | ||
`; | ||
} | ||
if (index === pathToTarget.length - 1 && index !== 0) { | ||
// last element | ||
if (index === (pathToTarget.length - 1)) { | ||
console.log('connector is last ') | ||
return ` | ||
# connector last | ||
?element_${outerIndex}_${index} ${pathElement.path} ?result . | ||
?result a <${pathElement.targetClassIri}> . | ||
VALUES ?resultP { | ||
${rdf.typePrefixed} | ||
${rdfs.labelPrefixed} | ||
} | ||
?result ?resultP ?resultO . | ||
`; | ||
} | ||
|
||
console.log('pathElement connector is in the middle'); | ||
console.log('pathElement', index); | ||
return ` | ||
# connector in the middle | ||
?element_${index} ${pathElement.path} ?element_${index + 1} . | ||
?element_${index + 1} a <${pathElement.targetClassIri}> . | ||
VALUES ?connectionPointP { | ||
${rdfs.labelPrefixed} | ||
${rdf.typePrefixed} | ||
} | ||
?element_${index + 1} ?connectionPointP ?connectionPointO . | ||
`; | ||
|
||
} | ||
if (index === 0) { | ||
return ` | ||
# first path element - form link | ||
<${subject}> a <${pathElement.sourceClassIri}> . | ||
<${subject}> ${pathElement.path} ?element_${index + 1} . | ||
?element_${index + 1} a <${pathElement.targetClassIri}> . | ||
`; | ||
} | ||
return ` | ||
# middle path element - form composition | ||
?element_${index} ${pathElement.path} ?element_${index + 1} . | ||
?element_${index + 1} a <${pathElement.targetClassIri}> . | ||
`; | ||
}).join('\n'); | ||
|
||
const query = ` | ||
${rdf.sparqlPrefix()} | ||
${rdfs.sparqlPrefix()} | ||
${blueprint.sparqlPrefix()} | ||
CONSTRUCT { | ||
<${link.iri}> ${blueprint.resultPrefixed} <${link.iri}/${outerIndex}> . | ||
<${link.iri}/${outerIndex}> a ${blueprint.CompositionLinkResultPrefixed} . | ||
<${link.iri}/${outerIndex}> ${blueprint.resultPrefixed} ?result . | ||
<${link.iri}/${outerIndex}> ${rdfs.labelPrefixed} "${link.label}" . | ||
?result ?resultP ?resultO . | ||
<${subject}> ?resultP ?subjectO . | ||
?result ${blueprint.sourcePrefixed} <${subject}> | ||
} WHERE { | ||
{ | ||
${body} | ||
} | ||
}`; | ||
return [query]; | ||
|
||
}); | ||
return queries; | ||
} | ||
|
||
} |
7 changes: 7 additions & 0 deletions
7
...ervice/graph/aggregate/strategy/composition-to-node/composition-to-node-query-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link"; | ||
|
||
export abstract class CompositionToNodeQueryStrategy { | ||
abstract filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[]; | ||
abstract createQuery(link: ICompositionToNodeLink, subject: string): string[]; | ||
} | ||
|
154 changes: 154 additions & 0 deletions
154
...raph/aggregate/strategy/composition-to-node/composition-to-node-root-strategy-strategy.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,154 @@ | ||
import { blueprint, rdf, rdfs } from "@blueprint/ontology"; | ||
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link"; | ||
import { CompositionToNodeQueryStrategy } from "./composition-to-node-query-strategy"; | ||
|
||
/** | ||
* CompositionToNodeRootStrategy | ||
* The subject class is the root of the source composition. That means we are on top of the Hierarchy and we link to | ||
* the target node. | ||
*/ | ||
export class CompositionToNodeRootStrategy extends CompositionToNodeQueryStrategy { | ||
filter(links: ICompositionToNodeLink[], classIris: string[]): ICompositionToNodeLink[] { | ||
return links.filter(link => { | ||
const sourceComposition = link.sourceComposition; | ||
if (sourceComposition === null) { | ||
return false; | ||
} | ||
const root = sourceComposition.aggregateNodes.find(memberNode => memberNode.iri === sourceComposition.rootIri); | ||
return classIris.includes(root?.targetClassIri); | ||
}); | ||
} | ||
|
||
createQuery(link: ICompositionToNodeLink, subject: string): string[] { | ||
console.log('%cCompositionToNodeRootStrategy query', 'color: red', link.label); | ||
return this.#createQueryForRootOfSourceAggregate(link, subject); | ||
} | ||
|
||
#createQueryForRootOfSourceAggregate(link: ICompositionToNodeLink, subject: string): string[] { | ||
const linkTargetNodeClass = link.targetNodeIri; | ||
const linkSourceComposition = link.sourceComposition; | ||
|
||
if (linkSourceComposition === null) { | ||
console.warn('No source composition'); | ||
return []; | ||
} | ||
const q = link.path.flatMap(pathFromLink => { | ||
const firstPathElement = pathFromLink[0]; | ||
|
||
const connectionPoints = linkSourceComposition.connectionPoints.filter(connectorNode => connectorNode.targetClassIri === firstPathElement.sourceClassIri); | ||
if (connectionPoints.length === 0) { | ||
console.warn('No connection points'); | ||
return []; | ||
} | ||
|
||
// the subject is the root node of the source of the link -> all links are from this to the targetNode | ||
// that means we are always source of the link | ||
const lastPathElement = pathFromLink[pathFromLink.length - 1]; | ||
if (lastPathElement.targetClassIri !== linkTargetNodeClass) { | ||
console.warn(`Last path element is not the target class for link <${link.iri}>. This is a configuration error.`); | ||
return []; | ||
} | ||
|
||
const sourceConnectors = connectionPoints.filter(connectorNode => connectorNode.targetClassIri === firstPathElement.sourceClassIri); | ||
if (sourceConnectors.length === 0) { | ||
console.warn(`No connector class <${firstPathElement.sourceClassIri}> for link <${link.iri}>. This is a configuration error.`); | ||
return []; | ||
} | ||
|
||
const queries = sourceConnectors.flatMap((connectionPoint, outerIndex) => { | ||
const pathFromRoot = connectionPoint.pathFromRoot; | ||
const linkPath = pathFromLink; | ||
const path = [...pathFromRoot, ...linkPath]; | ||
|
||
const body = path.map((pathElement, index) => { | ||
if (index === pathFromRoot.length - 1 && pathFromRoot.length != 0) { | ||
if (index === 0) { | ||
return ` | ||
<${subject}> a <${pathElement.sourceClassIri}> . | ||
<${subject}> ${pathElement.path} ?element_${outerIndex}_${index + 1} . | ||
VALUES ?connectionPointP { | ||
${rdfs.labelPrefixed} | ||
${rdf.typePrefixed} | ||
} | ||
?element_${outerIndex}_${index + 1} ?connectionPointP ?connectionPointO . | ||
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> . | ||
`; | ||
} else { | ||
return ` | ||
?element_${outerIndex}_${index} ${pathElement.path} ?element_${outerIndex}_${index + 1} . | ||
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> . | ||
VALUES ?connectionPointP { | ||
${rdfs.labelPrefixed} | ||
${rdf.typePrefixed} | ||
} | ||
?element_${outerIndex}_${index + 1} ?connectionPointP ?connectionPointO . | ||
`; | ||
} | ||
} | ||
|
||
if (index === 0) { | ||
if (pathFromRoot.length === 0) { | ||
return ` | ||
# first path element - form link | ||
<${subject}> a <${pathElement.sourceClassIri}> . | ||
<${subject}> ${pathElement.path} ?result . | ||
?result a <${pathElement.targetClassIri}> . | ||
VALUES ?resultP { | ||
${rdf.typePrefixed} | ||
${rdfs.labelPrefixed} | ||
} | ||
?result ?resultP ?resultO . | ||
BIND(?result as ?connectionPointO) | ||
BIND(?result as ?element_0_0) | ||
`; | ||
} | ||
return ` | ||
<${subject}> a <${pathElement.sourceClassIri}> . | ||
<${subject}> ${pathElement.path} ?element_${outerIndex}_${index + 1} . | ||
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> . | ||
`; | ||
} | ||
if (index === path.length - 1) { | ||
return ` | ||
?element_${outerIndex}_${index} ${pathElement.path} ?result . | ||
?result a <${pathElement.targetClassIri}> . | ||
VALUES ?resultP { | ||
${rdf.typePrefixed} | ||
${rdfs.labelPrefixed} | ||
} | ||
?result ?resultP ?resultO . | ||
`; | ||
} | ||
return ` | ||
?element_${outerIndex}_${index} ${pathElement.path} ?element_${outerIndex}_${index + 1} . | ||
?element_${outerIndex}_${index + 1} a <${pathElement.targetClassIri}> . | ||
`; | ||
}).join('\n'); | ||
|
||
|
||
const query = ` | ||
${rdf.sparqlPrefix()} | ||
${rdfs.sparqlPrefix()} | ||
${blueprint.sparqlPrefix()} | ||
CONSTRUCT { | ||
<${link.iri}> ${blueprint.resultPrefixed} <${link.iri}/${outerIndex}> . | ||
<${link.iri}/${outerIndex}> a ${blueprint.CompositionLinkResultPrefixed} . | ||
<${link.iri}/${outerIndex}> ${blueprint.resultPrefixed} ?result . | ||
<${link.iri}/${outerIndex}> ${rdfs.labelPrefixed} "${link.label}" . | ||
?result ?resultP ?resultO . | ||
?element_${outerIndex}_${pathFromRoot.length} ?connectionPointP ?connectionPointO . | ||
?result ${blueprint.sourcePrefixed} ?element_${outerIndex}_${pathFromRoot.length} . | ||
} WHERE { | ||
${body} | ||
}`; | ||
return query; | ||
}); | ||
return queries; | ||
}); | ||
return q; | ||
} | ||
} |
Oops, something went wrong.