UNPKG

sparnatural

Version:

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

301 lines (260 loc) 9.77 kB
import { DataFactory } from 'rdf-data-factory'; import rdfParser from "rdf-parse"; var Readable = require('stream').Readable import Datasources from "../ontologies/SparnaturalConfigDatasources"; import { RdfStore } from 'rdf-stores'; import { NamedNode, Quad, Stream, Term } from "@rdfjs/types"; import { StoreModel } from './StoreModel'; const factory = new DataFactory(); const RDF_NAMESPACE = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; export const RDF = { LANG_STRING: factory.namedNode(RDF_NAMESPACE + "langString") as NamedNode, TYPE: factory.namedNode(RDF_NAMESPACE + "type") as NamedNode, FIRST: factory.namedNode(RDF_NAMESPACE + "first") as NamedNode, REST: factory.namedNode(RDF_NAMESPACE + "rest") as NamedNode, NIL: factory.namedNode(RDF_NAMESPACE + "nil") as NamedNode, }; const RDFS_NAMESPACE = "http://www.w3.org/2000/01/rdf-schema#"; export const RDFS = { CLASS: factory.namedNode(RDFS_NAMESPACE + "Class") as NamedNode, LABEL: factory.namedNode(RDFS_NAMESPACE + "label") as NamedNode, COMMENT: factory.namedNode(RDFS_NAMESPACE + "comment") as NamedNode, DOMAIN: factory.namedNode(RDFS_NAMESPACE + "domain") as NamedNode, RANGE: factory.namedNode(RDFS_NAMESPACE + "range") as NamedNode, RESOURCE: factory.namedNode(RDFS_NAMESPACE + "Resource") as NamedNode, SUBPROPERTY_OF: factory.namedNode(RDFS_NAMESPACE + "subPropertyOf") as NamedNode, SUBCLASS_OF: factory.namedNode(RDFS_NAMESPACE + "subClassOf") as NamedNode, }; export class BaseRDFReader { protected lang: string; protected store: RdfStore; protected graph: StoreModel; constructor(n3store: RdfStore, lang: string) { this.store = n3store; this.lang = lang; this.graph = new StoreModel(n3store); } static buildStoreFromString(configData:string, filePath:string, callback: any) { const store:RdfStore = RdfStore.createDefault(); console.log("Building store from string..."); let quadStream: Stream<Quad> = BaseRDFReader.#toQuadStream(configData,filePath); store.import(quadStream) .on('error', () => console.log("Problem parsing inline config")) .once('end', () => callback(store)); } static buildStore(files: Array<string>, callback: any) { // Create a new store with default settings // see https://www.npmjs.com/package/rdf-stores const store:RdfStore = RdfStore.createDefault(); let promises = new Array<Promise<RdfStore>>(); for(let config of files) { console.log("Importing in store '" + config + "'"); let p:Promise<RdfStore> = new Promise((resolve, reject) => $.ajax({ method: "GET", url: config, dataType: "text", }).done(function (configData) { let quadStream: Stream<Quad> = BaseRDFReader.#toQuadStream(configData,config); store.import(quadStream) .on('error', reject) .once('end', () => resolve(store)); }).fail(function (response) { console.error( "Sparnatural - unable to load RDF config file : " + config ); console.log(response); reject(); }) ); // end Promise promises.push(p); } // when all done, call callback Promise.all(promises).then((values) => { console.log( "Specification store populated with " + store.countQuads( null, null, null, null ) + " triples." ); callback(store); }) } static #toQuadStream(string:any, filePath:any) { // turn input string into a stream var textStream = new Readable(); textStream.push(string) // the string you want textStream.push(null) // indicates end-of-file basically - the end of the stream var quadStream; try { // attempt to parse based on path console.log("Attempt to parse determining format from path " + filePath+"..."); quadStream = rdfParser.parse(textStream, { path: filePath }); } catch (exception) { try { console.log("Attempt to parse in Turtle..."); // attempt to parse in turtle quadStream = rdfParser.parse(textStream, { contentType: "text/turtle", }); } catch (exception) { console.log("Attempt to parse in RDF/XML..."); // attempt to parse in RDF/XML quadStream = rdfParser.parse(textStream, { contentType: "application/rdf+xml", }); } } return quadStream; } _readDatasourceAnnotationProperty( propertyOrClassId: any, datasourceAnnotationProperty: any ) { // read predicate datasource const datasourceQuads = this.store.getQuads( factory.namedNode(propertyOrClassId), factory.namedNode(datasourceAnnotationProperty), null, null ); if (datasourceQuads.length == 0) { return null; } for (const datasourceQuad of datasourceQuads) { const datasourceUri = datasourceQuad.object.value; var knownDatasource = Datasources.DATASOURCES_CONFIG.get(datasourceUri); if (knownDatasource != null) { return knownDatasource; } else { return this.#_buildDatasource(datasourceUri); } } // IMPORTANT should here be propper error handling? return {}; } /** * { * queryString: "...", * queryTemplate: "...", * labelPath: "...", * labelProperty: "...", * childrenPath: "...", * childrenProperty: "...", * noSort: true * } **/ #_buildDatasource(datasourceUri: string) { var datasource: { queryString?: string; queryTemplate?: any; labelPath?: any; labelProperty?: any; childrenPath?: any; childrenProperty?: any; sparqlEndpointUrl?: any; noSort?: any; } = {}; // read datasource characteristics // Alternative 1 : read optional queryString var queryStrings = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.QUERY_STRING) ).map(n=>n.value); if (queryStrings.length > 0) { datasource.queryString = queryStrings[0]; } // Alternative 2 : query template + label path var queryTemplates = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.QUERY_TEMPLATE) ).map(n=>n.value); if (queryTemplates.length > 0) { var theQueryTemplate = queryTemplates[0]; var knownQueryTemplate = Datasources.QUERY_STRINGS_BY_QUERY_TEMPLATE.get(theQueryTemplate); if (knownQueryTemplate != null) { // 2.1 It is known in default Sparnatural ontology datasource.queryTemplate = knownQueryTemplate; } else { // 2.2 Unknown, read the query string on the query template var queryStrings = this.graph.readProperty( factory.namedNode(theQueryTemplate), factory.namedNode(Datasources.QUERY_STRING) ).map(n=>n.value); if (queryStrings.length > 0) { var queryString = queryStrings[0]; datasource.queryTemplate = queryString.startsWith('"') && queryString.endsWith('"') ? queryString.substring(1, queryString.length - 1) : queryString; } } // labelPath var labelPaths = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.LABEL_PATH) ).map(n=>n.value); if (labelPaths.length > 0) { datasource.labelPath = labelPaths[0]; } // labelProperty var labelProperties = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.LABEL_PROPERTY) ).map(n=>n.value); if (labelProperties.length > 0) { datasource.labelProperty = labelProperties[0]; } // childrenPath var childrenPaths = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.CHILDREN_PATH) ).map(n=>n.value); if (childrenPaths.length > 0) { datasource.childrenPath = childrenPaths[0]; } // childrenProperty var childrenProperties = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.CHILDREN_PROPERTY) ).map(n=>n.value); if (childrenProperties.length > 0) { datasource.childrenProperty = childrenProperties[0]; } } // read optional sparqlEndpointUrl var sparqlEndpointUrls = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.SPARQL_ENDPOINT_URL) ).map(n=>n.value); if (sparqlEndpointUrls.length > 0) { datasource.sparqlEndpointUrl = sparqlEndpointUrls[0]; } // read optional noSort var noSorts = this.graph.readProperty( factory.namedNode(datasourceUri), factory.namedNode(Datasources.NO_SORT) ).map(n=>n.value); if (noSorts.length > 0) { datasource.noSort = noSorts[0] === "true"; } return datasource; } /** * Reads rdf:type(s) of an entity, and return them as an array **/ _readRdfTypes(uri: Term):Term[] { return this.graph.readProperty(uri, RDF.TYPE); } _pushIfNotExist(item: any, items: any[]) { if (items.indexOf(item) < 0) { items.push(item); } return items; } }