1. How-To Guides
1.1. How to implement a custom Node Expressions
Note: before proceeding you may want to read Conceptual Guides § dsl
1.1.1. How to implement a specialized SPARQL node expression
implementing hydra member assertion query
1.1.2. How to implement a compound node expression
implementing a limit/offset/order shorthand
1.2. How to register a custom function
In this how-to we will add support for a palindrome
function as described in this tutorial. Assuming that you’ve already added support of such a function
to your SPARQL engine, you should be able to perform a query such as that shown in the mentioned tutorial:
PREFIX rdfs : <http://www.w3.org/2000/01/rdf-schema#> PREFIX cfn : <http://example.org/custom-function/> SELECT ?x ?label WHERE { ?x rdfs : label ?label . FILTER ( cfn : palindrome ( str ( ?label ))) }
In order for shape-to-query to recognize you custom function, the necessary metadata needs to be added to the SPARQL vocabulary.
This can be easily done programmatically, at the minimum by adding an rdf:type
assertion about the function itself.
import vocabulary, { rdf, sh} from '@hydrofoil/shape-to-query/vocabulary.js' vocabulary. namedNode( 'http://example.org/custom-function/palindrome' ) . addOut( rdf. type, sh. Function)
Alternatively, you may choose to load the additional metadata by parsing a turtle string or loading an RDF file, or fetching it the web. Use the following code to add all the parsed triples to the vocabulary dataset:
import type { DatasetCore} from '@rdfjs/types' import vocabularyfrom '@hydrofoil/shape-to-query/vocabulary.js' import addAllfrom 'rdf-dataset-ext/addAll.js' let customFunctions: DatasetCore = await loadAndParseFunctions() addAll( vocabulary. dataset, customFunctions)
NOTE: Read Conceptual Guides § function-metadata to learn more about the recognised properties of SHACL functions.
With function metadata in place, you can now use the custom function in any node expression:
PREFIX ex: <http://example.org/> PREFIX schema: <http://schema.org/> PREFIX sh: <http://www.w3.org/ns/shacl#> PREFIX cfn: <http://example.org/custom-function/> [ a sh : NodeShape ; sh : target schema : Thing ; sh : property [ sh : path ex : nameIsPalindrome ; sh : values [ cfn : palindrome ( [ sh : path schema : name ] ) ; ] ; ] ; ] .
PREFIX schema : <http://schema.org/> CONSTRUCT { ?resource1 <http://example.org/nameIsPalindrome> ?resource2 . } WHERE { ?resource1 schema : name ?resource3 . BIND ( <http://example.org/custom-function/palindrome> ( ?resource3 ) AS ?resource2 ) }
simplify the example when sh:expressions
is implemented to use the result of the palindrome function call as a constraint [Issue #130]
1.3. Optimising joins in Stardog
Deep and broad structures described with sh:node
tend to degrade join performance in some versions of Stardog which
fail to optimise the joins necessary to combine the results coming from nested base graph patterns.
This can be remedied by introducing group.joins
query hint to the combined properties of a sh:node
constraint. To do that, it will be necessary to provide a customized instance
of PropertyShape
class which builds the property constraints. The following example shows how to do that:
import { PropertyShape, constructQuery} from '@hydrofoil/shape-to-query' import rdffrom '@zazuko/env/web.js' import type { BuildParameters} from '@hydrofoil/shape-to-query/model/Shape.js' import type sparqljsfrom 'sparqljs' /* * WARNING: this uses patched sparqljs to add support for comments */ // subclass PropertyShape and override `buildConstraints` method class PropertyShapeExextends PropertyShape{ buildConstraints( arg: BuildParameters ) : sparqljs. Pattern[] { return [ { type : 'comment' , text: 'pragma group.joins' }, ... super . buildConstraints( arg), ] } } // replace the default implementation when creating the query const pointer= rdf. clownface() . blankNode() . addOut( rdf. ns. sh. property, p=> p. addOut( rdf. ns. sh. path, rdf. ns. schema. name)) const query= constructQuery( pointer, { PropertyShape: PropertyShapeEx , }) console. log( query)