Search code examples
sparqlrdf

SPARQL: find objects with all sub-objects matching a criteria


Given this data, where each person may optionally have a "smart" predicate, and each department may have zero or more people, I need to find departments that contain only the smart people. The result should only include departments 1 and 2. Ideally, the result should also include the "smart" objects for each department. Thanks!

person:A  p:type  'p' ;
          p:smart 'yes' .
person:B  p:type  'p' ;
          p:smart 'maybe' .
person:C  p:type  'p' .

department:1   p:type 'd' ;
               p:has  person:A, person:B .
department:2   p:type 'd' ;
               p:has  person:B .
department:3   p:type 'd' ;
               p:has  person:B, person:C .
department:4   p:type 'd' .

Solution

  • I have a feeling I've answered something similar before, but anyway there is a reasonably nice way to do this:

    select ?dept 
       (count(?person) as ?pc) (count(?smart) as ?sc)
       (group_concat(?smart; separator=',') as ?smarts)
    {
        ?dept p:has ?person .
        optional { ?person p:smart ?smart }
    }
    group by ?dept
    having (?pc = ?sc)
    

    That is: find the departments, people, and (where available) smart value. For each department find ones where the number of people matches the number of smart values.

    -------------------------------------------------------------
    | dept                              | pc | sc | smarts      |
    =============================================================
    | <http://example.com/department#2> | 1  | 1  | "maybe"     |
    | <http://example.com/department#1> | 2  | 2  | "yes,maybe" |
    -------------------------------------------------------------
    

    When you want to get results for each object, matching some criteria, group by / having is often the cleanest answer (in that you can separate out matching from filtering).