UNPKG

@innotrade/enapso-sparql-tools

Version:
570 lines (491 loc) 23.1 kB
# enapso-sparql-tools Generic SPARQL query generator for Node.js This package provides of the following features: - Class Manager, ontology class cache to use OWL classes as a schema - Prefix Manager, manages the prefixes for the named prefix notation of ontology IRIs - Generator, SPARQL generator for class, property and annotation operations as well as CRUD-Operations for individuals The concept is to: - specify classes in a W3C-compliant ontology modelling tool - import the model into a W3C-compliant semantic graph-database (e.g. Ontotext GraphDB) - read the classes of the model into a Class Cache in a node.js application - let the node.js application automatically generate the SPARQL queries for CRUD operations for the individuals of the specified classes (done by this library) - Perform the generated SPARQL queries against the semantic graph-database to manage the individuals (supported by the enapso-graphdb-client package) This package is SPARQL 1.1 compliant. It is designed to run on all SPARQL 1.1 compliant databases. **The following demos use the Enapso GraphDB Client. Therefore, these examples require a running GraphDB 8.x/9.x instance on localhost at port 7200. The demos as well as the automated tests require a fully working Ontotext GraphDB repository "Test" and a user "Test" with the password "Test" being set up, which has read/write access to the "Test" Repository.** To run the demo.js first import the foundation Ontology (as available in demo folder) in Test repository and to run the test cases import the EnapsoOntologyRepository (as available in test folder) in Test repository Get the latest version of GraphDB for free at https://www.ontotext.com/products/graphdb/. **This project is actively developed and maintained.** To discuss questions and suggestions with the Enapso and GraphDB community, we'll be happy to meet you in our forum at https://www.innotrade.com/forum/. # Installation ``` npm i @innotrade/enapso-sparql-tools --save ``` # GraphDB Configuration and ClassCache Creation There we do configuration to create an connection with GraphDB using enapso graphdb client which needed for building class cache and running the demo. In following code we have also different method are used for building classcache like for getting all classes we use getAllClasses method, getClassProperties for getting object and data properties of a class. ```javascript const { EnapsoGraphDBClient } = require('@innotrade/enapso-graphdb-client'), { EnapsoLogger } = require('@innotrade/enapso-logger'); global.enlogger = new EnapsoLogger(); const EnapsoSPARQLTools = '@innotrade/enapso-sparql-tools'; const GRAPHDB_BASE_URL = 'http://localhost:7200', GRAPHDB_REPOSITORY = 'Test'; const NS_AUTH = 'http://ont.enapso.com/repo#', PREFIX_AUTH = 'enrepo'; // the default prefixes for all SPARQL queries const AUTH_PREFIXES = [ EnapsoGraphDBClient.PREFIX_OWL, EnapsoGraphDBClient.PREFIX_RDF, EnapsoGraphDBClient.PREFIX_RDFS, EnapsoGraphDBClient.PREFIX_XSD, { prefix: PREFIX_AUTH, iri: NS_AUTH } ]; const AUTH = { graphDBEndpoint: null, authentication: null, defaultBaseIRI: NS_AUTH, defaultPrefix: PREFIX_AUTH, defaultIRISeparator: '#', query: async function (sparql) { let query = await this.graphDBEndpoint.query(sparql); let resp; if (query.success) { resp = await this.graphDBEndpoint.transformBindingsToResultSet( query, { dropPrefixes: true } ); } else { let lMsg = query.message; if (400 === query.statusCode) { lMsg += ', check your query for potential errors'; } else if (403 === query.statusCode) { lMsg += ', check if user "' + GRAPHDB_USERNAME + '" has appropriate access rights to the Repository ' + '"' + this.graphDBEndpoint.getRepository() + '"'; } resp = { total: 0, success: false, message: lMsg }; } return resp; }, update: async function (sparql, params) { let resp = await this.graphDBEndpoint.update(sparql, params); if (!resp.success) { let lMsg = resp.message; if (400 === resp.statusCode) { lMsg += ', check your query for potential errors'; } else if (403 === resp.statusCode) { lMsg += ', check if user "' + GRAPHDB_USERNAME + '" has appropriate access rights to the Repository ' + '"' + this.graphDBEndpoint.getRepository() + '"'; } } return resp; }, // retrieve all classes from the graph getAllClasses: async function () { let generated = this.enSPARQL.getAllClasses(); // enlogger.log('SPARQL:\n' + generated.sparql); return this.query(generated.sparql); }, // retrieve all properties from a given class getClassProperties: async function (cls) { let generated = this.enSPARQL.getClassProperties(cls); // enlogger.log('SPARQL:\n' + generated.sparql); return this.query(generated.sparql); }, // generates an in-memory class from a SPARQL result set generateClassFromClassProperties: function (ns, name, classProps) { let cls = new EnapsoSPARQLTools.Class(ns, name); for (let propRec of classProps.records) { // todo: here we need to add the restrictions, domain, range, min, max, exactly etc. let prop = new EnapsoSPARQLTools.Property( ns, propRec.prop, propRec.type, propRec.range, propRec.domain ); // add the property to the classs cls.addProperty(prop); } return cls; }, // builds the class cache for all or selected classes buildClassCache: async function () { let classCache = new EnapsoSPARQLTools.ClassCache(); // get all classes of the database let classes = await this.getAllClasses(); // iterate through all returned classes for (let clsRec of classes.records) { let className = clsRec.class; // get the properties of the given class res = await this.getClassProperties(className); // generate an in-memory class of the retrieved properties let cls = this.generateClassFromClassProperties( NS_AUTH, className, res ); // add the class to the cache classCache.addClass(cls); } return classCache; }, demo: async function () { // instantiate a prefix manager enlogger.setLevel(EnapsoLogger.ALL); this.enPrefixManager = new EnapsoSPARQLTools.PrefixManager( AUTH_PREFIXES ); // in case no prefix is given for a certain resource identifier use the EDO: here this.enPrefixManager.setDefaultPrefix(PREFIX_AUTH); // create a SPARQL generator using the prefix manager this.enSPARQL = new EnapsoSPARQLTools.Generator({ prefixManager: this.enPrefixManager }); // instantiate a GraphDB connector and connect to GraphDB this.graphDBEndpoint = new EnapsoGraphDBClient.Endpoint({ baseURL: GRAPHDB_BASE_URL, repository: GRAPHDB_REPOSITORY, prefixes: this.enPrefixManager.getPrefixesForConnector() }); classCache = await this.buildClassCache(); } }; (async () => { await AUTH.demo(); })(); ``` ## Class Cache One essential component of the Enapso SPARQL Toolbox is its class cache. If you plan to run CRUD commands for individuals based on the property definitions for OWL classes, the SPARQL query requires to know about the fields of the class. In order to not need to load them with each and every query of the individuals, the Enapso SPARQL toolbox comes with an In-Memory-Class-Cache. On start of an application all classes on an ontology are loaded into the cache. For the sake of performance, to read one or all individuals of a certain class, the SPARQL generator routines utilize the cache. ## Create An Individual With the method createIndividualByClass(args)a new individual of a given class is created in the ontology. The following four parameters `cls`: class definition from the In-Memory Class-Cache as a reference for the individual to be created. `ind`: an object providing the values for the individual of the selected class (example see below). `baseiri(optional)`: an variable in which we define the baseiri which use along the name of individual if baseiri pass else it will check If ind contains an field iri, this IRI will be used as explicit IRI for the new individual. If ind does not contain an iri field, the IRI will be auto generated as UUID V4 by the method. options: `prefixSubjects`: true|false, whether to use prefixed names (default) or full IRI for subjects (here the individual to be updated). `prefixPredicates`: true|false, whether to use prefixed names (default) for predicates or full IRI for the predicates (here the properties). `prefixObjects`: true|false, whether to use prefixed names (default) for objects or full IRI for the related individuals (here the object properties). ```javascript createIndividualByClass: async function (args) { let generated = this.enSPARQL.createIndividualByClass(args); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql, { iri: generated.iri }); } ``` Initializing variables using that variable call the `createIndividualByClass` method ```javascript let Tenant = classCache.getClassByIRI(NS_AUTH + 'Tenant'); // insert a new individual based on the in-memory class res = await this.createIndividualByClass({ cls: Tenant, baseiri: baseiri, ind: { name: 'Test Company' } }); // save the iri for this individual iri = res.params.iri; out = JSON.stringify(res, null, 2); console.log('Creating individual by class:' + out); ``` ## Update An Individual of a Class The method updateIndividualByClass(cls, iri, ind) updates an individual in the ontology based on its class and iri. The method requires the following inbound parameters: `cls`: class definition from the In-Memory Class-Cache as a reference for the individual to be created. `iri`: the IRI of the individual to be updated. `ind`: an object providing the values for the individual of the selected class (example see below). ```javascript updateIndividualByClass: async function (cls, iri, ind) { let generated = this.enSPARQL.updateIndividualByClass(cls, iri, ind); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `updateIndividualByClass` method ```javascript let Tenant = classCache.getClassByIRI(NS_AUTH + 'Tenant'); let iri = 'http://ont.enapso.com/repo#Tenant_e7e124a2_3a7b_4333_8f51_5f70d48f0bfe'; // Update an exisitng individual based on the in-memory class res = await this.updateIndividualByClass(Tenant, baseiri, { name: 'Test' }); out = JSON.stringify(res, null, 2); console.log('Update an individual by class:' + out); ``` ## Read one or more Individuals of a Class The method getIndividualsByClass(args) returns all instances of a certain class. In order to run this method, the class cache needs to be properly initialized, since the properties of the class are loaded from that cache. The following arguments are supported: `cls`:class definition from the In-Memory Class-Cache as a reference for the individual to be created. `join`:An object in which we pass nested objects to get the whole tree structure of the class using there relation and classes name there we have two types of relation childtoMasterRelation and mastertochildRelation it is optional `filter`: filter used to get a specific individual of a class ```javascript getIndividualsByClass: async function (args) { let generated = this.enSPARQL.getIndividualsByClass(args); enlogger.log("SPARQL:\n" + generated.sparql); return this.query(generated.sparql); } ``` Initializing variables using that variable call the `getIndividualsByClass` method ```javascript this.Tenant = this.classCache.getClassByIRI(NS_AUTH + 'Tenant'); this.Environment = this.classCache.getClassByIRI(NS_AUTH + 'Environment'); this.Host = this.classCache.getClassByIRI(NS_AUTH + 'Host'); this.DatabaseInstance = this.classCache.getClassByIRI( NS_AUTH + 'DatabaseInstance' ); this.Repository = this.classCache.getClassByIRI(NS_AUTH + 'Repository'); this.Graph = this.classCache.getClassByIRI(NS_AUTH + 'Graph'); let joins = [ // first join (for tenants) on level 1 { cls: this.Environment, child2MasterRelation: 'hasTenant', joins: [ { cls: this.Host, child2MasterRelation: 'hasEnvironment', joins: [ { cls: this.DatabaseInstance, child2MasterRelation: 'hasHost', joins: [ { cls: this.Repository, child2MasterRelation: 'hasDatabaseInstance', joins: [ { cls: this.Graph, child2MasterRelation: 'hasRepository' } ] } ] } ] } ] } ]; let filter = [ { key: '$sparql', value: 'regEx(?name, "Ebner", "i")' } ]; // Read individual based on the in-memory class res = await this.getIndividualsByClass({ cls: this.Tenant, joins: joins, filter: filter }); out = JSON.stringify(res, null, 2); console.log('Read individuals by class:' + out); ``` ## Delete an Individual The deleteIndividual(args) deletes an arbitrary individual in the given addressed by its IRI and also its entire subtree structure but that option is optional.The following arguments are supported: `iri`:IRI of an individual of a class `join`:An object in which we pass nested objects to delete the whole tree structure of the class using there relation and classes name there we have two types of relation childtoMasterRelation and mastertochildRelation it is optional. ```javascript deleteIndividual: async function (args) { let generated = this.enSPARQL.deleteResource(args); enlogger.log("SPARQL:\n" + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `deleteIndividual` method ```javascript let iri = 'enrepo:Tenant_0143e7ee_fbdd_45b3_879f_fedc78e42ab4'; let joins = [ // first join (for tenants) on level 1 { cls: 'Environment', child2MasterRelation: 'hasTenant', joins: [ { cls: 'Host', child2MasterRelation: 'hasEnvironment', joins: [ { cls: 'DatabaseInstance', child2MasterRelation: 'hasHost', joins: [ { cls: 'Repository', child2MasterRelation: 'hasDatabaseInstance', joins: [ { cls: 'Graph', child2MasterRelation: 'hasRepository' } ] } ] } ] } ] } ]; res = await this.deleteIndividual({ iri: iri, joins: joins }); out = JSON.stringify(res, null, 2); console.log('Delete individuals by iri:' + out); ``` ## Create Relation between two Individuals Create Relation method create the relation between two indivdual of different classes we pass following parameter. `master`: the iri of master class individual `Relation`: the object property which relation want to create `child`: the iri of child class individual ```javascript createRelation: async function (master, property, child) { let generated = this.enSPARQL.createRelation(master, property, child); //console.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `createRelation` method ```javascript let master = 'http://ont.enapso.com/repo#Tenant_e7e124a2_3a7b_4333_8f51_5f70d48f0bfe'; let relation = 'hasTenant'; let child = 'http://ont.enapso.com/repo#Environment_833a44cc_ec58_4202_b44d_27460ae94e2d'; res = await this.createRelation(master, relation, child); out = JSON.stringify(res, null, 2); console.log('Create Relation between two individuals:' + out); ``` ## Delete Relation between two Individuals Delete Relation method to delete the relation between two indivdual of different classes we pass following parameter. `master`: the iri of master class individual `Relation`: the object property which relation want to create `child`: the iri of child class individual ```javascript deleteRelation: async function (master, property, child) { let generated = this.enSPARQL.deleteRelation(master, property, child); //console.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `createRelation` method ```javascript let master = 'http://ont.enapso.com/repo#Tenant_e7e124a2_3a7b_4333_8f51_5f70d48f0bfe'; let relation = 'hasTenant'; let child = 'http://ont.enapso.com/repo#Environment_833a44cc_ec58_4202_b44d_27460ae94e2d'; res = await this.deleteRelation(master, relation, child); out = JSON.stringify(res, null, 2); console.log('Delete Relation between two individuals:' + out); ``` ## Delete Property of an Class This method delete the given property of given class. We pass following parameter. `cls`: name of the class whoose property you wanna delete `property`: name of the property which you wanna delete. ```javascript deletePropertyOfClass(args) { let generated = this.enSPARQL.deleteGivenPropertyOfClass(args); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `deletePropertyOfClass` method ```javascript let cls = 'Environment'; let property = 'name'; res = await this.deletePropertyOfClass({ cls: cls, dataProperty: property }); out = JSON.stringify(res, null, 2); console.log('Delete Property of the class :' + out); ``` ## Delete label of each Class individuals This method to delete the labels of all individuals of given class of an specific language label. We pass following parameter. `cls`: name of the class whoose property you wanna delete `language`: name of language whoose label match with this language will be deleted. ```javascript deleteLabelOfEachClassIndividual(args) { let generated = this.enSPARQL.deleteLabelOfEachClassIndividual(args); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `deleteLabelOfEachClassIndividual` method ```javascript let cls = 'Environment'; let language = 'en'; res = await this.deleteLabelOfEachClassIndividual({ cls: cls, labelLanguage: language }); out = JSON.stringify(res, null, 2); console.log('Delete label of each individual of a class :' + out); ``` ## Copy label of each Class individuals to property This method to copy the label of specific language of each individual of a class and insert the label value as an given property of each individual of the given class We pass following parameter. `cls`: name of the class whoose label you want to copy and insert into given data property `language`: name of language whoose label match with this language will be copies. `property`: name of the property where you want to paste the label value. ```javascript copyLabelToDataPropertyOfEachIndividual(args) { let generated = this.enSPARQL.copyLabelToDataPropertyOfEachIndividual(args); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `copyLabelToDataPropertyOfEachIndividual` method ```javascript let cls = 'Environment'; let property = 'name'; let language = 'en'; res = await this.copyLabelToDataPropertyOfEachIndividual({ cls: cls, labelLanguage: language, dataProperty: property }); out = JSON.stringify(res, null, 2); console.log('Copy label of each individual of a class into Property :' + out); ``` ## Copy Property of each Class individuals into label This method to copy the property each individual of a class and insert the property value as an label of each individual of a class and assign a specific langauges.We pass following parameter. `cls`: name of the class whose property value you want to copy and insert into given label `language`: name of language which you want to assign while creating label. `property`: name of the property where you want to copy the property value. ```javascript copyDataPropertyToLabelOfEachIndividual(args) { let generated = this.enSPARQL.copyDataPropertyToLabelOfEachIndividual(args); //enlogger.log('SPARQL:\n' + generated.sparql); return this.update(generated.sparql); } ``` Initializing variables using that variable call the `copyDataPropertyToLabelOfEachIndividual` method ```javascript let cls = 'Environment'; let property = 'name'; let language = 'en'; res = await this.copyDataPropertyToLabelOfEachIndividual({ cls: cls, labelLanguage: language, dataProperty: property }); out = JSON.stringify(res, null, 2); console.log('Delete label of each individual of a class :' + out); ```