Search code examples
sparqlrdfwikidata

SPARQL - Return mutual objects of a list of subjects


How can i get all predicates + objects, which are shared by a list of subjects - without knowing anything about the predicates/objects of these subjects?
Let's look at this example query from Wikidata:

SELECT ?chancellor WHERE{
    ?chancellor wdt:P39 wd:Q4970706. #P39 = position held, Q4970706 = Chancellor of Germany
}

Link to this query.

This query returns all former chancellors of germany.
Now i want to return every predicate + object, which every chancellor has in common e.g. every of the subjects is an instance of human, is born in Germany and whatever.
I guess this is an easy one. However i have no idea.


Solution

  • This is a good one. Here's a near-hit:

    prefix wdt: <http://www.wikidata.org/prop/direct/>
    prefix wd: <http://www.wikidata.org/entity/>
    
    select ?p ?o (count(distinct ?chancellor) as ?cs) where {
        ?chancellor wdt:P39 wd:Q4970706.  
        ?chancellor ?p ?o .
    }
    group by ?p ?o
    order by desc(?cs)
    

    Link to query

    This takes all chancellors, and their properties and values. It counts the number of chancellors per prop/val.

    By ordering that you can see the most common prop / vals at the top.

    Now what you want is the only the results for all chancellors. We can get the number of chancellors in one query easily enough, and stick the two together:

    prefix wdt: <http://www.wikidata.org/prop/direct/>
    prefix wd: <http://www.wikidata.org/entity/>
    
    select ?p ?o where {
       {
           # Find number of chancellors 
           select (count(?chancellor) as ?num_chancellors) where { 
               ?chancellor wdt:P39 wd:Q4970706
           }
       }
       {
           # Find number of chancellors per predicate / value
           select ?p ?o (count(distinct ?chancellor) as ?chancellor_count) where {
               ?chancellor wdt:P39 wd:Q4970706.  
               ?chancellor ?p ?o .
           }
           group by ?p ?o 
       }
       # Choose results all chancellors share 
       filter (?num_chancellors = ?chancellor_count)
    }
    

    Link to query.

    I think this does what you want. Not very pretty, I confess.