sparnatural
Version:
Visual client-side SPARQL query builder and knowledge graph exploration tool
255 lines • 11.7 kB
JavaScript
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 _SparqlTreeDataProvider_instances, _SparqlTreeDataProvider_getParser;
import { sameTerm } from "../../SparnaturalQueryIfc";
export class BaseSparqlListDataProvider {
constructor(sparqlHandler) {
this.sparqlHandler = sparqlHandler;
}
init(lang, defaultLang, typePredicate) {
this.lang = lang;
this.defaultLang = defaultLang;
this.typePredicate = typePredicate;
}
doExecuteWithCallback(sparqlQuery, callback, errorCallback) {
// 2. execute it
this.sparqlHandler.executeSparql(sparqlQuery, (data) => {
// 3. parse the results
let result = new Array;
for (let index = 0; index < data.results.bindings.length; index++) {
const solution = data.results.bindings[index];
// this is to avoid corner-cases with GraphDB queries returning only count=0 in aggregation queries.
// we need at least 2 bindings anyway
if (Object.keys(solution).length > 1) {
if (solution.uri) {
// if we find a "uri" column...
// read uri key & label key
result[result.length] = { term: solution.uri, label: solution.label.value, group: solution.group?.value, itemLabel: solution.itemLabel?.value };
}
else if (solution.value) {
// if we find a "value" column...
// read value key & label key
result[result.length] = { term: solution.value, label: solution.label.value, group: solution.group?.value, itemLabel: solution.itemLabel?.value };
}
else {
// try to determine the payload column by taking the column other than label
let columnName = this.getRdfTermColumn(solution);
if (columnName) {
result[result.length] = { term: solution[columnName], label: solution.label.value, group: solution.group?.value, itemLabel: solution.itemLabel?.value };
}
else {
throw Error("Could not determine which column to read from the result set");
}
}
}
}
// 4. call the callback
callback(result);
}, errorCallback);
}
getRdfTermColumn(aBindingSet) {
let foundKey = undefined;
for (const key of Object.keys(aBindingSet)) {
if (key != "label") {
if (!foundKey) {
foundKey = key;
}
else {
// it means there are more than one column, don't know which one to take, break
return undefined;
}
}
}
return foundKey;
}
}
/**
* Implementation of ListDataProviderIfc that executes a SPARQL query against an endpoint,
* and read the 'uri' and 'label' columns.
*/
export class SparqlListDataProvider extends BaseSparqlListDataProvider {
constructor(sparqlHandler, queryBuilder) {
super(sparqlHandler);
this.queryBuilder = queryBuilder;
}
getListContent(domainType, predicate, rangeType, callback, errorCallback) {
// 1. create the SPARQL
let sparql = this.queryBuilder.buildSparqlQuery(domainType, predicate, rangeType, this.lang, this.defaultLang, this.typePredicate);
// 2. execute it
super.doExecuteWithCallback(sparql, callback, errorCallback);
}
}
export class SparqlValuesListDataProvider extends BaseSparqlListDataProvider {
constructor(sparqlHandler, queryBuilder) {
super(sparqlHandler);
this.queryBuilder = queryBuilder;
}
getListContent(values, callback, errorCallback) {
// 1. create the SPARQL
let sparql = this.queryBuilder.buildSparqlQuery(values, this.lang, this.defaultLang, this.typePredicate);
// 2. execute it
super.doExecuteWithCallback(sparql, callback, errorCallback);
}
}
/**
* Implementation of AutocompleteDataProviderIfc that executes a SPARQL query against an endpoint,
* and read the 'uri' and 'label' columns.
*/
export class SparqlAutocompleDataProvider {
constructor(sparqlHandler, queryBuilder) {
this.queryBuilder = queryBuilder;
this.sparqlHandler = sparqlHandler;
}
init(lang, defaultLang, typePredicate) {
this.lang = lang;
this.defaultLang = defaultLang;
this.typePredicate = typePredicate;
}
getAutocompleteSuggestions(domain, predicate, range, key, callback, errorCallback) {
if (key.startsWith("http") && SparqlAutocompleDataProvider.isValidUrl(key, ["http", "https"])) {
// valid URI given, return it directly
let result = new Array;
result.push({
term: {
type: "uri",
value: key
},
label: key
});
callback(result);
return;
}
// 1. create the SPARQL
let sparql = this.queryBuilder.buildSparqlQuery(domain, predicate, range, key, this.lang, this.defaultLang, this.typePredicate);
// 2. execute it
this.sparqlHandler.executeSparql(sparql, (data) => {
// 3. parse the results
let result = new Array;
for (let index = 0; index < data.results.bindings.length; index++) {
const solution = data.results.bindings[index];
if (solution.uri) {
// read uri key & label key
result[result.length] = { term: solution.uri, label: solution.label.value, group: solution.group?.value };
}
else if (solution.value) {
result[result.length] = { term: solution.value, label: solution.label.value, group: solution.group?.value };
}
else {
// try to determine the payload column by taking the column other than label
let columnName = this.getRdfTermColumn(solution);
if (columnName) {
result[result.length] = { term: solution[columnName], label: solution.label.value };
}
else {
throw Error("Could not determine which column to read from the result set");
}
}
}
// 4. call the callback
callback(result);
}, errorCallback);
}
getRdfTermColumn(aBindingSet) {
let foundKey = undefined;
for (const key of Object.keys(aBindingSet)) {
if (key != "label") {
if (!foundKey) {
foundKey = key;
}
else {
// it means there are more than one column, don't know which one to take, break
return undefined;
}
return foundKey;
}
}
}
static isValidUrl(s, protocols) {
try {
let url = new URL(s);
return protocols
? url.protocol
? protocols.map(x => `${x.toLowerCase()}:`).includes(url.protocol)
: false
: true;
}
catch (err) {
return false;
}
}
;
}
export class SparqlTreeDataProvider {
constructor(sparqlHandler, queryBuilder) {
_SparqlTreeDataProvider_instances.add(this);
this.queryBuilder = queryBuilder;
this.sparqlHandler = sparqlHandler;
}
init(lang, defaultLang, typePredicate) {
this.lang = lang;
this.defaultLang = defaultLang;
this.typePredicate = typePredicate;
}
getRoots(domainType, predicate, rangeType, callback, errorCallback) {
// 1. create the SPARQL
let sparql = this.queryBuilder.buildRootsSparqlQuery(domainType, predicate, rangeType, this.lang, this.defaultLang, this.typePredicate);
// 2. execute it
this.sparqlHandler.executeSparql(sparql, __classPrivateFieldGet(this, _SparqlTreeDataProvider_instances, "m", _SparqlTreeDataProvider_getParser).call(this, callback), errorCallback);
}
getChildren(node, domainType, predicate, rangeType, callback, errorCallback) {
// 1. create the SPARQL
let sparql = this.queryBuilder.buildChildrenSparqlQuery(node, domainType, predicate, rangeType, this.lang, this.defaultLang, this.typePredicate);
// 2. execute it
this.sparqlHandler.executeSparql(sparql, __classPrivateFieldGet(this, _SparqlTreeDataProvider_instances, "m", _SparqlTreeDataProvider_getParser).call(this, callback), errorCallback);
}
}
_SparqlTreeDataProvider_instances = new WeakSet(), _SparqlTreeDataProvider_getParser = function _SparqlTreeDataProvider_getParser(callback) {
return (data) => {
// 3. parse the results
let result = new Array;
for (let index = 0; index < data.results.bindings.length; index++) {
const solution = data.results.bindings[index];
result[result.length] = {
term: solution.uri,
label: solution.label.value,
itemLabel: solution.itemLabel?.value,
// make sure to parse the value as a boolean so that it is not a string
// we also test on "1" because Virtuoso returns this as a result instead of a true boolean
hasChildren: solution.hasChildren ? ((solution.hasChildren.value === "true" || solution.hasChildren.value == 1) ? true : false) : true,
disabled: solution.count ? solution.count.value == 0 : false
};
}
// 4. call the callback
callback(result);
};
};
/**
* @param items Merges the datasource items based on their equality, in the case that multiple groups
* (= multiple datasets) return the same RDF term (= the same URI or literal value). In that case a single result is kept,
* with a group that is the concatenation of the groups of the merged items.
* @returns a new list of datasource items in which the items have been merge based on their rdfTerm equality.
*/
export function mergeDatasourceResults(items) {
let result = new Array();
// iterate on each item
items.forEach(item => {
// if it wasn't already added...
if (!result.some(itemInResult => sameTerm(itemInResult.term, item.term))) {
// find all items with the same URI
let sameTerms = items.filter(i => sameTerm(item.term, i.term));
// add the first identical item of this in our result table, with a merged group
let newTerm = {
term: sameTerms[0].term,
label: sameTerms[0].label,
itemLabel: sameTerms[0].itemLabel,
group: sameTerms.map(i => i.group).join(" + ")
};
result.push(newTerm);
}
});
return result;
}
//# sourceMappingURL=SparqlDataProviders.js.map