UNPKG

sparnatural

Version:

Visual client-side SPARQL query builder and knowledge graph exploration tool

390 lines 19.2 kB
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