1

I'm trying to build a SPARQL query that gets the students of one teacher and finds all the teachers with all those students (they can have more students).

This is what I have so far:

SELECT ?otherTeacher
WHERE {
VALUES ?teacher {$teacher}
  ?teacher hasStudent ?student .
  ?otherTeacher hasStudent ?student .
  FILTER(?teacher <> ?otherTeacher)
}

Here are the expected cases with the below data:
- If teacher1 is given, only teacher3 should show up (teacher3 teaches all the students that teacher1 teaches).
- If teacher2 is given, both teacher1 and teacher3 should show up (both teacher1 and teacher3 teach all the students teacher2 teaches).
- If teacher3 is given, no teachers should show up (no other teacher teaches all the students teacher3 teaches).

<teacher1> <hasStudent> "Alice" .
<teacher1> <hasStudent> "Bob" .
<teacher1> <hasStudent> "Charlie" .
<teacher2> <hasStudent> "Alice" .
<teacher2> <hasStudent> "Dan" .
<teacher3> <hasStudent> "Alice" .
<teacher3> <hasStudent> "Bob" .
<teacher3> <hasStudent> "Charlie" .
<teacher3> <hasStudent> "Dan" .

How do I add a requirement that ?otherTeacher has all the students that ?teacher has (that the new set contains at least all the elements of the original set)?

Anthony T
  • 23
  • 4
  • Is one teacher given by you, or not? – UninformedUser Nov 17 '17 at 15:02
  • Yes, one teacher is given. If teacher1 is given, only teacher3 should show up. If teacher2 is given, both teacher1 and teacher3 should show up. If teacher3 is given, no teachers should show up. – Anthony T Nov 17 '17 at 19:06

2 Answers2

4

If one teacher is given, you could try this query:

SELECT DISTINCT ?otherTeacher
WHERE {
VALUES ?teacher {<teacher1> }
  ?otherTeacher <hasStudent> ?student .
  FILTER(?teacher != ?otherTeacher)
  FILTER NOT EXISTS {
    ?teacher <hasStudent> ?s . 
    FILTER NOT EXISTS {?otherTeacher <hasStudent> ?s .}
  }
}

It uses "double-negation", i.e. it checks that there exists no student of the given teacher that is not taught by the other teacher.

UninformedUser
  • 8,397
  • 1
  • 14
  • 23
  • I'm running the query and I get back both teacher2 and teacher3. The teacher3 is right, but teacher2 does not teach all the students that teacher1 teaches. – Anthony T Nov 17 '17 at 19:12
  • @AnthonyT, in GraphDB 8.3, an analogous query returns `teacher3` only. – Stanislav Kralin Nov 17 '17 at 20:39
  • @AnthonyT I used Apache Jena on your sample data and it works as expected. Which triple store do you use? The query should be correct, we had this problem here several times, and Stanislav Kralin and I always post the same two solutions: double negation or multiple COUNT queries. – UninformedUser Nov 19 '17 at 12:33
  • I'm using the MarkLogic triple store, which should follow the same W3C standards. I'll go through their help system and maybe they can get me a better answer, since it seems to be an issue with just their system. I'll mark your answer as correct - thanks for all the help! – Anthony T Nov 21 '17 at 19:00
1

Not an elegant solution since it involves nesting multiple queries. There should be a better way of solving this problem.

This solution uses a nested query to get the maximum of the counts.
Line 13 is to only include students for otherTeacher that teacher also teaches (to remove students like Dan from the count).

select distinct ?otherTeacher
where{
  {
    select (max(?count) as ?max)
      where{
        {
          SELECT DISTINCT ?otherTeacher (COUNT(?student) as ?count)
          WHERE {
          VALUES ?teacher {<teacher1> }
            ?otherTeacher <hasStudent> ?student .
            FILTER(?teacher != ?otherTeacher)
            FILTER EXISTS {
              ?teacher <hasStudent> ?student . 
            }
          }
          group by ?otherTeacher
       }
    }
  }
  {
    SELECT DISTINCT ?otherTeacher (COUNT(?student) as ?count)
    WHERE {
    VALUES ?teacher {<teacher1> }
      ?otherTeacher <hasStudent> ?student .
      FILTER(?teacher != ?otherTeacher)
      FILTER EXISTS {
        ?teacher <hasStudent> ?student . 
      }
    }
    group by ?otherTeacher
  }
  filter(?count >= ?max) # epsilon error/no match if it's equal?
}
Anthony T
  • 23
  • 4