Stardog webfunction plugin release v1.0.3

I've just released a new version of the WebFunction plugin v1.0.3

Lots of new updates.

  • Support for installing multiple versions
  • Support for calling functions with a property function
  • Support for calling functions with a sparql service
  • Moved rust functions to a separate repository
  • Rust based testing and release automation
  • Support full output across funcitons, property functions, and service queries
  • Support for lambdas with compose,map, reduce, partial and filter functions
  • memoize function

There was some behind the scenes work to move the function implementations to their own repository now located at GitHub - semantalytics/stardog-webfunctions This included getting the testing and release automation written in Rust. This should make publishing new functions pretty quick. You can now publish a new function in a matter of minutes.

While working on the testing framework I realized that it would be nice to be able to test multiple versions of the plugin in the same Stardog instance. After thinking about it I realized that one of my goals was stability and reliability when using WebFunctions. No one likes it when an upgrade comes along and breaks their stuff. So I decided to allow multiple versions of the plugin to be installed at the same time. The WebFunctions are assigned possibly two names. The first name is an immutable name keyed off the plugin versions http://(http://semantalytics.com/2021/03/ns/stardog/webfunction/{plugin_version}/ the second name is negotiated among the installed plugins and is given to the plugin with the most recent version number and is the mutable name http://(http://semantalytics.com/2021/03/ns/stardog/webfunction/latest/ That way it's up to the user if they get automatic upgrades. If you'd like the latest and greates, use the mutable name and your queries will use the most recent version. If you'd either like stability and not have things unexpectedly change on you or roll back to an older version in the event that something breaks, you can use the immutable version. It's up to you.

Function implementations have been moved to a separate repository located here GitHub - semantalytics/stardog-webfunctions

Support for property functions was added. Since property functions can take input on either the subject or object position two functions were created that take the function name as the first argument wf:IN_call_OUT and wf:OUT_call_IN.

These would be called as follows:

prefix wf: <http://semantalytics.com/2021/03/ns/stardog/webfunction/1.0.3/>
prefix string: <http://wf.semantalytics.com/ipns/k51qzi5uqu5dlx0ttqevj64d3twk31y7hsgnofkqkjaiv11k98lj2rx60kjgv5/stardog/function/string/>

SELECT ?result WHERE {
    (?result) wf:OUT_call_IN (string:toUpper "stardog") .
}

or the equivalent query

prefix wf: <http://semantalytics.com/2021/03/ns/stardog/webfunction/1.0.3/>
prefix string: <http://wf.semantalytics.com/ipns/k51qzi5uqu5dlx0ttqevj64d3twk31y7hsgnofkqkjaiv11k98lj2rx60kjgv5/stardog/function/string/>

SELECT ?result WHERE {
     (string:toUpper "stardog") wf:IN_call_OUT (?result) .
}

Property functions are deprecated in Stardog so I've also included support for service queries The above query can be executed as a service query.

prefix string: <http://wf.semantalytics.com/ipns/k51qzi5uqu5dlx0ttqevj64d3twk31y7hsgnofkqkjaiv11k98lj2rx60kjgv5/stardog/function/string/>
prefix wfs: <tag:semantalytics:stardog:webfunction:1.0.3:>
prefix wf: <http://semantalytics.com/2021/03/ns/stardog/webfunction/1.0.3/>

SELECT ?result WHERE {
    SERVICE wfs:service {
        [] wf:call string:toUpper;
           wf:args "stardog";
           wf:result ?result
    }
}

A separate namespace for the service was needed to differentiate it from a regular service query.

Any function can be called as either a function, property function or service query. If a function that returns multiple bindings the results will be wrapped in an array literal. If multiple results are return it again will be wrapped in another array literal making it a 2D array. Why would you choose one vs the other? It's mostly personal preference with service queries and property functions being somewhat more performant. You will only notice the performance difference for large or complex functions that have a high startup cost. Service queries only need to create a single webassembly instance while functions need to allocate a new instance for each result.

Lastly support for lamdas were added with wf:map, wf:reduce, wf:compose, wf:partial and wf:filter.

Compose and partial allow you to define functions as either pipelined compostions of other functions and partial functions. Partial functions can have undefined variables in arbitrary positions with by defining those postions with wf:var. The results of compose and partial are assigned to BNodes that can be executed with wf:call.

A trivial compose function could be defined as

SELECT ?result WHERE { BIND(wf:call(wf:compose("LCASE", "UCASE"), "stardog") AS ?result) }";

Call can execute builtin functions as well as webassembly functions

a partial

SELECT ?result WHERE { BIND(wf:call(wf:partial(f:concat, wf:var, "dog"), "star") AS ?result) }

a filter

SELECT ?result WHERE { UNNEST(wf:filter(f:is_numeric.wasm, wf:call(f:arrayof, "star", "dog", "1", "2")) AS ?result) }

a map

SELECT ?result WHERE { UNNEST(wf:map(f:toUpper, wf:call(f:arrayOf, "star", "dog")) AS ?result) }

and reduce

SELECT ?result WHERE { BIND(wf:reduce("http://www.w3.org/2005/xpath-functions#numeric-add", wf:call(f:arrayOf, 2, 2, 2)) AS ?result) }

The last funciton added was wf:memoize to cache expensive functions that might e repeatedly called. It's exactly the same as the wf:call function with the first argument being the size of the cache.

I'm hoping that this rounds out the functionality for the plugin and that the api is getting to a stable enough point to allow me to concentrate on creating and releasing functions that you can use.