Binding a variable to one of two values with IF?

user2983384 picture user2983384 · Jan 14, 2014 · Viewed 14.7k times · Source

In the following SPARQL query, I'm not sure how to use if to bind one of two strings to the variable ?result. I heard that there are concepts of “in scope” and “out of scope,” but I don't really see the difference. I've also tried putting the if clause in the select line, but it didn't work either. How can I fix this query to bind ?result to one of the two strings based on the condition?

SELECT ?result
WHERE{
    ?chain rdf:type rdfs:Property .
    ?chain rdfs:domain <http://www.vs.cs.hs-rm.de/ontostor/SVC#MDiskGroup> .
    ?chain rdfs:range <http://www.vs.cs.hs-rm.de/ontostor/SVC#IOgroup> .
    ?this ?chain ?arg .
    ?arg io:id ?var .
    IF(?var = "0"^^xsd:integer,
       BIND("    *"^^xsd:string AS ?result),
       BIND(""^^xsd:string AS ?result)) .
}

Answer

Joshua Taylor picture Joshua Taylor · Jan 14, 2014

The if operator in SPARQL isn't a statement as it sometimes is in a programming language, but rather is an "function form" for creating an expression (with special evaluation semantics). The value of if(test,a,b) is a if test is true, and b if test is false. As the documentation says:

17.4.1.2 IF

rdfTerm  IF (expression1, expression2, expression3)

The IF function form evaluates the first argument, interprets it as a effective boolean value, then returns the value of expression2 if the EBV is true, otherwise it returns the value of expression3. Only one of expression2 and expression3 is evaluated. If evaluating the first argument raises an error, then an error is raised for the evaluation of the IF expression.

Examples: Suppose ?x = 2, ?z = 0 and ?y is not bound in some query solution:

IF(?x = 2, "yes", "no")     returns "yes"
IF(bound(?y), "yes", "no")  returns "no"
IF(?x=2, "yes", 1/?z)       returns "yes", the expression 1/?z is not evaluated
IF(?x=1, "yes", 1/?z)       raises an error
IF("2" > 1, "yes", "no")    raises an error

So, if isn't a statement like it might be in a programming language, but it's simply a function (though lazily evaluated) that takes three arguments and returns a value. SPARQL is a query language, and doesn't have statements that get executed; it's a query language for matching patterns in a graph and binding variables to values. So if is a function, and it just so happens that if the first argument is true, then it returns the second argument, otherwise it returns the third. In general, you'd bind the value of a function to a variable with

bind( function(args...) as ?variable )

and this case is no different. You'd call the if function and bind its result to a variable with

bind( if(condition,then,else) as ?result )

In your case, this means that you would use the following query. I've added some newlines to help the readability, but they're not necessary. Integers in a SPARQL query are shorthand for a literal with type xsd:integer, so I've also used (thanks to RobV's comment) 0 instead of "0"^^xsd:integer. (See 2.3.2 Matching Literals with Numeric Types.)

bind(if(?var = 0,
        "    *"^^xsd:string,
        ""^^xsd:string )
     as ?result)

If we actually want to shorten this even more, then we can use xsd:string as a constructor, and do (see 17.5 XPath Constructor Functions):

bind(xsd:string(if(?var = 0,"    *", "")) as ?result)

This might seem a little bit odd at first if you're used to doing things like

String result;
if ( var == 0 ) {
  result = "case 1";
}
else {
  result = "case 2";
}

but many language actually provide a ternary operator that lets you do the much shorter

String result = (var == 0) ? "case 1" : "case 2";

instead. This is the functionality you're getting with SPARQL.