Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- configs/oslo-kg: use smaller datasets for queries.
- React-admin version updated to 5.7.2 (#207).
- Tool renamed to 'Miravi - a linked data viewer'; default favicon.ico and miravi.png provided (#210).
- Bumped Comunica version from 3.2.3 to 4.2.0, resulting in increased execution speed of some queries (#212).
Breaking change for typed literals in query configuration, "variables" field; see README for new syntax.

### Fixed

Expand Down
12 changes: 9 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,15 @@ The configuration file must follow the structure shown below.
"queryLocation": "Path to the location, relative to 'queryFolder', of the (auxiliary) query that yields the sources from above RDF resource"
},
"variables": {
"variableExampleString": ["\"String1\"", "\"String2\""],
"variableExampleUri": ["<https://example.com/uri1>", "<https://example.com/uri2>"],
"variableExampleInteger": ["1", "2"]
"variableExampleStrings": ["\"String1\"", "\"String2\""],
"variableExampleUris": ["<https://example.com/uri1>", "<https://example.com/uri2>"],
"variableExampleLangStrings": ["\"String1\"@en", "\"Chaîne2\"@fr"],
"variableExampleTypedLiterals": [
"\"1\"^^<http://www.w3.org/2001/XMLSchema#integer>",
"\"1.3\"^^<http://www.w3.org/2001/XMLSchema#decimal>",
"\"1.5E6\"^^<http://www.w3.org/2001/XMLSchema#double>",
"\"true\"^^<http://www.w3.org/2001/XMLSchema#boolean>"
]
},
"indirectVariables": {
"queryLocations": [
Expand Down
11,475 changes: 9,290 additions & 2,185 deletions main/package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion main/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"dev-with-path": "vite --base /random/path/"
},
"dependencies": {
"@comunica/query-sparql": "^3.2.3",
"@comunica/query-sparql": "^4.2.0",
"@inrupt/solid-client": "^1.30.0",
"@inrupt/solid-client-authn-browser": "^1.17.1",
"@inrupt/vocab-common-rdf": "^1.0.5",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,10 @@ const TemplatedListResultTable = (props) => {
if (query.variables || query.indirectVariables) {
try {
// LOG console.log('start waiting for variable options');
// LOG const t1 = Date.now();
setVariableOptions(await dataProvider.getVariableOptions(query));
// LOG console.log('done waiting for variable options');
// LOG const t2 = Date.now();
// LOG console.log(`done waiting for variable options after ${t2-t1} ms`);
setWaitingForVariableOptions(false);
} catch (error) {
// LOG console.log(`error getting variable options: ${error.message}`);
Expand Down
63 changes: 30 additions & 33 deletions main/src/dataProvider/SparqlDataProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export default {
results = listCache.results;
} else {
if (query.comunicaContext?.sources?.length) {
// LOG console.log(`query.queryText: ${ query.queryText }`);
results = await executeQuery(query);
listCache.hash = hash;
listCache.results = results;
Expand Down Expand Up @@ -336,6 +337,32 @@ function handleComunicaContextCreation(query) {

async function getVariableOptions(query) {

function termToSparqlCompatibleString(term) {
switch (term.termType) {
case 'NamedNode':
return `<${term.value}>`;

case 'Literal':
const escaped = term.value.replace(/"/g, '\\"');
if (term.language) {
return `"${escaped}"@${term.language}`;
}
if (term.datatype && term.datatype.value !== 'http://www.w3.org/2001/XMLSchema#string') {
return `"${escaped}"^^<${term.datatype.value}>`;
}
return `"${escaped}"`;

case 'BlankNode':
return `_:${term.value}`;

case 'Variable':
return `?${term.value}`;

default:
throw new Error(`Unknown RDF Term type: ${term.termType}`);
}
}

// LOG console.log(`--- getVariableOptions #${++getVariableOptionsCounter}`);

// BEGIN duplicated chunk of code (duplicated in order for templated queries with indirect queries having sources from a source index to work correctly)
Expand Down Expand Up @@ -389,44 +416,14 @@ async function getVariableOptions(query) {
await new Promise((resolve, reject) => {
bindingsStream.on('data', (bindings) => {
// see https://comunica.dev/docs/query/advanced/bindings/
// LOG console.log(`bindings: ${bindings.toString()}`);
for (const [variable, term] of bindings) {
const name = variable.value;
if (!variableOptions[name]) {
variableOptions[name] = [];
}
let variableValue;
switch (term.termType) {
case "Literal":
// escape double quotes
// example: This is a string containing some \"double quotes\"
variableValue = `${term.value.replace(/"/g, '\\"')}`;
// test whether there is a type specifier ^^...
// this code is hacky, because it depends on undocumented term.id - cover it with sufficient test cases!
if (/\"\^\^/.test(term.id)) {
// do not surround with double quotes
// example: 1
} else {
// surround with double quotes
// example: "This is a string containing some \"double quotes\""
variableValue = `"${variableValue}"`;
}
// test whether there is a language tag @...
// this code is hacky, because it depends on undocumented term.id - cover it with sufficient test cases!
const lt = /\@(.*)$/.exec(term.id);
if (lt) {
// append language tag
// example: "This is a string in English"@en
variableValue = `${variableValue}@${lt[1]}`;
}
break;
case "NamedNode":
// surround with triangle brackets
// example: <https://www.example.com/data/o1>
variableValue = `<${term.value}>`;
break;
default:
break;
}
const variableValue = termToSparqlCompatibleString(term);
// LOG console.log(`variableValue: ${variableValue}`);
if (variableValue && !variableOptions[name].includes(variableValue)) {
variableOptions[name].push(variableValue);
}
Expand Down
16 changes: 10 additions & 6 deletions test/cypress/e2e/indirect-variables-different-types.cy.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
describe("Indirect variable query", () => {

function escapeRegex(str) {
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
}

it("Indirect with 1 variable (different types)", () => {

const testdata = [
Expand All @@ -16,23 +20,23 @@ describe("Indirect variable query", () => {
out: 'This is a string in English'
},
{
in: 'true',
in: '"true"^^<http://www.w3.org/2001/XMLSchema#boolean>',
out: 'true'
},
{
in: '1',
in: '"1"^^<http://www.w3.org/2001/XMLSchema#integer>',
out: '1'
},
{
in: '1.3',
in: '"1.3"^^<http://www.w3.org/2001/XMLSchema#decimal>',
out: '1.3'
},
{
in: '1.5E6',
in: '"1.5E6"^^<http://www.w3.org/2001/XMLSchema#double>',
out: '1.5E6'
},
{
in: '"This is a string containing some \\\\"double quotes\\\\""',
in: '"This is a string containing some \\"double quotes\\""',
out: 'This is a string containing some "double quotes"'
}
];
Expand All @@ -45,7 +49,7 @@ describe("Indirect variable query", () => {
// Fill in the form
cy.get('.ra-input-object').click();
// RegExp: to have an exact match - see https://stackoverflow.com/questions/56443963/click-an-exact-match-text-in-cypress
cy.get('li').contains(new RegExp("^" + t.in + "$", "g")).click();
cy.get('li').contains(new RegExp(`^${escapeRegex(t.in)}$`)).click();

// Comfirm query
cy.get('button').contains('Query').click();
Expand Down