Binding a variable to one of two values with IF?

if-statement, rdf, sparql

Solution

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.

Problem

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)) . } ```

Original source