sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
296 lines (268 loc) • 9.92 kB
text/typescript
import { DataFactory } from "rdf-data-factory";
import { Pattern, Variable } from "sparqljs";
import { OptionTypes } from "../../components/builder-section/groupwrapper/criteriagroup/optionsgroup/OptionsGroup";
import GroupWrapper from "../../components/builder-section/groupwrapper/GroupWrapper";
import { AbstractWidget } from "../../components/widgets/AbstractWidget";
import ISparnaturalSpecification from "../../spec-providers/ISparnaturalSpecification";
import ClassBuilder from "./ClassBuilder";
import IntersectionBuilder from "./IntersectionBuilder";
import SparqlFactory from "./SparqlFactory";
import ValueBuilderIfc, { ValueBuilderFactory } from "./ValueBuilder";
import { SelectAllValue } from "../../components/builder-section/groupwrapper/criteriagroup/edit-components/EditComponents";
const factory = new DataFactory();
export default class WhereBuilder {
// variables set in construtor
#grpWrapper: GroupWrapper;
#specProvider: ISparnaturalSpecification;
#typePredicate: string;
#isChild: boolean;
#isInOption: boolean;
settings: any;
#valueBuilder: ValueBuilderIfc;
// patterns built in the build process
#resultPtrns: Pattern[] = [];
#startClassPtrn: Pattern[] = [];
#endClassPtrn: Pattern[] = [];
#intersectionPtrn: Pattern[] = [];
#whereChildPtrns: Pattern[] = [];
#andChildPtrns: Pattern[] = [];
#rdfPtrns: Pattern[] = [];
#executedAfterPtrns: Pattern[] = [];
// default vars gathered from children
#defaultVars: Variable[] = [];
constructor(
grpWrapper: GroupWrapper,
specProvider: ISparnaturalSpecification,
typePredicate: string,
isChild: boolean,
isInOption: boolean,
settings: any
) {
this.#grpWrapper = grpWrapper;
this.#specProvider = specProvider;
this.#typePredicate = typePredicate;
this.#isChild = isChild;
this.#isInOption = isInOption;
this.settings = settings;
// create the object to convert widget values to SPARQL
let endClassValue =
this.#grpWrapper.CriteriaGroup.EndClassGroup.endClassVal.type;
// this is because the query geenration may be triggered while the end class is not there yet
if (endClassValue != null) {
this.#valueBuilder = new ValueBuilderFactory().buildValueBuilder(
this.#specProvider
.getProperty(
this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup.getTypeSelected()
)
.getPropertyType(endClassValue)
);
// pass everything needed to generate SPARQL
this.#valueBuilder.init(
this.#specProvider,
this.#grpWrapper.CriteriaGroup.StartClassGroup.startClassVal,
this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup.objectPropVal,
this.#grpWrapper.CriteriaGroup.EndClassGroup.endClassVal,
this.#grpWrapper.CriteriaGroup.EndClassGroup.isVarSelected(),
this.#grpWrapper.CriteriaGroup.endClassWidgetGroup
.getWidgetValues()
.filter((v) => !(v instanceof SelectAllValue))
.map((v) => {
return v.value;
})
);
}
}
build() {
this.#buildChildPatterns();
this.#buildRdfPtrn();
this.#buildStartClassPtrn();
this.#buildEndClassPtrn();
this.#buildIntersectionPtrn();
this.#buildGrpWrapperPtrn();
}
#buildChildPatterns() {
if (this.#grpWrapper.whereChild) this.#buildWhereChildPtrn();
if (this.#grpWrapper.andSibling) this.#buildAndChildPtrn();
}
#buildWhereChildPtrn() {
const builder = new WhereBuilder(
this.#grpWrapper.whereChild,
this.#specProvider,
this.#typePredicate,
true,
this.#grpWrapper.optionState !== OptionTypes.NONE,
this.settings
);
builder.build();
this.#whereChildPtrns = builder.getResultPtrns();
// gather default vars from children
this.#defaultVars.push(...builder.getDefaultVars());
// gather patterns to be executed after
this.#executedAfterPtrns.push(...builder.getExecutedAfterPtrns());
}
#buildAndChildPtrn() {
const builder = new WhereBuilder(
this.#grpWrapper.andSibling,
this.#specProvider,
this.#typePredicate,
true,
this.#isInOption,
this.settings
);
builder.build();
this.#andChildPtrns = builder.getResultPtrns();
// gather default vars from children
this.#defaultVars.push(...builder.getDefaultVars());
// gather patterns to be executed after
this.#executedAfterPtrns.push(...builder.getExecutedAfterPtrns());
}
#buildRdfPtrn() {
// exclude the "Any" value
if (
(this.#grpWrapper.CriteriaGroup.endClassWidgetGroup?.getWidgetValues()?.length > 0)
&&
!(this.#grpWrapper.CriteriaGroup.endClassWidgetGroup?.hasAnyValue())
) {
this.#rdfPtrns = this.#valueBuilder.build();
}
}
#buildEndClassPtrn() {
const endClsGrp = this.#grpWrapper.CriteriaGroup.EndClassGroup;
const endClsBuilder = new ClassBuilder(
endClsGrp,
this.#specProvider,
this.#valueBuilder?.isBlockingEnd(),
this.#typePredicate
);
endClsBuilder.build();
this.#endClassPtrn = endClsBuilder.getPattern();
if (endClsBuilder.getDefaultVar()) {
this.#defaultVars.push(endClsBuilder.getDefaultVar());
}
}
#buildStartClassPtrn() {
const startClsGrp = this.#grpWrapper.CriteriaGroup.StartClassGroup;
const startClsBuilder = new ClassBuilder(
startClsGrp,
this.#specProvider,
this.#valueBuilder?.isBlockingStart(),
this.#typePredicate
);
startClsBuilder.build();
this.#startClassPtrn = startClsBuilder.getPattern();
if (startClsBuilder.getDefaultVar()) {
this.#defaultVars.push(startClsBuilder.getDefaultVar());
}
}
#buildIntersectionPtrn() {
const objectPropCls = this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup;
const intersectionBuilder = new IntersectionBuilder(
this.#grpWrapper.CriteriaGroup.StartClassGroup.getVarName(),
this.#grpWrapper.CriteriaGroup.EndClassGroup.getVarName(),
this.#valueBuilder,
objectPropCls,
this.#specProvider,
this.settings
);
intersectionBuilder.build();
this.#intersectionPtrn = intersectionBuilder.getPattern();
console.log(this.#intersectionPtrn);
console.log(objectPropCls);
}
#buildGrpWrapperPtrn() {
// The startClassPtrn does not need to be created if it is a WHERE or AND child
const hasStartClass = !this.#isChild;
const hasEndClass =
!this.#specProvider
.getEntity(
this.#grpWrapper.CriteriaGroup.EndClassGroup.getTypeSelected()
)
.isLiteralEntity() &&
(this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup.getTypeSelected() ==
null ||
!this.#specProvider
.getProperty(
this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup.getTypeSelected()
)
.omitClassCriteria());
const hasIntersectionTriple = this.#intersectionPtrn;
let exceptStartPtrn: Pattern[] = [];
if (hasIntersectionTriple && this.#intersectionPtrn)
exceptStartPtrn.push(...this.#intersectionPtrn);
if (hasEndClass) exceptStartPtrn.push(...this.#endClassPtrn);
exceptStartPtrn.push(...this.#rdfPtrns);
exceptStartPtrn.push(...this.#whereChildPtrns);
this.#createOptionStatePtrn(hasStartClass, exceptStartPtrn);
this.#resultPtrns.push(...this.#andChildPtrns);
}
#createOptionStatePtrn(hasStartClass: boolean, exceptStartPtrn: Pattern[]) {
// always store the startClassPattern in the final result pattern (no OPTIONAL, no NOT EXISTS, no SERVICE)
if (hasStartClass) this.#resultPtrns.push(...this.#startClassPtrn);
// create a SERVICE clause if needed
const sparqlService = this.#specProvider
.getProperty(
this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup?.getTypeSelected()
)
?.getServiceEndpoint();
let servicePtrn = null;
if (this.#grpWrapper.optionState === OptionTypes.SERVICE || sparqlService) {
const endpoint = factory.namedNode(sparqlService);
// to be on the safe side : when rollback an endclass group, we may come here with only the start class group criteria
// and nothing in this array
if (exceptStartPtrn.length > 0) {
servicePtrn = SparqlFactory.buildServicePattern(
exceptStartPtrn,
endpoint
);
}
}
let normalOrServicePatterns: Pattern[] = servicePtrn
? [servicePtrn]
: exceptStartPtrn;
// produce the generated patterns, maybe wrapped in OPTIONAL or NOT EXISTS
let finalResultPtrns: Pattern[] = [];
if (
this.#grpWrapper.optionState === OptionTypes.OPTIONAL &&
!this.#isInOption
) {
finalResultPtrns.push(
SparqlFactory.buildOptionalPattern(normalOrServicePatterns)
);
} else if (
this.#grpWrapper.optionState === OptionTypes.NOTEXISTS &&
!this.#isInOption
) {
finalResultPtrns.push(
SparqlFactory.buildNotExistsPattern(
SparqlFactory.buildGroupPattern(normalOrServicePatterns)
)
);
} else {
// nothing special, just retain the patterns in the final result pattern
finalResultPtrns.push(...normalOrServicePatterns);
}
// then decide where to store the generated patterns : either in "normal" patterns
// or in patterns that shall be executed after the rest of the query
if (
servicePtrn &&
this.#specProvider
.getProperty(
this.#grpWrapper.CriteriaGroup.ObjectPropertyGroup?.getTypeSelected()
)
?.isLogicallyExecutedAfter()
) {
this.#executedAfterPtrns.push(...finalResultPtrns);
} else {
this.#resultPtrns.push(...finalResultPtrns);
}
}
getResultPtrns() {
return this.#resultPtrns;
}
getDefaultVars() {
return this.#defaultVars;
}
getExecutedAfterPtrns() {
return this.#executedAfterPtrns;
}
}