sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
390 lines • 19.2 kB
JavaScript
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _RdfTermValueBuilder_instances, _RdfTermValueBuilder_rdfTermToSparqlQuery, _DateTimePickerValueBuilder_instances, _DateTimePickerValueBuilder_formatSparqlDate, _DateTimePickerValueBuilder_pad, _DateTimePickerValueBuilder_padYear, _MapValueBuilder_instances, _MapValueBuilder_buildPolygon;
import SparqlFactory from "./SparqlFactory";
import { DataFactory } from "rdf-data-factory";
import { Config } from "../../ontologies/SparnaturalConfig";
import { SHACLSpecificationEntity } from "../../spec-providers/shacl/SHACLSpecificationEntity";
import { GEOFUNCTIONS, GEOSPARQL } from "rdf-shacl-commons";
const factory = new DataFactory();
/**
* A factory for creating ValueBuilders from the widgetType. This is the association between the widget type
* and the corresponding ValueBuilder
*/
export class ValueBuilderFactory {
buildValueBuilder(widgetType) {
switch (widgetType) {
case Config.LITERAL_LIST_PROPERTY:
case Config.LIST_PROPERTY:
case Config.TREE_PROPERTY:
case Config.AUTOCOMPLETE_PROPERTY:
return new RdfTermValueBuilder();
case Config.VIRTUOSO_SEARCH_PROPERTY:
case Config.GRAPHDB_SEARCH_PROPERTY:
case Config.STRING_EQUALS_PROPERTY:
case Config.SEARCH_PROPERTY:
return new SearchRegexValueBuilder();
case Config.NON_SELECTABLE_PROPERTY:
return new NonSelectableValueBuilder();
case Config.BOOLEAN_PROPERTY:
return new BooleanValueBuilder();
case Config.MAP_PROPERTY:
return new MapValueBuilder();
case Config.NUMBER_PROPERTY:
return new NumberValueBuilder();
case Config.TIME_PROPERTY_YEAR:
case Config.TIME_PROPERTY_DATE:
return new DateTimePickerValueBuilder();
case Config.TIME_PROPERTY_PERIOD:
console.warn(Config.TIME_PROPERTY_PERIOD + " is not implement yet");
break;
default:
throw new Error(`WidgetType ${widgetType} not recognized`);
}
}
}
export class BaseValueBuilder {
init(specProvider, startClassVal, propertyVal, endClassVal, endClassVarSelected, values) {
this.specProvider = specProvider;
this.startClassVal = startClassVal;
this.propertyVal = propertyVal;
this.endClassVal = endClassVal;
this.endClassVarSelected = endClassVarSelected;
this.values = values;
}
isBlockingStart() {
return false;
}
isBlockingEnd() {
return false;
}
isBlockingObjectProp() {
return false;
}
}
/**
* A ValueBuilder that can work from an RdfTermValue and tests the equality either
* by inserting the sole unique value as the object of the triple or by using a VALUES clause
*/
export class RdfTermValueBuilder extends BaseValueBuilder {
constructor() {
super(...arguments);
_RdfTermValueBuilder_instances.add(this);
}
build() {
let widgetValues = this.values;
if (this.isBlockingObjectProp()) {
let singleTriple = SparqlFactory.buildTriple(factory.variable(this.startClassVal.variable), factory.namedNode(this.propertyVal.type), __classPrivateFieldGet(this, _RdfTermValueBuilder_instances, "m", _RdfTermValueBuilder_rdfTermToSparqlQuery).call(this, widgetValues[0].rdfTerm));
let ptrn = {
type: "bgp",
triples: [singleTriple],
};
return [ptrn];
}
else {
let vals = widgetValues.map((v) => {
let vl = {};
vl["?" + this.endClassVal.variable] = __classPrivateFieldGet(this, _RdfTermValueBuilder_instances, "m", _RdfTermValueBuilder_rdfTermToSparqlQuery).call(this, v.rdfTerm);
return vl;
});
let valuePattern = {
type: "values",
values: vals,
};
return [valuePattern];
}
}
/**
* @returns true if there is at least one value, because in that case the rdf:type criteria is redundant
*/
isBlockingEnd() {
return this.values?.length > 0;
}
/**
* @returns true if there is a single value and the end class is not selected (in which case we need the variable
* to put it in the SELECT clause), and the target entity is not associated to a SPARQL query, in which case the variable
* must not disappear from the query
*/
isBlockingObjectProp() {
return (this.values?.length == 1
&&
!this.endClassVarSelected
&&
!(this.specProvider.getEntity(this.endClassVal.type) instanceof SHACLSpecificationEntity
&&
this.specProvider.getEntity(this.endClassVal.type).hasShTarget()));
}
}
_RdfTermValueBuilder_instances = new WeakSet(), _RdfTermValueBuilder_rdfTermToSparqlQuery = function _RdfTermValueBuilder_rdfTermToSparqlQuery(rdfTerm) {
if (rdfTerm.type == "uri") {
return factory.namedNode(rdfTerm.value);
}
else if (rdfTerm.type == "literal") {
if (rdfTerm["xml:lang"]) {
return factory.literal(rdfTerm.value, rdfTerm["xml:lang"]);
}
else if (rdfTerm.datatype) {
// if the second parameter is a NamedNode, then it is considered a datatype, otherwise it is
// considered like a language
// so we make the datatype a NamedNode
let namedNodeDatatype = factory.namedNode(rdfTerm.datatype);
return factory.literal(rdfTerm.value, namedNodeDatatype);
}
else {
return factory.literal(rdfTerm.value);
}
}
else if (rdfTerm.type == "bnode") {
// we don't know what to do with this, but don't trigger an error
return factory.blankNode(rdfTerm.value);
}
else {
throw new Error("Unexpected rdfTerm type " + rdfTerm.type);
}
};
export class BooleanValueBuilder extends BaseValueBuilder {
build() {
let widgetValues = this.values;
let isLiteral = this.specProvider.getEntity(this.endClassVal.type).isLiteralEntity();
if (!isLiteral) {
// not a literal, we turn the criteria into FILTER EXISTS or FILTER NOT EXISTS
let ptrn = {
type: "bgp",
triples: [
{
subject: factory.variable(this.startClassVal.variable),
predicate: factory.namedNode(this.propertyVal.type),
object: factory.variable(this.endClassVal.variable),
},
],
};
let groupPattern = SparqlFactory.buildGroupPattern([ptrn]);
let filterPtrn = widgetValues[0].boolean
? SparqlFactory.buildExistsPattern(groupPattern)
: SparqlFactory.buildNotExistsPattern(groupPattern);
return [filterPtrn];
}
else {
// if we are blocking the object prop, we create it directly here with the value as the object
if (this.isBlockingObjectProp()) {
let ptrn = {
type: "bgp",
triples: [
{
subject: factory.variable(this.startClassVal.variable),
predicate: factory.namedNode(this.propertyVal.type),
object: factory.literal(widgetValues[0].boolean.toString(), factory.namedNode("http://www.w3.org/2001/XMLSchema#boolean")),
},
],
};
return [ptrn];
}
else {
// otherwise the object prop is created and we create a VALUES clause with the actual boolean
let vals = this.values.map((v) => {
let vl = {};
vl["?" + this.endClassVal.variable] = factory.literal(widgetValues[0].boolean.toString(), factory.namedNode("http://www.w3.org/2001/XMLSchema#boolean"));
return vl;
});
let valuePattern = {
type: "values",
values: vals,
};
return [valuePattern];
}
}
}
/**
* @returns true if a value is selected but the variable is not selected
*/
isBlockingObjectProp() {
return (this.values?.length == 1
&&
!this.endClassVarSelected);
}
/**
* @returns true if the range is not a literal, indicating we will generate a FILTER NOT EXISTS
*/
isBlockingEnd() {
let isLiteral = this.specProvider.getEntity(this.endClassVal.type).isLiteralEntity();
return !isLiteral;
}
}
export class NumberValueBuilder extends BaseValueBuilder {
build() {
let widgetValues = this.values;
return [
SparqlFactory.buildFilterRangeDateOrNumber(widgetValues[0].min != undefined
? factory.literal(widgetValues[0].min.toString(), factory.namedNode("http://www.w3.org/2001/XMLSchema#decimal"))
: null, widgetValues[0].max != undefined
? factory.literal(widgetValues[0].max.toString(), factory.namedNode("http://www.w3.org/2001/XMLSchema#decimal"))
: null, factory.variable(this.endClassVal.variable)),
];
}
}
export class NonSelectableValueBuilder extends BaseValueBuilder {
build() {
return [];
}
}
export class SearchRegexValueBuilder extends BaseValueBuilder {
build() {
let widgetType = this.specProvider
.getProperty(this.propertyVal.type)
.getPropertyType(this.endClassVal.type);
let widgetValues = this.values;
switch (widgetType) {
case Config.STRING_EQUALS_PROPERTY: {
// builds a FILTER(lcase(...) = lcase(...))
return [SparqlFactory.buildFilterOr(widgetValues.map((v) => {
return SparqlFactory.buildOperationLcaseEquals(factory.literal(`${v.search}`), factory.variable(this.endClassVal.variable));
}))];
}
case Config.SEARCH_PROPERTY: {
// builds a FILTER(regex(...,...,"i"))
return [SparqlFactory.buildFilterOr(widgetValues.map((v) => {
return SparqlFactory.buildRegexOperation(factory.literal(`${v.search}`), factory.variable(this.endClassVal.variable));
}))];
}
case Config.GRAPHDB_SEARCH_PROPERTY: {
// builds a GraphDB-specific search pattern
let ptrn = {
type: "bgp",
triples: [
{
subject: factory.variable(this.startClassVal.variable),
predicate: factory.namedNode("http://www.ontotext.com/connectors/lucene#query"),
object: factory.literal(`text:${widgetValues[0].search}`),
},
{
subject: factory.variable(this.startClassVal.variable),
predicate: factory.namedNode("http://www.ontotext.com/connectors/lucene#entities"),
object: factory.variable(this.endClassVal.variable),
},
],
};
return [ptrn];
}
case Config.VIRTUOSO_SEARCH_PROPERTY: {
let bif_query = widgetValues[0].search
.replace(/[\"']/g, " ")
.split(" ")
.map((e) => `'${e}'`)
.join(" and ");
let ptrn = {
type: "bgp",
triples: [
{
subject: factory.variable(this.endClassVal.variable),
predicate: factory.namedNode("http://www.openlinksw.com/schemas/bif#contains"),
object: factory.literal(`${bif_query}`),
},
],
};
return [ptrn];
}
case Config.JENA_SEARCH_PROPERTY: {
throw new Error("Not implemented yet");
}
}
}
}
export class DateTimePickerValueBuilder extends BaseValueBuilder {
constructor() {
super(...arguments);
_DateTimePickerValueBuilder_instances.add(this);
}
build() {
let widgetValues = this.values;
let specProperty = this.specProvider.getProperty(this.propertyVal.type);
let beginDateProp = specProperty.getBeginDateProperty();
let endDateProp = specProperty.getEndDateProperty();
if (beginDateProp && endDateProp) {
// special config with a begin and end date
let exactDateProp = specProperty.getExactDateProperty();
// we have some values, generate the filters
return [
SparqlFactory.buildDateRangeOrExactDatePattern(widgetValues[0].start ? factory.literal(__classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_formatSparqlDate).call(this, widgetValues[0].start), factory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime")) : null, widgetValues[0].stop ? factory.literal(__classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_formatSparqlDate).call(this, widgetValues[0].stop), factory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime")) : null, factory.variable(this.startClassVal.variable), factory.namedNode(beginDateProp), factory.namedNode(endDateProp), exactDateProp != null ? factory.namedNode(exactDateProp) : null, factory.variable(this.endClassVal.variable))
];
}
else {
// normal case, standard config
return [
SparqlFactory.buildFilterRangeDateOrNumber(widgetValues[0].start ? factory.literal(__classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_formatSparqlDate).call(this, widgetValues[0].start), factory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime")) : null, widgetValues[0].stop ? factory.literal(__classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_formatSparqlDate).call(this, widgetValues[0].stop), factory.namedNode("http://www.w3.org/2001/XMLSchema#dateTime")) : null, factory.variable(this.endClassVal.variable))
];
}
}
/**
* We are blocking the generation of the predicate between start and end class
* if the property is configured with a begin and end date (because the triples will then be generated by this class)
* @returns true if the property has been configured with a begin and an end date property
*/
isBlockingObjectProp() {
let beginDateProp = this.specProvider.getProperty(this.propertyVal.type).getBeginDateProperty();
let endDateProp = this.specProvider.getProperty(this.propertyVal.type).getEndDateProperty();
return (this.values?.length == 1
&&
beginDateProp != null
&&
endDateProp != null);
}
}
_DateTimePickerValueBuilder_instances = new WeakSet(), _DateTimePickerValueBuilder_formatSparqlDate = function _DateTimePickerValueBuilder_formatSparqlDate(dateString) {
if (dateString == null)
return null;
let date = new Date(dateString);
return __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_padYear).call(this, date.getUTCFullYear()) +
'-' + __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_pad).call(this, date.getUTCMonth() + 1) +
'-' + __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_pad).call(this, date.getUTCDate()) +
'T' + __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_pad).call(this, date.getUTCHours()) +
':' + __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_pad).call(this, date.getUTCMinutes()) +
':' + __classPrivateFieldGet(this, _DateTimePickerValueBuilder_instances, "m", _DateTimePickerValueBuilder_pad).call(this, date.getUTCSeconds()) +
'Z';
}, _DateTimePickerValueBuilder_pad = function _DateTimePickerValueBuilder_pad(number) {
if (number < 10) {
return '0' + number;
}
return number;
}, _DateTimePickerValueBuilder_padYear = function _DateTimePickerValueBuilder_padYear(number) {
let absoluteValue = (number < 0) ? -number : number;
let absoluteString = (absoluteValue < 1000) ? absoluteValue.toString().padStart(4, '0') : absoluteValue.toString();
let finalString = (number < 0) ? "-" + absoluteString : absoluteString;
return finalString;
};
export class MapValueBuilder extends BaseValueBuilder {
constructor() {
super(...arguments);
_MapValueBuilder_instances.add(this);
}
// reference: https://graphdb.ontotext.com/documentation/standard/geosparql-support.html
build() {
let widgetValues = this.values;
// the property between the subject and its position expressed as wkt value, e.g. http://www.w3.org/2003/01/geo/wgs84_pos#geometry
let filterPtrn = {
type: "filter",
expression: {
type: "functionCall",
function: GEOFUNCTIONS.WITHIN,
args: [
factory.variable(this.endClassVal.variable),
__classPrivateFieldGet(this, _MapValueBuilder_instances, "m", _MapValueBuilder_buildPolygon).call(this, widgetValues[0].coordinates[0]),
],
},
};
return [filterPtrn];
}
}
_MapValueBuilder_instances = new WeakSet(), _MapValueBuilder_buildPolygon = function _MapValueBuilder_buildPolygon(coordinates) {
let polygon = "";
coordinates.forEach((coordinat) => {
polygon = `${polygon}${coordinat.lng} ${coordinat.lat}, `;
});
// polygon must be closed with the starting point
let startPt = coordinates[0];
let literal = factory.literal(`Polygon((${polygon}${startPt.lng} ${startPt.lat}))`, GEOSPARQL.WKT_LITERAL);
return literal;
};
//# sourceMappingURL=ValueBuilder.js.map