Search code examples
sparqlrdfsemantic-webowlontology

Why is the optional binding not working


This is a question about why my code is not working, not about how to do something, that's why I didn't provide you with a data, but if you want, i can give you, but the data will be my relatively small ontology.

this is my code

OPTIONAL
      { 

    VALUES ?user { rs:ania }
        ?userContext  rdf:type       rs:UserContext ;
                  rs:appliedOnItems  ?itemClass ;
                  rs:appliedOnUsers  ?userClass .

        ?item  rdf:type  ?itemClass .

        OPTIONAL
          { ?userContext  rs:hasWeightIfContextMatched  ?weightMatched }
        OPTIONAL
          { ?userContext  rs:hasWeightIfContextDoesNotMatch  ?weightNotMatched }
        OPTIONAL
          { ?userContext  rs:doNotRecommendInCaseNotMatch  true
            BIND(1 AS ?skip_)
          }
      bind(if (bound(?skip_) && (not EXISTS {?user a ?userClass}) , ?skip_, 0) as ?skip1)


    values (?defaultUserMatched ?defaultUserNotMatched) {(1 0.5)}
        BIND(if(EXISTS { ?user  rdf:type  ?userClass }, coalesce(?weightMatched, ?defaultUserMatched), coalesce(?weightNotMatched, ?defaultUserNotMatched)) AS ?weight)


      }
    values ?defaultNoUserContext {1}
    BIND(if(bound(?skip1), ?skip1, 0) as ?skip)
    BIND(if(bound(?weight), ?weight, ?defaultNoUserContext) AS ?userContextWeight)

  }

this code is just a block in my real query, there is another block that brings the ?item variable.

as you see, my code has ?item rdf:type ?itemClass , but one of the bindings for ?item is not from the type of ?itemClass so the whole optional will not execute (for that binding), so when we go out of the optional, there is this line

BIND(if(bound(?weight), ?weight, ?defaultNoUserContext) AS ?userContextWeight)

the if part will give false so the ?userContextWeight should be bound to ?defaultNoUserContext. However, my code doesn't produce anything (any value at all) to these items . do you know why please?

again if you need data, i am more than welcome to give you, thanks

Update

Now I see better, what i want is:

even if the item doesn't belong to the ?itemClass, i need to give a default value for the ?userContextWeight.

look at the update

 OPTIONAL
      { 
     VALUES ?user { rs:ania }

        ?userContext  rdf:type       rs:UserContext ;
                  rs:appliedOnItems  ?itemClass ;
                  rs:appliedOnUsers  ?userClass .

    bind (if (  exists {?item  rdf:type  ?itemClass .}, true , false) as ?doesItemBelongToUserContextItemClass)

    OPTIONAL
          { ?userContext  rs:hasWeightIfContextMatched  ?weightMatched }
        OPTIONAL
          { ?userContext  rs:hasWeightIfContextDoesNotMatch  ?weightNotMatched }
        OPTIONAL
          { ?userContext  rs:doNotRecommendInCaseNotMatch  true
            BIND(1 AS ?skip_)
          }
      bind(if (bound(?skip_) && (not EXISTS {?user a ?userClass}) , ?skip_, 0) as ?skip1)


    values (?defaultUserMatched ?defaultUserNotMatched) {(1 0.5)}
        BIND(if(EXISTS { ?user  rdf:type  ?userClass }, coalesce(?weightMatched, ?defaultUserMatched), coalesce(?weightNotMatched, ?defaultUserNotMatched)) AS ?weight)
      }
    values ?defaultNoUserContext {1}
    BIND(if(bound(?skip1), ?skip1, 0) as ?skip)

  BIND( if ( !?doesItemBelongToUserContextItemClass , ?defaultNoUserContext ,if(bound(?weight), ?weight, ?defaultNoUserContext)) AS ?userContextWeight)

  }

in the update, i check if the item belongs to the class or not using this bind

 bind (if (  exists {?item  rdf:type  ?itemClass .}, true , false) as ?doesItemBelongToUserContextItemClass)

and then outside the optional i do this

BIND( if ( !?doesItemBelongToUserContextItemClass , ?defaultNoUserContext ,if(bound(?weight), ?weight, ?defaultNoUserContext)) AS ?userContextWeight)

my problem is the value of ?userContextWeight when the item doesn't belong to the class is ?weightNotMatched, but it should be ?defaultNoUserContext (look again at the last binding please)

any idea please?

Update 2

by !?doesItemBelongToUserContextItemClass I mean the normal boolean not that we study in algebra, maybe here it is not the same as there? this could be the problem?

Update 3

I see that this

 bind (if ( exists {?item  a  ?itemClass }, true , false) as ?doesItemBelongToUserContextItemClass)

always gives true to doesItemBelongToUserContextItemClass even though that is not correct, for a specific item, it is not from the itemClass.

now i am sure that the problem is here, because i printed the value of the doesItemBelongToUserContextItemClass and it is always true but that is not correct, we are close, so just solving this will solve the question


Solution

  • Your query is big enough that I'm having a very hard time making sense of it, but my best guess is that you're running into a situation where your exists expression doesn't have all the variables bound that you need to make it test what you want it to test. Here's some very simple data:

    @prefix : <urn:ex:>
    
    :s a :D .
    :t a :E .
    

    Now, take a look at this query and the results:

    prefix : <urn:ex:>
    
    select * where {
      #-- Find an individual ?a and (one of)
      #-- the classes that it belongs to.
      ?a a ?aClass .
    
      optional {
        #-- Find an individual ?b and (one of)
        #-- the classes that it belongs to.
        ?b a ?bClass .
    
        #-- And bind ?isCommonClass to true or
        #-- false, to indicate whether ?b is
        #-- also an element of ?aClass.
        bind(exists{?b a ?aClass} as ?isCommonClass)
      }
    }
    
    ---------------------------------------------
    | a  | aClass | b  | bClass | isCommonClass |
    =============================================
    | :s | :D     | :s | :D     | true          |
    | :s | :D     | :t | :E     | true          |
    | :t | :E     | :s | :D     | true          |
    | :t | :E     | :t | :E     | true          |
    ---------------------------------------------
    

    ?isCommonClass is always true, even though it seems like it should be true when ?a and ?b are the same, and false otherwise. I think that what's happening here is that the bind gets evaluated in a context where either ?b or ?aClass isn't set yet, so the exists is actually checking for something more general. We can test this by moving the bind outside of the optional:

    select * where {
      ?a a ?aClass .
    
      optional {
        ?b a ?bClass .
      }
    
      bind(exists{?b a ?aClass} as ?isCommonClass)
    }
    
    ---------------------------------------------
    | a  | aClass | b  | bClass | isCommonClass |
    =============================================
    | :s | :D     | :s | :D     | true          |
    | :s | :D     | :t | :E     | false         |
    | :t | :E     | :s | :D     | false         |
    | :t | :E     | :t | :E     | true          |
    ---------------------------------------------
    

    Here, we get the results we'd expect, with ?isCommonClass being true exactly when ?a and ?b are the same.

    The query snippet in the question doesn't provide enough to be sure that this is what's happening, and the query you provided in the comments is too big for anyone else to check, but this seems like a very good candidate for what's going on in your situation.