Native SPARQL engine with custom functions

classic Classic list List threaded Threaded
4 messages Options
ehe
Reply | Threaded
Open this post in threaded view
|

Native SPARQL engine with custom functions

ehe
Hi,

I like the native SPARQL engine and I was wondering, if it is possible to integrate the option from rdflib to create own functions like this:

from rdflib.plugins.sparql.operators import custom_function

FN = Namespace("https://example.org/fn#")

@custom_function(FN.someFunction)
def some_function(s):
    result = do_sth_with_s(s)
    return result

And then use it e.g. like this:

sparql_query= """
PREFIX fn: <https://example.org/fn#>
SELECT ?s ?o WHERE {
  ?s a ?o .
    FILTER(?o > fn:someFunction(?s))
}
"""

This would be a great feature for me!
Reply | Threaded
Open this post in threaded view
|

Re: Native SPARQL engine with custom functions

Jiba
Administrator
Hi,

Indeed, I think it is possible, as a few SPARQL function are actually implemented in Python.

You need to :

 * create the Python function

 * declare it as an SQLite function, with world.graph.db.create_function(name, num_params, func, is_deterministic) (see the doc for create_function in the sqlite3 module)

 * finally, you need to modify the SPARQL parser so as it accepts your function.

Maybe we can extend Owlready with some functionalities for facilitating that ?

Jiba
ehe
Reply | Threaded
Open this post in threaded view
|

Re: Native SPARQL engine with custom functions

ehe
Thanks for the tip. I managed to do it without having to modify the library.
Here is the function and the decorator:

import re

from owlready2 import default_world
from owlready2.base import _universal_datatype_2_abbrev
from owlready2.rply import Rule
from owlready2.sparql.func import _FUNC_2_DATATYPE
from owlready2.sparql.parser import LEXER


def register_sparql_function(name, ret_type, function):
    LEXER.rules.append(Rule("FUNC", rf"(?:{name})\b", re.IGNORECASE))
    _FUNC_2_DATATYPE[name] = _universal_datatype_2_abbrev[ret_type]
    default_world.graph.db.create_function(name, 1, function, deterministic=True)

def sparql_function(name, ret_type):
    name = name.upper()

    def decorator(func):
        register_sparql_function(name, ret_type, func)
        return func

    return decorator

Then it can be used like this for functions with 1 parameter:

@sparql_function(name="OWNFUNC", ret_type =str)
def fn(s):
    ...
    return result

To support functions with more parameters, the parse_expression function from FuncSupport and perhaps the parser must be adapted.

Reply | Threaded
Open this post in threaded view
|

Re: Native SPARQL engine with custom functions

Jiba
Administrator
Hi,

Thank you for your implementation!

I've integrated it in the development version of Owlready, in a slightly modified version. In particular, all Python-defined functions must be prefixed with PY_FUNC_ in SPARQL, which simplify the problem of the parser/lexer.

Enjoy,
Jiba