While SPARQL is a greatly expressive language for all kinds of query and modeling tasks, many users sooner or later discover that they need a bit of extra functionality that does not come out of the box. The Jena implementation of the SPARQL language used by TopBraid provides two built-in extension mechanisms:
However, SPARQL does not define mechanisms on how to declare such functions in an platform-independent way. TopBraid and its underlying Jena API suggests various approaches for that purpose:
SHACL functions are a declarative mechanism for defining new SPARQL functions. No programming or special set-up required, you just need to be able to express your function as another SPARQL query. If you are familiar with JavaScript, you can also use SHACL-JS to define highly expressive functions of almost arbitrary complexity. Whenever these approaches are sufficient, you should use SHACL.
SHACL can be regarded as the next step in the evolution of our earlier technology called SPIN. While we have no plans to drop support for these SPIN features, you are still encouraged to use SHACL instead. SPIN and SPINx are mentioned here for completeness. SPIN provides a declarative mechanism for defining new SPARQL Functions. See Understanding SPIN Functions for details on how to define new functions by combining other functions and SPARQL queries. SPINx is a dynamic mechanism to declare SPARQL Functions using JavaScript. Essentially, JavaScript code is executed and supplied with arguments. Then any JavaScript computation can be performed on it. This is the next preferred option in TopBraid, as it does not require writing and compiling Java code. Instead, JavaScript can be shared on the web or with co-workers easily. The limitation is that the JavaScript code cannot access any RDF background knowledge from the current model, because there are no API calls to query the current TopBraid model (this has been addressed by SHACL-JS mentioned above).
You can use SWP to define new functions that execute an SWP script whenever the function is called.
SWP itself is very expressive and provides access to features such as if-then-else or for-each loops, as
well as tons of built-in capabilities and access to SPARQLMotion modules.
Subclass ui:Functions
to get started, and use ui:return
to define the result.
Various built-in namespaces such as swa.ui.ttlx
contain examples that may help you get started.
SPARQLMotion can be used to
declare the body of a SPIN query. This provides much greater expressivity
because the computation of the result value can be any SPARQLMotion script,
as long as it ends with a literal value (e.g. using sml:ReturnNode
or sml:ReturnText
). In order to use this capability, just
define a SPIN function that points to the return module of a SPARQLMotion
script using sm:returnModule
. Such functions can also be used
as web services. The disadvantage is that you will still be limited by
whatever SPARQLMotion modules are available, and you may need to fall back
to Java code if something is missing. On the other hand, we recommend to
try to push the declarative (and interpreted) use of SPARQLMotion as far as
possible, and then add the missing detail using custom SPARQLMotion modules.
Java plug-ins are the last and most general resort. TopBraid provides an extension point (org.topbraid.core.pluginActivators) for that purpose. Java provides you the greatest flexibility but also requires the most advanced programming skills. The following guide will attempt to help you get started.
Note that this capability is not officially supported by TopQuadrant. It is left here for people who are willing to make considerable efforts to work around problems as we cannot support every possible use case. The complexity of setting up plugins also means that we are not verifying whether the steps below are up to date with the latest Eclipse and Equinox versions.
In the following steps, we will create a new SPARQL Function called
my:max(predicate)
that computes the maximum literal
found for a given property in the current RDF model. It basically
walks through all triples that have a given property as predicate and
gets the numeric maximum of all those values.
If you haven't already done it, you first need to setup the Eclipse Plug-in development environment within TopBraid Composer. As of version 3.6, TopBraid Composer no longer ships with this plug-in included by default; you will need to install it. First, launch TopBraid Composer, click on Help->Install New Software-> (Select the most recent version of Eclipse from the drop-down). Once the list of software has populated (this may take a moment) look for the "Eclipse Plug-in development environment" in the list, or get it by typing "Eclipse Plug-in Development" into the filter text-box. Next, download and install this software plugin by checking off the box next to it and clicking Next when prompted. You may also be prompted to agree to some terms and conditions of the software plugin that is provided by Eclipse. Finally, once the plugin is installed TopBraid Composer will need to be restarted.
Start the plugin development environment that you setup and use File > New... to create a new Plug-in Project:
On the next screen of the wizard, fill in the project name and make it depend on OSGi only (only plug-ins that make contributions to the Eclipse user interface must use the Eclipse option).
On the last screen other options may be changed, but nothing crucial.
After finishing the wizard you should see a screen like the following. Eclipse may ask you to switch perspectives, in which case please do so.
Switch to the Dependencies tab and add all required plug-ins as shown on the next screenshot. The version numbers near the plug-ins might look different in your case.
Next, right-click on the example package com.my.project.functions
in the Package Explorer and run New > Class, opening the following
dialog. The class must be a subclass of the Jena class
org.topbraid.spin.arq.AbstractFunction1
. We recommend using
the TopBraid spin extension classes such as AbstractFunction1 (for one
argument), AbstractFunction2 (for two) etc.
.
We can now implement the body of the actual Java function that does the work.
Now we only need to declare that this code is a TopBraid plug-in.
Go back to the com.my.project.functions
editor, and
switch to the Overview page.
Click on Extensions to make the Extensions tab visible.
Create an extension of type org.topbraid.core.pluginActivators
:
Back on the main page of Eclipse, click on the extensions tab, expandorg.topbraid.core.pluginActivators
, select the activator and provide a new class com.my.project.functions.MaxFunctionPluginActivator
.
Click on class*: to get the following dialog, which will create a class that implements org.topbraid.core.activator.IPluginActivator. Click Finish.
In org.topbraid.core.activator.IPluginActivator, override the activate method to register your function with com.hp.hpl.jena.sparql.function.FunctionRegistry. For property functions, you use com.hp.hpl.jena.sparql.pfunction.PropertyFunctionRegistry.
Now we are ready to go, and try out the new function. To do so, go back to the Overview page and select Launch an Eclipse application to open a "nested" TopBraid Composer instance from within your plugin development environment. This new TBC instance would automatically generate a some system projects as in the following:
You should now able able to use the new function
with its provided URI. For example, open the example skos-xl.ttl
and enter the query
SELECT ?max WHERE { LET (?max := <http://my.com/functions#max>(owl:minCardinality)) }Result should be 2, as shown below.
As an optional but highly recommended step to make this function better
accessible and to document it, you should create a SPIN file that declares
the function in a clean way.
First, create a new file using New > RDF/SPIN File.
It is important to check that the box at the bottom so that the file
will have the ending .spin.ttl
.
Files marked with .spin.
will be scanned by TopBraid when
the system starts up, and then all functions defined in those files will
be immediately accessible.
In that file, adjust the namespace http://my.com/functions#
to the prefix my
.
Then create a subclass of spin:Functions
called my:max
. You should enter a comment on what it does.
Now we declare the arguments of the function using constraints.
Our function only takes a single argument (?arg1), and we can declare
it by dragging the property sp:arg1
over the label of
the spin:constraint
widget on the form. This will open
up a dialog in which we can specify the argument.
The declaration of this function is now finished (we do not need to
provide a spin:body
because this is handled by the Java
code. We now need to restart TopBraid Composer and should be able to
use the new function straight away. In particular you will be able
to use it with the abbreviation my:max
because this is
the prefix declared in the .spin.
file.