Adding SPARQL Functions to TopBraid

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.

Tutorial: Writing a new SPARQL Function in Java

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.

Step 0: Setup a plugin development environment with TopBraid Composer

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.

Step 1: Create the Plug-in

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.

Step 2: Implement the Java Class

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.

Step 3: Declare the Plug-in

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.

Step 4 (Optional): Declare the function in SPIN

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.