Skip to content

Commit

Permalink
Merge pull request #38 from zazuko/simplify-aggregate
Browse files Browse the repository at this point in the history
Simplify aggregate
  • Loading branch information
BenjaminHofstetter authored Nov 14, 2024
2 parents 6d8688c + 05f3700 commit 888b542
Show file tree
Hide file tree
Showing 14 changed files with 619 additions and 445 deletions.

Large diffs are not rendered by default.

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;
}
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();
}
}
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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@ import { GraphPointer } from "clownface";
import { ICompositionToNodeLink } from "../../model/composition/composition-to-node-link";

export abstract class CompositionToNodeLinkFactory {
abstract createCompositionToNodeLink(node: GraphPointer): ICompositionToNodeLink;
abstract creteLink(node: GraphPointer): ICompositionToNodeLink;
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { CompositionToNodeLinkFactory } from "./composition-to-node-link-factory

export class IncomingCompositionToNodeLinkFactory extends CompositionToNodeLinkFactory {

createCompositionToNodeLink(node: GraphPointer): ICompositionToNodeLink {
creteLink(node: GraphPointer): ICompositionToNodeLink {

return new CompositionToNodeLink(node).invert();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { CompositionToNodeLinkFactory } from "./composition-to-node-link-factory

export class OutgoingCompositionToNodeLinkFactory extends CompositionToNodeLinkFactory {

createCompositionToNodeLink(node: GraphPointer): CompositionToNodeLink {
creteLink(node: GraphPointer): CompositionToNodeLink {
return new CompositionToNodeLink(node);
}
}
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[];
}

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'];
}
}
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;
}

}
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[];
}

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;
}
}
Loading

0 comments on commit 888b542

Please sign in to comment.