UNPKG

sparnatural

Version:

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

302 lines (272 loc) 10.5 kB
import { ISparJson, Order, VariableExpression, VariableTerm, } from "../../json/ISparJson"; import { Pattern, VariableTerm as SparqlVariableTerm } from "sparqljs"; import { VariableExpression as SparqlVariableExpression } from "sparqljs"; import ISparnaturalSpecification from "../../../spec-providers/ISparnaturalSpecification"; import { Grouping, Ordering, SelectQuery, Variable } from "sparqljs"; import WhereBuilder from "../WhereBuilder"; import SparqlFactory from "../SparqlFactory"; import { DataFactory } from "rdf-data-factory"; import QueryWhereTranslator from "./QueryWhereTranslator"; import ISpecificationProperty from "../../../spec-providers/ISpecificationProperty"; const factory = new DataFactory(); /** * Generates a Sparql query in the SparqlJs structure from the JSON query format of Sparnatural */ export default class JsonSparqlTranslator { typePredicate: string; specProvider: ISparnaturalSpecification; prefixes: { [key: string]: string } = {}; jsonQuery: ISparJson; settings: any; defaultLabelVars: Variable[] = []; constructor( // the Sparnatural configuration specProvider: ISparnaturalSpecification, // settings settings: any ) { this.specProvider = specProvider; this.settings = settings; } /** * @param jsonQuery the Sparnatural JSON query * @returns a SPARQL query translated from the Sparnatural JSON query structure */ generateQuery(jsonQuery: ISparJson): SelectQuery { this.jsonQuery = jsonQuery; const sparqlJsQuery: SelectQuery = { queryType: "SELECT", distinct: jsonQuery.distinct, variables: this.#varsToRDFJS(jsonQuery.variables), type: "query", where: this.#createWhereClause(), prefixes: this.prefixes, order: this.#orderToRDFJS(this.jsonQuery.order, jsonQuery.variables[0]), // sets a limit if provided, otherwise leave to undefined limit: jsonQuery.limit && jsonQuery.limit > 0 ? jsonQuery.limit : undefined, }; // if the RdfJsQuery contains empty 'where' array, then the generator crashes. // create query with no triples if (sparqlJsQuery.where?.length === 0) { sparqlJsQuery.where = [ { type: "bgp", triples: [], }, ]; } // post processing for defaultlabel property if (this.defaultLabelVars.length > 0) { this.defaultLabelVars.forEach((defaultLabelVar) => this.#insertDefaultLabelVar(sparqlJsQuery, defaultLabelVar) ); } // don't set an order if there is no expression for it if (!sparqlJsQuery?.order || !sparqlJsQuery?.order[0]?.expression) delete sparqlJsQuery.order; // set a GROUP BY based on aggregation expression in the variables // add this after defaultLabel var have been inserted, and re-read them from the query sparqlJsQuery.group = this.#addGroupBy( sparqlJsQuery.variables as Variable[] ); return sparqlJsQuery; } /** * @param variables The list variables of the SELECT query * @returns GROUP BY clause if needed, of all non-aggregated variables, or undefined if not needed */ #addGroupBy(variables: Variable[]): Grouping[] { if (this.#needsGrouping(variables)) { let g: Grouping[] = []; variables.forEach((v) => { if (!(v as SparqlVariableExpression).expression) { g.push({ expression: v as SparqlVariableTerm, }); } }); return g; } else { // no aggregation, or only one column, grouping is undefined return undefined; } } #needsGrouping(variables: Variable[]): boolean { return variables.find((v) => (v as VariableExpression).expression) && variables.length > 1 ? true : false; } /** * Generates the WHERE clause of the SparqlJs query from the original JSON structure * @returns an array of SparqlJs Pattern representing the complete content of the WHERE clause */ #createWhereClause(): Pattern[] { let whereBuilder = new QueryWhereTranslator( this.jsonQuery, this.specProvider, this.settings ); whereBuilder.build(); this.defaultLabelVars = whereBuilder.getDefaultVars(); return whereBuilder.getResultPtrns(); /* const builder = new WhereBuilder( this.sparnatural.BgWrapper.componentsList.rootGroupWrapper, this.specProvider, this.typePredicate, false, false ) builder.build() this.defaultLabelVars = builder.getDefaultVars() if(builder.getExecutedAfterPtrns().length > 0) { // put all normal patterns in a subquery to garantee they are logically executed before // and their variables are bound in the rest of the query outside the subquery let subquery = SparqlFactory.buildSubQuery(builder.getResultPtrns()); // and the patterns to be executed after in the normal where clause return [subquery, ...builder.getExecutedAfterPtrns()]; } else { return builder.getResultPtrns() } */ } //--------------------------------------------------------------------- // old version of the method, to be removed after testing the new one /** * Translates the variables from the JSON structure to variables in the SparqlJs structure * @param variables The variables in the original JSON query structure * @returns An array of Variable structure in the SparqlJs structure */ #varsToRDFJS1( variables: Array<VariableTerm | VariableExpression> ): Variable[] { return variables.map((v) => { // if it is an aggregated variable... if ((v as VariableExpression).expression) { let vExpression: VariableExpression = v as VariableExpression; return SparqlFactory.buildAggregateFunctionExpression( vExpression.expression.aggregation, factory.variable(vExpression.expression.expression.value), factory.variable(vExpression.variable.value) ); } else { return factory.variable((v as VariableTerm).value); } }); } //-------------------------------------------------------------------- /** * Translates the variables from the JSON structure to variables in the SparqlJs structure * @param variables The variables in the original JSON query structure * @returns An array of Variable structure in the SparqlJs structure */ #varsToRDFJS( variables: Array<VariableTerm | VariableExpression> ): Variable[] { let variablesArray: Variable[][] = variables.map((v) => { if ((v as VariableExpression).expression) { let vExpression: VariableExpression = v as VariableExpression; return [ SparqlFactory.buildAggregateFunctionExpression( vExpression.expression.aggregation, factory.variable(vExpression.expression.expression.value), factory.variable(vExpression.variable.value) ), ]; } else { let specProperty: ISpecificationProperty | undefined = undefined; // Parcourir les branches pour trouver la correspondance uniquement avec l'objet (o) (this.jsonQuery.branches as any[]).forEach((branch) => { if (branch.line.o === (v as VariableTerm).value) { //console.log("Matching branch found for object:", branch); specProperty = this.specProvider.getProperty(branch.line.p); } }); //console.log("specProperty", specProperty); if (!specProperty) { // Si aucune propriété n'est trouvée, retourne une variable simple console.log("Variable", (v as VariableTerm).value); return [factory.variable((v as VariableTerm).value)]; } if ( specProperty.getBeginDateProperty() && specProperty.getEndDateProperty() ) { let result: Variable[] = []; if (specProperty.getBeginDateProperty()) { result.push(factory.variable((v as VariableTerm).value + "_begin")); } if (specProperty.getEndDateProperty()) { result.push(factory.variable((v as VariableTerm).value + "_end")); } if (specProperty.getExactDateProperty()) { result.push(factory.variable((v as VariableTerm).value + "_exact")); } return result; } else { return [factory.variable((v as VariableTerm).value)]; } } }); // Aplatissement du tableau de tableaux en un tableau simple let finalResult: Variable[] = []; variablesArray.forEach((varArray: Variable[]) => { finalResult.push(...varArray); }); return finalResult; } /** * Converts the order parameter in the original JSON structure to an Ordering object in SparqlJs * @param order the order information in the JSON structure * @param variable the variable to order on * @returns an Ordering structure in SparqlJs */ #orderToRDFJS( order: Order, variable: VariableTerm | VariableExpression ): Ordering[] { if (order == Order.DESC || order == Order.ASC) { let varName = (variable as VariableExpression).expression ? (variable as VariableExpression).variable.value : (variable as VariableTerm).value; return [ { expression: factory.variable(varName), descending: order == Order.DESC ? true : false, }, ]; } else { // no order return null; } } /** * Inserts the provided default label variable, having the name "xxx_label", after the variable named "xxx" * @param sparqlQuery The SparqlJs query * @param defaultLabelVar The default label variable, ending in xxx_label, to insert */ #insertDefaultLabelVar(sparqlQuery: SelectQuery, defaultLabelVar: Variable) { // reconstruct the original var name by removing "_label" suffix var originalVar = (defaultLabelVar as VariableTerm).value.substring( 0, (defaultLabelVar as VariableTerm).value.length - "_label".length ); //console.log("SpraqlQuery variables", sparqlQuery.variables); for (var i = 0; i < sparqlQuery.variables.length; i++) { // find variable with the original name if ((sparqlQuery.variables[i] as VariableTerm).value == originalVar) { // insert the default label var after this one sparqlQuery.variables.splice(i + 1, 0, defaultLabelVar); // don't forget, otherwise infinite loop break; } } } }