ruby-rdf / sparql

Ruby SPARQL library
http://rubygems.org/gems/sparql
The Unlicense
88 stars 14 forks source link

Allow custom functions to be registered #10

Closed gkellogg closed 11 years ago

gkellogg commented 11 years ago

As requested in issue #9, provide a mechanism for registering function handlers for IRIs using the iriOrFunction rule (see http://www.w3.org/TR/sparql11-query/#ririOrFunction).

This should also automatically register the defined XPath constructor functions: http://www.w3.org/TR/sparql11-query/#operandDataTypes.

Jena documents writing filter functions here: http://jena.apache.org/documentation/query/writing_functions.html

mwkuster commented 11 years ago

Just to avoid potential double work: I had volunteered to invest some work on this ticket if needed. On the other hand you've commented on issue 11 that you are already "pretty close" to finishing the custom function functionality. So I assume no action expected from my side?

gkellogg commented 11 years ago

Right, I've pretty much finished it, but it requires a bit of documentation. I should push it up today.

Basically, you register a class which is a subclass of SPARQL::Algebra::Operator with an IRI. It must also implement SPARQL::Algebra::Evaluatable.

Gregg Kellogg

Sent from my iPad

On May 20, 2013, at 2:40 AM, mwkuster notifications@github.com wrote:

Just to avoid potential double work: I had volunteered to invest some work on this ticket if needed. On the other hand you've commented on issue 11 that you are already "pretty close" to finishing the custom function functionality. So I assume no action expected from my side?

— Reply to this email directly or view it on GitHub.

gkellogg commented 11 years ago

By the way, I ended up going with a block-based approach for defining the function, as described in the README:

# Register a function using the IRI <http://rubygems.org/gems/sparql#crypt>
crypt_iri = RDF::URI("http://rubygems.org/gems/sparql#crypt")
SPARQL::Algebra::Expression.register_extension(crypt_iri) do |literal|
  raise TypeError, "argument must be a literal" unless literal.literal?
  RDF::Literal(literal.to_s.crypt)
end

Then, use the function in a query:

PREFIX rsp: <http://rubygems.org/gems/sparql#>
PREFIX schema: <http://schema.org/>
SELECT ?crypted
{
  [ schema:email ?email]
  BIND(rsp:crypt(?email) AS ?crypted)
}
mwkuster commented 11 years ago

Great. Will check it out directly this evening

mwkuster commented 11 years ago

Have tested it and it works great. Have starting to use this functionality straight away in my application.

Just one minor remark: the query does not yet seem to report errors when trying to use a non-registered custom function

Thanks a lot for adding this to the library!

gkellogg commented 11 years ago

The extension mechanism raises TypeError if no function is found, however the SPARQL specification treats this like it does any function that raises TypeError: it essentially ignores it and removes the relevant results from the result set. From SPARQL 1.1 Query:

It should be noted that any function or operator that is specified to return an error under some conditions is a valid extension point. That is, an implementation may return a non-error value in these error cases, and still be conformant with this recommendation.

Trying to access a non-existant extension function falls into this category. If there's some other reading that indicates a different error should be raised, I'm unaware of it.

gkellogg commented 11 years ago

Pushed in version 1.0.8.