Conditional subquery in SPIN function (SPARQL)

337 Views Asked by At

How do I change the query formula based on whether or not a variable is bound?

I am invoking the magic property like this:

WHERE {
    VALUES (?subj) {
        ([my bound positional parameter value goes here...])
    }
    ?subj :myMagicProperty ?result .    
}

Inside the magic property, I do a union:

?result a :Rule .
{
    ?result :someProp ?subj .
}
UNION
{
    FILTER NOT EXISTS {
        ?result :someProp ?anyValue .
    }
}

In other words, get me all results where :someProp is this value or :someProp is not defined.

Here is the tricky part. If ?subj is unbound (i.e., I set it as UNDEF in the VALUES block), the above query goes wild and returns everything.

Instead, I want to check if ?subjis unbound. If ?subj is unbound, :myMagicProperty should only return the following results:

FILTER NOT EXISTS {
    ?result ?someProp ?anyValue .
}

I have experimented with using FILTER and the BOUND function, but I can't figure out how to get the correct behavior. How can I drop one of UNION clauses from my query when ?subj is not bound?

Updates

Revised the first query to add the VALUES block.
Added missing ?result a :Rule . statement.
Corrected ?someProp to :someProp.

3

There are 3 best solutions below

0
On

Another way to do this is with COALESCE:

SELECT ?result
WHERE {
   ?result a :Rule .
   OPTIONAL {
      ?result :someProp ?value .
   }
   FILTER (COALESCE(?value = ?subj, !bound(?value)))
}

...this avoids the sub-select and simply filters to include only the ?result matches where '?value = ?subj', and if that clause fails the !bound() clause ensures matches that do not have a :someProp property are also included.

2
On

First I'd like to confirm what your intent is. I'd like to do that by asking you to respond to the following query that you can run in TopBraid Composer.

SELECT *
  WHERE { GRAPH <http://topbraid.org/examples/kennedys> {
    VALUES (?property) {(kennedys:firstName) (kennedys:lastName) (UNDEF)}
    { 
      FILTER(BOUND(?property) )
      ?s ?property ?result .  
    }
  UNION 
   {
     FILTER(!BOUND(?property))
     BIND("not sure what you want to do in this case" AS ?result)
    }
   }
 }

The difference in the code above to your code is that I am setting values of your ?someProp in the VALUES statement, whereas you are setting ?subj.

The UNIONed subgraphs are using BOUND and !BOUND as guards.

Before going further with help I'd like to hear from you with a clearer explanation of the query you are wanting to build. Then I can show you the magic property that will be needed.

It's this piece of your initial post I need to understand more:

Here is the tricky part. If ?subj is unbound (i.e., I set it as UNDEF in the VALUES block), the above query goes wild and returns everything.

Instead, I want to check if ?subj is unbound. If ?subj is unbound, myMagicProperty should only return the following results:

FILTER NOT EXISTS {
   ?result ?someProp ?anyValue .
}*

With ?someProp undefined, as well as ?result and ?anyValue, what were you expecting to come back? Also this subgraph of yours has no assertions that will populate the graph and therefore will return nothing.


Ralph

0
On

The trick is, I need to do the UNION using a variable different than the one passed in as an argument. This way, the UNION operation does not cause the unbound parameter to be bound. After the UNION, I can use a FILTER to control the results based on the input parameter.

SELECT ?result
WHERE {
    ?result a :Rule .
    {
        SELECT ?rule ?value ?anyValueMatch
        WHERE {
            {
                ?rule :someProp ?value .
                BIND (false AS ?anyValueMatch) .
            }
            UNION
            {
                FILTER NOT EXISTS {
                    ?rule :someProp ?any .
                } .
                BIND (true AS ?anyValueMatch) .
            } .
        }
    } .
    FILTER ((bound(?subj) && (?value = ?subj)) || (?anyValueMatch = true)) .
}