UNPKG

sparnatural

Version:

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

420 lines 15.4 kB
import { Wildcard } from "sparqljs"; import { Parser as SparqlParser } from "sparqljs"; import { DataFactory } from 'rdf-data-factory'; const factory = new DataFactory(); export default class SparqlFactory { /** * @param aggregation The aggregation function to apply * @param aggregatedVar The original variable being aggregated * @param asVar The final variable holding the result of the aggregation function * @returns An aggregation expression, always using a DISTINCT */ static buildAggregateFunctionExpression(aggregation, aggregatedVar, asVar) { var aggregateExpression = { type: "aggregate", aggregation: aggregation, // always use a DISTINCT, so that we don't count duplicated results // e.g. same result in different named graphs distinct: true, expression: aggregatedVar }; // group_concat will always use ";" as separator if (aggregation === "group_concat") { aggregateExpression.separator = "; "; } return { expression: aggregateExpression, variable: asVar }; } static buildBgpPattern(triples) { if (triples.findIndex(t => (t == null)) > -1) { throw new Error("Trying to build a bgp pattern with null triple !"); } return { type: "bgp", triples: triples, }; } static buildGroupPattern(patterns) { return { type: "group", patterns: patterns }; } static buildUnionPattern(patterns) { return { type: "union", patterns: patterns }; } static buildServicePattern(patterns, serviceIRI) { return { type: 'service', name: serviceIRI, silent: false, patterns: patterns }; } static buildSubQuery(patterns) { return { type: "group", patterns: [ { type: "query", queryType: "SELECT", prefixes: {}, variables: [new Wildcard()], where: patterns } ] }; } static buildExistsPattern(groupPattern) { return { type: "filter", expression: { type: "operation", operator: "exists", args: [ groupPattern ], }, }; } static buildNotExistsPattern(groupPattern) { return { type: "filter", expression: { type: "operation", operator: "notexists", args: [ groupPattern ], }, }; } static buildOptionalPattern(patterns) { return { type: "optional", patterns: patterns, }; } static buildRegexOperation(texte, variable) { return { type: "operation", operator: "regex", args: [ { type: "operation", operator: "str", args: [variable] }, texte, factory.literal(`i`) ] }; } static buildFilterLangEquals(variable, lang) { return { type: "filter", expression: { type: "operation", operator: "=", args: [ { type: "operation", operator: "lang", args: [variable] }, lang ], }, }; } /** * @param firstVariable First variable in the COALESCE * @param secondvariable Second variable in the COALESCE * @param finalVariable Finale variable of the BIND clause * @returns BIND(COALESCE(?var1, ?var2) AS ?finalVar) */ static buildBindCoalescePattern(firstVariable, secondvariable, finalVariable) { return { type: "bind", expression: { type: "operation", operator: "coalesce", args: [ firstVariable, secondvariable ] }, variable: finalVariable }; } static buildFilterStrInOrEquals(values, variable) { if (values.length == 1) { return { type: "filter", expression: { type: "operation", operator: "=", args: [ { type: "operation", operator: "str", args: [variable] }, values[0] ], }, }; } else { return { type: "filter", expression: { type: "operation", operator: "in", args: [ { type: "operation", operator: "str", args: [variable] }, values ], }, }; } } /** * Wraps the given operations in a filter with an OR operator * @param operations a flat array or operations * @returns */ static buildFilterOr(operations) { return { type: "filter", expression: SparqlFactory.combineWithOr(operations) }; } /** * Combines multiple operations with an || operator, recursively * @param operations a flet array or operations * @returns a hierarchy of || operations, each having 2 args */ static combineWithOr(operations) { if (operations.length == 1) { return operations[0]; } else if (operations.length == 2) { return { type: "operation", operator: "||", args: operations }; } else { return { type: "operation", operator: "||", args: [ operations[0], SparqlFactory.combineWithOr(operations.slice(1)) ] }; } } /** * Builds an operation expression that compares the lowercase of a variable with the lowercase of a literal * @param texte * @param variable * @returns */ static buildOperationLcaseEquals(texte, variable) { return { type: "operation", operator: "=", args: [ { type: "operation", operator: "lcase", args: [variable] }, { type: "operation", operator: "lcase", args: [texte] } ] }; } static buildFilterRangeDateOrNumber(rangeBegin, rangeEnd, variable) { var filters = new Array; if (rangeBegin != null) { filters.push({ type: "operation", operator: ">=", args: [ variable, rangeBegin ] }); } if (rangeEnd != null) { filters.push({ type: "operation", operator: "<=", args: [ variable, rangeEnd ] }); } if (filters.length == 2) { return { type: "filter", expression: { type: 'operation', operator: "&&", args: filters } }; } else { return { type: "filter", expression: filters[0] }; } } static buildTriple(subject, predicate, object) { return { subject: subject, predicate: predicate, object: object, }; } static buildPropertyPathTriple(subject, predicate, object) { return { subject: subject, predicate: { type: 'path', pathType: '*', items: [predicate] }, object: object, }; } static buildTypeTriple(subject, predicate, object) { if (!subject?.value || !object?.value) return null; return SparqlFactory.buildTriple(subject, predicate, object); } // It is the intersection between the startclass and endclass chosen. // example: ?person dpedia:birthplace ?country static buildIntersectionTriple(subj, pred, obj) { if (!subj?.value || !pred || !obj?.value) return null; return { subject: subj, predicate: factory.namedNode(pred), object: obj, }; } static parsePropertyPath(path) { return this.sparqlParser.parse(path); } static buildDateRangePattern(startDate, endDate, startClassVar, beginDatePred, endDatePred, objectVariable) { // we have provided both begin and end date criteria if (startDate != null && endDate != null) { // 1. case where the resource has both start date and end date let firstAlternative = SparqlFactory.buildGroupPattern([]); let bgp = SparqlFactory.buildBgpPattern([]); let beginDateVarName = factory.variable(objectVariable.value + `_begin`); let endDateVarName = factory.variable(objectVariable.value + `_end`); bgp.triples.push(SparqlFactory.buildTriple(startClassVar, beginDatePred, beginDateVarName)); bgp.triples.push(SparqlFactory.buildTriple(startClassVar, endDatePred, endDateVarName)); firstAlternative.patterns.push(bgp); // begin date is before given end date firstAlternative.patterns.push(SparqlFactory.buildFilterRangeDateOrNumber(null, endDate, beginDateVarName)); // end date is after given start date firstAlternative.patterns.push(SparqlFactory.buildFilterRangeDateOrNumber(startDate, null, endDateVarName)); // 2. case where the resource has only a start date let secondAlternative = SparqlFactory.buildGroupPattern([]); let secondBgp = SparqlFactory.buildBgpPattern([]); secondBgp.triples.push(SparqlFactory.buildTriple(startClassVar, beginDatePred, beginDateVarName)); secondAlternative.patterns.push(secondBgp); let notExistsEndDate = SparqlFactory.buildNotExistsPattern(SparqlFactory.buildGroupPattern([ SparqlFactory.buildBgpPattern([ SparqlFactory.buildTriple(startClassVar, endDatePred, endDateVarName) ]) ])); secondAlternative.patterns.push(notExistsEndDate); // begin date is before given end date secondAlternative.patterns.push(SparqlFactory.buildFilterRangeDateOrNumber(null, endDate, beginDateVarName)); // 3. case where the resource has only a end date let thirdAlternative = SparqlFactory.buildGroupPattern([]); let thirdBgp = SparqlFactory.buildBgpPattern([]); thirdBgp.triples.push(SparqlFactory.buildTriple(startClassVar, endDatePred, endDateVarName)); thirdAlternative.patterns.push(thirdBgp); let notExistsBeginDate = SparqlFactory.buildNotExistsPattern(SparqlFactory.buildGroupPattern([ SparqlFactory.buildBgpPattern([ SparqlFactory.buildTriple(startClassVar, beginDatePred, beginDateVarName) ]) ])); thirdAlternative.patterns.push(notExistsBeginDate); // end date is after given start date thirdAlternative.patterns.push(SparqlFactory.buildFilterRangeDateOrNumber(startDate, null, endDateVarName)); return SparqlFactory.buildUnionPattern([firstAlternative, secondAlternative, thirdAlternative]); // we have provided only a start date } else if (startDate != null && endDate === null) { let endDateVarName = factory.variable(objectVariable.value + "_end"); var bgp = SparqlFactory.buildBgpPattern([ SparqlFactory.buildTriple(startClassVar, endDatePred, endDateVarName) ]); // end date is after given start date var filter = SparqlFactory.buildFilterRangeDateOrNumber(startDate, null, endDateVarName); // if the resource has no end date, and has only a start date // then it necessarily overlaps with the provided open-ended range // so let's avoid this case for the moment return SparqlFactory.buildGroupPattern([bgp, filter]); // we have provided only a end date } else if (startDate === null && endDate != null) { let beginDateVarName = factory.variable(objectVariable.value + "_begin"); var bgp = SparqlFactory.buildBgpPattern([ SparqlFactory.buildTriple(startClassVar, beginDatePred, beginDateVarName) ]); // begin date is before given end date var filter = SparqlFactory.buildFilterRangeDateOrNumber(null, endDate, beginDateVarName); return SparqlFactory.buildGroupPattern([bgp, filter]); } } ; static buildDateRangeOrExactDatePattern(startDate, endDate, startClassVar, beginDatePred, endDatePred, exactDatePred, objectVariable) { if (exactDatePred != null) { // first alternative of the union to test exact date let exactDateVarName = factory.variable(objectVariable.value + "_exact"); let firstAlternative = SparqlFactory.buildGroupPattern([ SparqlFactory.buildBgpPattern([ SparqlFactory.buildTriple(startClassVar, exactDatePred, exactDateVarName) ]), // exact date is within provided date range SparqlFactory.buildFilterRangeDateOrNumber(startDate, endDate, exactDateVarName) ]); // second alternative to test date range let secondAlternative = SparqlFactory.buildDateRangePattern(startDate, endDate, startClassVar, beginDatePred, endDatePred, objectVariable); // return as an array so that caller can have generic forEach loop to all // every element to outer query return SparqlFactory.buildUnionPattern([firstAlternative, secondAlternative]); } else { return SparqlFactory.buildDateRangePattern(startDate, endDate, startClassVar, beginDatePred, endDatePred, objectVariable); } } } SparqlFactory.sparqlParser = new SparqlParser({ pathOnly: true }); //# sourceMappingURL=SparqlFactory.js.map