Search code examples
sparqlrdfowlontology

Narrowing down on SPARQL query


I want to make a query that returns only the items where person1(David) and person2(Charles) appears together.

My query is:

SELECT ?item ?par ?mod ?hon ?firstName ?lastName
WHERE {
    ?item   sci:itemTitle ?title.
    {?item sci:hasParticipant ?par. {?par sci:firstName "Charles".}UNION{?par sci:firstName "David".}} 
    UNION {?item sci:hasModerator ?mod. {?mod sci:firstName "Charles".}UNION{?mod sci:firstName "David".}} 
    UNION {?item sci:hasGuestOfHonor ?hon. {?hon sci:firstName "Charles".}UNION{?hon sci:firstName "David".}}
}

My result is shown here:

results

There are only two occurrences where person1 and person2 appear together (Programme Item 4 and 6). How can I narrow down the query to get what I want or is there a better way to do it than what I did?


Solution

  • union is for expressing alternatives. Each time you say { x } union { y }, you're asking for matches whether either x or y (or both) hold. Since you're looking for cases where both people are involved (not either person), you don't want to use union. As I understand it, you have three ways that a person can part of an item: they can be a participant, a moderator, or a guest of honor. You can condense that into:

    ?item sci:hasModerator|sci:hasParticipant|sci:hasGuestofHonor ?person
    

    The pattern ?a x|y|z ?b says that ?a is connected to ?b by either x, y, or z. It's an alternation path. You can read more about property paths in the SPARQL 1.1 spec, §9 Property Paths.

    Then, you want there to be two people, so:

    ?item sci:hasModerator|sci:hasParticipant|sci:hasGuestofHonor ?person1, ?person2
    

    will get you two people (or possibly the same person, but when we check for names, we'll enforce that they have the names we want, which should require them to be different people, or at least a single person with two first names). The pattern ?a p ?b, ?c says that ?a is related by p to both ?b and ?c.

    Finally, you want one person to have the first name Charles and the other David, so we just need to add

    ?person1 sci:firstName "Charles" .
    ?person2 sci:firstName "David" .
    

    Since you're not actually using the values of ?person1 and ?person2 for anything else, you can also just use a blank node here. E.g.,

    ?item sci:hasModerator|sci:hasParticipant|sci:hasGuestofHonor [ sci:firstName "Charles" ] ,  [ sci:firstName "David" ] .
    

    Let's see how it all fits together. I created some sample data based on the results you showed. Obviously, it's not the same as your data, but it's got enough to reproduce the results that you showed, and to demonstrate how this works.

    prefix sci: <urn:ex:>
    
    sci:Male_Person2 sci:firstName "David" .
    sci:Male_Person3 sci:firstName "Charles" .
    
    sci:ProgrammeItem1 sci:itemTitle "" ;
                       sci:hasParticipant sci:Male_Person3 ;
                       sci:hasModerator sci:Male_Person3 .
    sci:ProgrammeItem3 sci:itemTitle "" ;
                       sci:hasParticipant sci:Male_Person2 ;
                       sci:hasModerator sci:Male_Person2 .
    sci:ProgrammeItem4 sci:itemTitle "" ;
                       sci:hasParticipant sci:Male_Person3 ;
                       sci:hasModerator sci:Male_Person2 .
    sci:ProgrammeItem6 sci:itemTitle "" ;
                       sci:hasParticipant sci:Male_Person2, sci:Male_Person3 .
    
    prefix sci: <urn:ex:>
    
    select ?item {
        ?item sci:itemTitle ?title .
        ?item sci:hasModerator|sci:hasParticipant|sci:hasGuestofHonor
          [ sci:firstName "Charles" ] ,
          [ sci:firstName "David" ] .
    }
    
    ----------------------
    | item               |
    ======================
    | sci:ProgrammeItem4 |
    | sci:ProgrammeItem6 |
    ----------------------