sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
267 lines (266 loc) • 13 kB
JavaScript
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
if (kind === "m") throw new TypeError("Private method is not writable");
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
};
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
};
var _OWLSpecificationProvider_parser, _OWLSpecificationProvider_generator;
import { DataFactory } from 'rdf-data-factory';
import { Config } from "../../ontologies/SparnaturalConfig";
import { Parser, Generator } from "sparqljs";
import { OWLSpecificationEntity } from "./OWLSpecificationEntity";
import { OWLSpecificationProperty } from "./OWLSpecificationProperty";
import { Dag } from '../../dag/Dag';
import { BaseRDFReader } from '../BaseRDFReader';
import { RDFS } from '../../../rdf/vocabularies/RDFS';
const factory = new DataFactory();
const OWL_NAMESPACE = "http://www.w3.org/2002/07/owl#";
export const OWL = {
THING: factory.namedNode(OWL_NAMESPACE + "Thing"),
EQUIVALENT_PROPERTY: factory.namedNode(OWL_NAMESPACE + "equivalentProperty"),
EQUIVALENT_CLASS: factory.namedNode(OWL_NAMESPACE + "equivalentClass"),
UNION_OF: factory.namedNode(OWL_NAMESPACE + "unionOf")
};
export class OWLSpecificationProvider extends BaseRDFReader {
constructor(n3store, lang) {
super(n3store, lang);
_OWLSpecificationProvider_parser.set(this, void 0);
_OWLSpecificationProvider_generator.set(this, void 0);
// init SPARQL parser and generator once
__classPrivateFieldSet(this, _OWLSpecificationProvider_parser, new Parser(), "f");
__classPrivateFieldSet(this, _OWLSpecificationProvider_generator, new Generator(), "f");
/*
var sparql = `
SELECT ?o2 WHERE {
{
OPTIONAL {?x ?p1 ?var_1 }
OPTIONAL {?x ?p2 ?var_2 }
BIND(COALESCE(?var_1, ?var_2) AS ?var)
}
?x ?p2 ?o2 .
}
`;
var query = this.#parser.parse(sparql);
console.dir(query)
*/
}
getLanguages() {
let languages = this.store
.getQuads(null, RDFS.LABEL, null, null)
.map((quad) => quad.object.language);
// deduplicate the list of languages
return [...new Set(languages)];
}
getEntity(entityUri) {
return new OWLSpecificationEntity(entityUri, this, this.store, this.lang);
}
getProperty(property) {
return new OWLSpecificationProperty(property, this, this.store, this.lang);
}
getAllSparnaturalEntities() {
var classes = this.getEntitiesInDomainOfAnyProperty();
// copy initial array
// create a set to avoid duplicates
var result = new Set(classes.slice());
// now look for all classes we can reach from this class list
for (const aClass of classes) {
var connectedClasses = this.getEntity(aClass).getConnectedEntities();
for (const aConnectedClass of connectedClasses) {
result.add(aConnectedClass);
}
}
return Array.from(result);
}
getEntitiesInDomainOfAnyProperty() {
const quadsArray = this.store.getQuads(null, RDFS.DOMAIN, null, null);
const items = new Set();
for (const quad of quadsArray) {
// we are not looking at domains of _any_ property
// the property we are looking at must be a Sparnatural property, with a known type
var objectPropertyId = quad.subject.value;
var typeClass = quad.object.termType;
var classId = quad.object.value;
var classAsRDFTerm = quad.object;
// empty string means we are searching without specifying a range
if (this.getProperty(objectPropertyId).getPropertyType("")) {
// keep only Sparnatural classes in the list
if (typeClass == "BlankNode" || this.isSparnaturalClass(classId)) {
// always exclude "remote classes" that should not have a type that from first list
if (this.getEntity(classId).hasTypeCriteria()) {
if (!this._isUnionClass(classAsRDFTerm)) {
items.add(classId);
}
else {
// read union content - /!\ this returns RDFTerm
var classesInUnion = this.graph.readAsList(classAsRDFTerm, OWL.UNION_OF);
for (const aUnionClass of classesInUnion) {
items.add(aUnionClass.value);
}
}
}
}
}
}
return this._sort(Array.from(items));
}
getEntitiesTreeInDomainOfAnyProperty() {
// 1. get the entities that are in a domain of a property
let entities = this.getEntitiesInDomainOfAnyProperty().map(s => this.getEntity(s));
// build dag from flat list
let dag = new Dag();
dag.initFlatTreeFromFlatList(entities);
return dag;
}
isSparnaturalClass(classUri) {
return (this.store.getQuads(factory.namedNode(classUri), RDFS.SUBCLASS_OF, factory.namedNode(Config.SPARNATURAL_CLASS), null).length > 0 ||
this.store.getQuads(factory.namedNode(classUri), RDFS.SUBCLASS_OF, factory.namedNode(Config.NOT_INSTANTIATED_CLASS), null).length > 0 ||
this.store.getQuads(factory.namedNode(classUri), RDFS.SUBCLASS_OF, factory.namedNode(Config.RDFS_LITERAL), null).length > 0);
}
expandSparql(sparql, prefixes) {
// for each owl:equivalentProperty ...
var equivalentPropertiesPerProperty = {};
this.store
.getQuads(null, OWL.EQUIVALENT_PROPERTY, null, null)
.forEach((quad) => {
// store it if multiple equivalences are declared
if (!equivalentPropertiesPerProperty[quad.subject.value]) {
equivalentPropertiesPerProperty[quad.subject.value] = [];
}
equivalentPropertiesPerProperty[quad.subject.value].push(quad.object.value);
});
// join the equivalences with a |
for (let [property, equivalents] of Object.entries(equivalentPropertiesPerProperty)) {
var re = new RegExp("<" + property + ">", "g");
sparql = sparql.replace(re, "<" + equivalents.join(">|<") + ">");
}
// for each owl:equivalentClass ...
var equivalentClassesPerClass = {};
this.store
.getQuads(null, OWL.EQUIVALENT_CLASS, null, null)
.forEach((quad) => {
// store it if multiple equivalences are declared
if (!equivalentClassesPerClass[quad.subject.value]) {
equivalentClassesPerClass[quad.subject.value] = [];
}
equivalentClassesPerClass[quad.subject.value].push(quad.object.value);
});
// use a VALUES if needed
var i = 0;
for (let [aClass, equivalents] of Object.entries(equivalentClassesPerClass)) {
var re = new RegExp("<" + aClass + ">", "g");
if (equivalents.length == 1) {
sparql = sparql.replace(re, "<" + equivalents[0] + ">");
}
else {
sparql = sparql.replace(re, "?class" +
i +
" . VALUES ?class" +
i +
" { <" +
equivalents.join("> <") +
"> } ");
}
i++;
}
// for each sparqlString
this.store
.getQuads(null, factory.namedNode(Config.SPARQL_STRING), null, null)
.forEach((quad) => {
// prepare the regex
let classUri = quad.subject.value;
let sparqlString = quad.object.value;
// first pass to work with classes sparqlString that can contain a long expression
// with variables and MINUSes, like
// <http://exemple.com/MyClass> MINUS { $this <http://exemple.com/myProp> ?x VALUES ?x { A B } }
// replace the $this with the name of the original variable in the query
// \S matches any non-whitespace character
var re = new RegExp("(\\S*) (rdf:type|a) <" + classUri + ">", "g");
// prepare the function that will return the string to replace
let replacer = function (match, p1, offset, fullString) {
// first substitutes any other variable name with a prefix
// so that we garantee unicity across the complete query
var reVariables = new RegExp("\\?(\\S*)", "g");
let whereClauseReplacedVariables = p1 + " rdf:type " + sparqlString.replace(reVariables, "?$1_" + p1.substring(1));
// then, replace the match on the original URI with the whereClause of the target
// replacing "$this" with the original variable name
var reThis = new RegExp("\\$this", "g");
let whereClauseReplacedThis = whereClauseReplacedVariables.replace(reThis, p1);
return whereClauseReplacedThis;
};
sparql = sparql.replace(re, replacer);
// then a second pass simpler one that will work for properties
var re = new RegExp("<" + quad.subject.value + ">", "g");
sparql = sparql.replace(re, quad.object.value);
});
// reparse the query, apply prefixes, and reserialize the query
var query = __classPrivateFieldGet(this, _OWLSpecificationProvider_parser, "f").parse(sparql);
for (var key in prefixes) {
query.prefixes[key] = prefixes[key];
}
let finalString = __classPrivateFieldGet(this, _OWLSpecificationProvider_generator, "f").stringify(query);
return finalString;
}
_sort(items) {
var me = this;
const compareFunction = function (item1, item2) {
// return me.getLabel(item1).localeCompare(me.getLabel(item2));
var entity1 = me.getEntity(item1);
var entity2 = me.getEntity(item2);
var order1 = entity1.getOrder();
var order2 = entity2.getOrder();
if (order1) {
if (order2) {
if (order1 == order2) {
return entity1.getLabel().localeCompare(entity2.getLabel());
}
else {
// if the order is actually a number, convert it to number and use a number conversion
if (!isNaN(Number(order1)) && !isNaN(Number(order2))) {
return Number(order1) - Number(order2);
}
else {
return (order1 > order2) ? 1 : -1;
}
}
}
else {
return -1;
}
}
else {
if (order2) {
return 1;
}
else {
return entity1.getLabel().localeCompare(entity2.getLabel());
}
}
};
// sort according to order or label
items.sort(compareFunction);
return items;
}
/**
* Reads config:order of an entity and returns it, or null if not set
**/
_readOrder(uri) {
return this.graph.readSingleProperty(uri, factory.namedNode(Config.ORDER))?.value;
}
/*** Handling of UNION classes ***/
_readUnionContent(classUri) {
var lists = this.graph.readProperty(factory.namedNode(classUri), OWL.UNION_OF);
if (lists.length > 0) {
return this.graph.readListContent(lists[0]);
}
}
_isUnionClass(classUriOrBNodeIdentifier) {
return this.graph.hasProperty(classUriOrBNodeIdentifier, OWL.UNION_OF);
}
}
_OWLSpecificationProvider_parser = new WeakMap(), _OWLSpecificationProvider_generator = new WeakMap();
//# sourceMappingURL=OWLSpecificationProvider.js.map