Skip to content

innotrade/enapso-sparql-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

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.

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).

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

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).

	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

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

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

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.

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

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

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

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

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

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.

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

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.

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

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.

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

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.

 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

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);

About

Enapso Platform Independent SPARQL Tools

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published