SPARQL Function, Method, Event and Datatype

2019, April 8th

Authors

Olivier Corby <olivier.corby@inria.fr>

Abstract

This document presents Method, Event and Datatype extensions to SPARQL Function, a simple programming that enables users to define end execute extension functions with SPARQL. We propose a mechanism to define events that are associated to query processing. We also propose mechanisms that enables us to overload SPARQL operators and to define methods attached to classes of the ontology.

Table of Contents

1. Introduction
2. Definition
3. Error Overloading
4. Operator Overloading
5. Query Processing Event
6. Method
7. Implementation

1. Introduction

We have defined SPARQL Function which enables users to define extension functions for SPARQL. We now define SPARQL Event which models events occcurring during query processing. We associate SPARQL function definitions to SPARQL events. When an event occurs the associated function is executed. Furthermore, we define SPARQL Datatype which are extension datatypes to represent and manipulate objects that participates to SPARQL query processing such as: statement, expression, triple, solution, error, etc. This generic design pattern, SPARQL Function, Event and Datatype enables us to provide error overloading, extension datatype operator overloading and SPARQL statement overloading. In addition, it enables us to provide a generic "eval" function that enables us to call the SPARQL filter interpreter within SPARQL Function.

2. Definition

In the document, we use prefix and namespaces shown below:

prefix rq: <http://ns.inria.fr/sparql-function/>
prefix dt: <http://ns.inria.fr/sparql-datatype/>
prefix st: <http://ns.inria.fr/sparql-template/>
prefix xt: <http://ns.inria.fr/sparql-extension/>
prefix us: <http://ns.inria.fr/sparql-extension/user/>

SPARQL Function

SPARQL Function is a simple programming language that enables users to define and execute functions within SPARQL. The body of a function is based on SPARQL filter expressions. We show an example of SPARQL Function.

select * where {
    ?x rdf:value ?v 
    filter us:test(?x)
}

function us:test(?x) {
    strstarts(str(?x), rdf:) || strstarts(str(?x), rdfs:)
}

SPARQL Function enables users to execute SPARQL queries within functions. It provides users with additional statements such as "let" and "for", second order functions such as "apply" and "map", pattern matching statements and extension datatypes to represent RDF and SPARQL objects. The function below returns the type of an individual.

function us:type(?x) { 
    let (select * where { ?x a ?t }) {
        ?t
    }
}

SPARQL Event

We define SPARQL Event as events occurring during query processing such as: start, finish, etc. For each SPARQL Event we define an event annotation: @start, @finish, etc. An event annotation can be associated to a function in such a way that the function is associated to the event.

In the example below, we associate a function to the ''before'' event by means of the ''@before'' annotation. The "before" function is called with the query as argument.

@event
select * where {
    ?x rdf:value ?v 
}

@before
function us:before(?q) {
    xt:print('before', ?q)
}

We define the ''@type'' annotation which enables us to specify that a function is associated to a specific extension datatype.

@type dt:triple
function us:eq(?t1, ?t2) {
    mapevery(rq:eq, ?t1, ?t2)
}

We define the ''@type dt:error'' annotation which enables us to specify that a function is associated to the occurrence of an error.

@type dt:error
function us:eq(?e, ?t1, ?t2) {
    xt:print('error', ?e, ?t1, ?t2)
}

SPARQL Datatype

We define a set of extension datatypes to represent SPARQL query processing objects.

The list datatype is dt:list

dt:list

SPARQL select and construct, as well as update, query is represented as dt:query datatype value. SPARQL statement such as union or optional is represented as dt:statement, filter expression is represented as dt:expression.

dt:query
dt:statement
dt:expression

The dt:expression datatype represents expressions such as: "?x + ?y" of "us:fun(?x)". The content of an expression can be accessed by pattern matching in SPARQL Function, considering the expression in prefix (functional) notation.

# ?e = "?x + ?y"^^dt:expression
let ((?name | ?arg) = ?e) {
    # ?name = "+"
    # ?arg = "(?x ?y)"^^dt:list
}

SPARQL Query solution sequence is represented as dt:mappings and solution as dt:mapping. SPARQL construct query solution is represented as dt:graph and dt:triple. A solution to a Property Path is a dt:path which contains the list of triples of the path.

dt:mappings
dt:mapping
dt:graph
dt:triple
dt:path

Query solution sequence is iterable.

# ?s : dt:mappings
# ?r : dt:mapping
for (?r in ?s) {

}

Graph and triple are iterable.

# ?graph  : dt:graph
# ?triple : dt:triple
for (?triple in ?graph) {
    let ((?s ?p ?o) = ?triple) {
    
    }
}

An error is represented by the abstract datatype dt:error.

dt:error

 

3. Error Overloading

The first event that we define is the ''error'' event wich represents the occurrence of an error in SPARQL filter evaluation. An error may occur when a variable is unbound or when the arguments of an operator do not have the expected type. We distinguish local errors from top level errors. A local error is detected at the moment where it is throwned. A top level error is detected in the SPARQL clause that started the evaluation of the expression: filter, select, having, etc.

Local Error

If an error occurs during the evaluation of an expression, an error is thrown. Error processing for binary operators is done by method annotated with @type dt:error, whose name is the name of the operator in the us: namespace, e.g. us:eq for "=". There is also a default method with name us:error, that traps errors for all binary operators. The arguments of the method are the expression and the values of the arguments of the operator. The result of the method is returned as the result of the evaluation of the expression, hence it is no more an error. However, it is possible to return an error from the method by calling the specific function error().

@type dt:error 
function us:eq(?e, ?a, ?b) { }

@type dt:error 
function us:error(?e, ?a, ?b) { }

Below is the list of error functions.

function us:eq(dt:expression ?e, term ?a, term ?b)
function us:ne(dt:expression ?e, term ?a, term ?b)
function us:le(dt:expression ?e, term ?a, term ?b)
function us:lt(dt:expression ?e, term ?a, term ?b)
function us:ge(dt:expression ?e, term ?a, term ?b)
function us:gt(dt:expression ?e, term ?a, term ?b)

function us:plus(dt:expression ?e, term ?a, term ?b)
function us:mult(dt:expression ?e, term ?a, term ?b)
function us:minus(dt:expression ?e, term ?a, term ?b)
function us:divis(dt:expression ?e, term ?a, term ?b)

function us:error(dt:expression ?e, term ?a, term ?b)

Top Level Error

Errors trapped at toplevel of evaluation (filter, select, bind, etc.) call a method with same name as above but with expression as solely argument. In addition to binary operators, it is generalized to every SPARQL predefined function with a method whose name is the function name in the us: namespace, for example "us:if" for "if". The generic method us:error can also be used to trap errors on any expression. The result of the method overloads the error. However, it is possible to return an error using the error() specific function.

@type dt:error 
function us:plus(?e) { }

@type dt:error 
function us:if(?e) { }

@type dt:error 
function us:error(?e) { }
function us:eq(dt:expression ?e)
function us:ne(dt:expression ?e)
function us:le(dt:expression ?e)
function us:lt(dt:expression ?e)
function us:ge(dt:expression ?e)
function us:gt(dt:expression ?e)

function us:plus(dt:expression ?e)
function us:mult(dt:expression ?e)
function us:minus(dt:expression ?e)
function us:divis(dt:expression ?e)

function us:error(dt:expression ?e)

Specific Functions

Error function

The error function has for effect to throw an error, in the same way as an exception. Evaluation of the current expression resumes to the top level SPARQL clause that executes the expression, e.g. filter. An error that is throwned by the error function can be trapped by the mechanism presented in this document.

function error()

Eval function

The ''eval'' function evaluates an argument that is an expression of type dt:expression. It returns the result that would be returned by the SPARQL filter interpreter when evaluating the expression. Hence, the "eval" function can be seen as a call to the SPARQL filter interpreter given a representation of an expression in the form as a dt:expression datatype value.
Use case: error @select @filter, etc., which call the event function with an argument which is a Pointer on Expression.

@select 
function us:select(?e, ?v) {
    xt:print('select', ?e, eval(?e), ?v)
}

The code below emulates the SPARQL filter interpreter.

@filter 
function us:filter(?g, ?e, ?v) {
    us:eval(?e)
}

Variable ?name is bound to the name of the function or operation and ?arg is bound to the list of arguments.

function us:eval(?e) {
    let ((?name | ?arg) = ?e) {
        if (bound(?name)) {
            apply(?name, maplist(us:eval, ?arg))
        }
        else { 
            eval(?e)
        }
    }
}
function eval(dt:expression ?e)

 

4. Operator Overloading

Extension datatypes deserve extension operators.

function us:eq(datatype ?a, datatype ?b)
function us:ne(datatype ?a, datatype ?b)
function us:le(datatype ?a, datatype ?b)
function us:lt(datatype ?a, datatype ?b)
function us:ge(datatype ?a, datatype ?b)
function us:gt(datatype ?a, datatype ?b)

function us:plus(datatype ?a, datatype ?b)
function us:mult(datatype ?a, datatype ?b)
function us:minus(datatype ?a, datatype ?b)
function us:divis(datatype ?a, datatype ?b)

Simple Overloading

Operator overloading for ''km'' datatype.

insert data { 
    us:t1 us:length '1'^^us:km  .  
    us:t2 us:length '2'^^us:km .  
}
@type us:km 
function us:eq(?a, ?b) {
    us:convert(?a) = us:convert(?b)
}

function us:convert(?a) {
    if (datatype(?a) = us:km, us:value(?a) * 1000, 
    if (datatype(?a) = us:m,  us:value(?a),
    if (datatype(?a) = us:length, us:valueunit(?a), us:value(?a))))
}
     
function us:value(?a) {
    if (contains(?a, ' '), xsd:integer(strbefore(?a, ' ')), xsd:integer(?a))
}

function us:valueunit(?a) {
    if (strafter(?a, ' ') = 'km', 1000 * us:value(?a), us:value(?a))
}

Multiple Overloading

Possibility to define datatypes as specializing supertype.
If two different datatypes specialize the same supertype, overloading is performed.
Overloaded operator may be defined on type or supertype
us:km and us:m can be compared because they specialize us:length, operator can be defined on us:km, us:m and us:length.
Suitable for "1"^^us:km and for "1 km"^^us:length

xt:datatype(us:km, us:length)
xt:datatype(us:m,  us:length)
filter ('1 km'^^us:length = '1000 m'^^us:length)

@type us:length 
function us:eq(?a, ?b) { 
    us:convert(?a) = us:concert(?b)
}

Possible to mix '1'^^us:km with '1000 m'^^us:length.

insert data { 
    us:t1 us:length '1'^^us:km  .  
    us:t2 us:length '1000'^^us:m .  
    us:t3 us:length '1000 m'^^us:length
}
@event 
select *  where {
 ?x ?p ?v . ?y ?p ?w  filter (?v = ?w) 
}
       
@init 
function us:init(?q){
    xt:datatype(us:km, us:length) ;
    xt:datatype(us:m,  us:length)
}
@type us:length us:km us:m
function us:eq(?a, ?b) {
    us:convert(?a) = us:convert(?b)
}

Overload us:compare for order by with extension datatypes.

@event 
select where {}
order by ?x

@type us:length
function us:compare(?a, ?b) {
   if (?a < ?b, -1, if (?a = ?b, 0, 1))
}

Overload operator for bnode

Overload bnode comparison.

@type dt:bnode
function us:eq(?a, ?b) { 
    ?a = ?b 
}

Example

Romain numbers with datatype us:romain.

prefix rm: <http://ns.inria.fr/sparql-extension/spqr/>
@event 
select (rm:digit(?res) as ?ope) (rm:digit(?val) as ?dig) 
where {
  bind ('II'^^us:romain * 'X'^^us:romain + 'V'^^us:romain as ?res) 
  bind (maplist(us:romain,  xt:iota(7)) as ?list)
  bind (reduce (lambda(?x, ?y) { ?x + ?y }, ?list) as ?val)
}

function us:rom(?x) { strdt(rm:romain(?x), us:romain) }

@type us:romain {
function us:compare(?x, ?y) { if (?x < ?y, -1, if (?x = ?y, 0, 1)) }

function us:eq(?x, ?y)    { (rm:digit(?x) =  rm:digit(?y)) } 
function us:ne(?x, ?y)    { (rm:digit(?x) != rm:digit(?y)) }
function us:lt(?x, ?y)    { (rm:digit(?x) <  rm:digit(?y)) }
function us:le(?x, ?y)    { (rm:digit(?x) <= rm:digit(?y)) }
function us:gt(?x, ?y)    { (rm:digit(?x) >  rm:digit(?y)) }
function us:ge(?x, ?y)    { (rm:digit(?x) >= rm:digit(?y)) } 

function us:plus(?x, ?y)  { us:rom(rm:digit(?x) + rm:digit(?y)) }
function us:mult(?x, ?y)  { us:rom(rm:digit(?x) * rm:digit(?y)) }
function us:minus(?x, ?y) { us:rom(rm:digit(?x) - rm:digit(?y)) }
function us:divis(?x, ?y) { us:rom(rm:digit(?x) / rm:digit(?y)) } 
}

 

5. Query Processing Event

LDScript functions annotated with @before, @after are executed by QuerySolverVisitor during query processing.

@before 
function us:before(?q) {
  xt:print('start', ?q)
}

@after 
function us:after(?m) {
   xt:print('result', ?m)
}

Annotation @event enables use of event functions
Annotation @recursion enables operator recursive overloading: dt:graph = dt:graph can recursively overload dt:triple = dt:triple

@event @recursion

@timeout function us:timeout(?serv) timeout for service

@limit function us:limit(?map) return true if under limit

@start @finish for SPARQL subquery, LDScript subquery and subtemplate.

Distinct

@event 
select * 
where {
  ?a owl:sameAs ?b
  {?a foaf:knows ?x} union {?b foaf:knows ?x}
}

@distinct
function us:distinct(?m) {
    let ((?a ?b ?x) = ?m) {
        us:key(xt:add(xt:sort(xt:list(?a, ?b)), ?x))
    }
}

function us:key(?l) {
    reduce(rq:concat, maplist(lambda(?e) { concat(xt:index(?e), ".") }, ?l))
}

Order by

@event 
select *
where {
  ?a foaf:knows ?y optional {?y foaf:name ?n}
}

@orderby       
function us:comparemap(?m1, ?m2) {
    us:revcompare(xt:size(?m1) , xt:size(?m2))
}

function us:revcompare(?x, ?y) {
    if (?x < ?y, 1, if (?x = ?y, 0, -1))
}

Path

Path where all edges verify a constraint, e.g. subject.age <= object.age.

@event 
select * where {
    ?x foaf:knows+ ?y 
}     

@step 
function us:step(?g, ?q, ?p, ?s, ?o) {
    let ((?f . ?l) = ?p, (?a ?q ?b) = ?l) {
        return(exists { ?a us:age ?aa . ?b us:age ?bb  filter (?aa <= ?bb) })
    } 
}

Focus on path with size less than threshold

@step 
function us:step(?g, ?q, ?p, ?s, ?o) {
   return(xt:size(?p) <= 2)
}

SPARQL Event List

We present the list of SPARQL events and the associated functions with their signature. RDF terms, i.e. URI, bnode and literal, have term type.

@init       function us:init(dt:query ?q)
@before     function us:before(dt:query ?q)
@after      function us:after(dt:mappings ?map)
@start      function us:start(dt:query ?q)
@finish     function us:finish(dt:mappings ?map)

@statement  function us:statement(URI ?g, dt:statement ?e)
@function   function us:function(dt:expression ?call, dt:expression ?fun)
@produce    function us:produce(URI ?g, dt:triple ?q)
@candidate  function us:candidate(URI ?g, dt:triple ?q, dt:triple ?t)
@result     function us:result(dt:mappings ?map, dt:mapping ?m)
@distinct   function us:key(dt:mapping ?m)
@orderby    function us:compare(dt:mapping ?m1, dt:mapping ?m2)
@limit      function us:limit(dt:mappings ?map)
@timeout    function us:timeout(URI ?serv)
@slice      function us:slice(URI ?serv, dt:mappings ?map)

@join       function us:join    (URI ?g, dt:statement ?e, dt:mappings ?m1, dt:mappings ?m2)
@minus      function us:minus   (URI ?g, dt:statement ?e, dt:mappings ?m1, dt:mappings ?m2)
@union      function us:union   (URI ?g, dt:statement ?e, dt:mappings ?m1, dt:mappings ?m2)
@optional   function us:optional(URI ?g, dt:statement ?e, dt:mappings ?m1, dt:mappings ?m2)

@bgp        function us:bgp    (URI ?g, dt:statement ?e, dt:mappings ?m)
@graph      function us:graph  (URI ?g, dt:statement ?e, dt:mappings ?m)
@service    function us:service(URI ?s, dt:statement ?e, dt:mappings ?m)
@query      function us:query  (URI ?g, dt:statement ?e, dt:mappings ?m)
@values     function us:values (URI ?g, dt:statement ?e, dt:mappings ?m)

@path       function us:path(URI ?g, dt:triple ?q, dt:path ?p, term ?s, term ?o)
@step       function us:step(URI ?g, dt:triple ?q, dt:path ?p, term ?s, term ?o)

@select     function us:select(dt:expression ?e, term ?v)
@aggregate  function us:agg(dt:expression ?e, term ?v)
@bind       function us:bind(URI ?g, dt:expression ?e, term ?v)
@filter     function us:filter(URI ?g, dt:expression ?e, xsd:boolean ?b)
@having     function us:having(dt:expression ?e, xsd:boolean ?b)

 

6. Method

We propose a design pattern to associate methods (functions) to classes of the ontology. We associate a method to a class on the ontology by specifying the @type annotation followed by the name of the class. This enables us to call a method on an individual in such a way that the method that is executed is retrieved in the list of the classes (using the rdf:type property) and superclasses (using rdf:type/rdfs:subClassOf*) of the individual. A method is called using the "method" function. The @method annotation enables method call.

@method 
select (method(us:area, ?x) as ?a)
where {
    ?x a us:Figure
}
@type us:Rectangle
function us:area(?x) {
    let (select * where { ?x us:width ?w ; us:length ?l } ) {
        ?w * ?l
    }
}

@type us:Circle
function us:area(?x) {
    let (select * where { ?x us:radius ?r } ) {
        3.1415 * ?r * ?r
    }
}

The "xt:method" function enables users to specify the type of the individual to be considered for the method call, independently of its rdf:type.

@method 
select (xt:method(us:area, ?x, us:Circle) as ?a)
where {
    ?x a us:Circle
}

 

7. Implementation

SPARQL Function Method, Event and Datatype are implemented is the Corese Semantic Web factory as open source software. It is available on github. A demo server is available online.

Bibliography

Olivier Corby, Catherine Faron-Zucker and Fabien Gandon, LDScript: a Linked Data Script Language, International Semantic Web Conference, ISWC, 2017 October, Vienna, Austria.

RDF 1.1 Concepts and Abstract Syntax, Graham Klyne, Jeremy J. Carroll, Brian McBride. W3C Recommendation, February 2014

SPARQL 1.1 Query Language, Steve Harris, Andy Seaborne. W3C Recommendation, March 2013