Search code examples
sparqlrdfsemantic-web

A SPARQL query to return entities that satisfy some of the conditions


I have a requirement to write a SPARQL query to return those entities that satisfy a fixed number of conditions (not always all of them). The idea is that I need to get all those entities that satisfy say 2 out of the 4 conditions provided. I am writing the following query

SELECT ?ent WHERE {
    BIND(0 as ?cnt).
    OPTIONAL {
       ?ent ns:age ?age.
       FILTER(?age > 20 && ?age <= 40).
       BIND(?cnt + 1, ?cnt).
    }.
    OPTIONAL {
        ?ent ns:livesIn nst:Mauritius.
        BIND(?cnt + 1, ?cnt).
    }.
    OPTIONAL {
        ?ent ns:maritalStatus nst:Married.
        BIND(?cnt + 1, ?cnt).
    }.
    OPTIONAL {
        ?ent ns:fatherIs/ns:age ?fatherAge.
        FILTER(?fatherAge > 55 && ?fatherAge <= 80).
        BIND(?cnt + 1, ?cnt).
    }
} HAVING(?cnt > 2)

Am I going about the problem in the correct way? Am I missing out on something? Is there a better way to solve my problem?

EDIT 1: The above mentioned query gave me an error. Now I am trying with this

SELECT ?ent ?cnt WHERE {
    BIND(0 as ?cnt1).
    BIND(0 as ?cnt2).
    BIND(0 as ?cnt3).
    BIND(0 as ?cnt4).
    OPTIONAL {
       ?ent ns:age ?age.
       FILTER(?age > 20 && ?age <= 40).
       BIND(1 as ?cnt1).
    }.
    OPTIONAL {
        ?ent ns:livesIn nst:Mauritius.
        BIND(1 as ?cnt2).
    }.
    OPTIONAL {
        ?ent ns:maritalStatus nst:Married.
        BIND(1 as ?cnt3).
    }.
    OPTIONAL {
        ?ent ns:fatherIs/ns:age ?fatherAge.
        FILTER(?fatherAge > 55 && ?fatherAge <= 80).
        BIND(1 as ?cnt4).
    }
    BIND((?cnt1 + ?cnt2 + ?cnt3 + ?cnt4) as ?cnt)
} ORDER BY DESC(?cnt)

This query returns a value of 0 under ?cnt for all records


Solution

  • I'd suggest the following naive approach.

    SELECT ?ent ?cnt WHERE {
        ?ent a foaf:Person .
        OPTIONAL {
            ?ent ns:age ?age.
            FILTER(?age > 20 && ?age <= 40).
        }
        OPTIONAL {
            ?ent ns:livesIn ?livesIn.
            FILTER (?livesIn = nst:Mauritius)
        }
        OPTIONAL {
            ?ent ns:maritalStatus ?maritalStatus.
            FILTER (?maritalStatus = nst:Married).
        }
        OPTIONAL {
            ?ent ns:fatherIs/ns:age ?fatherAge.
            FILTER(?fatherAge > 55 && ?fatherAge <= 80).
        }
        BIND(xsd:integer(bound(?age)) +
             xsd:integer(bound(?livesIn)) +
             xsd:integer(bound(?maritalStatus)) +
             xsd:integer(bound(?fatherAge))
             AS ?cnt)
        FILTER (?cnt > 2)
    } ORDER BY DESC(?cnt)
    

    There are two problems with your recent query:

    • External BINDs override internal ones (or rather there is no join between them).
    • You're starting with OPTIONAL (see In SPARQL, order matters).