sparqlrdfturtle-rdfknowledge-graph

SPARQL return instances not linked to a set of classes - query returns all instances instead of only one


A small example ontology

@prefix : <http://example.org/> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .


:Product a owl:Class .
:Accessories rdfs:subClassOf :Product .
:hasAccessories a rdf:Property .
:hasAccessories  rdfs:domain :Product.
:hasAccessories  rdfs:range :Accessories.
 
:Mouse rdfs:subClassOf :Accessories.
:Keyboard rdfs:subClassOf :Accessories.

:Computer rdfs:subClassOf :Product .
:Desktop rdfs:subClassOf :Computer .
:Laptop rdfs:subClassOf :Computer .

:m1 a :Mouse .
:m2 a :Mouse .
:k1 a :Keyboard .
:k2 a :Keyboard .  # linked wrongly
:k3 a :Keyboard .  # linked not at all
:c1 a :Computer .
:c2 a :Computer .
:d1 a :Desktop .
:d2 a :Desktop .
:l1 a :Laptop .

:s a :Storage

:c1 :hasAccessories :m1 , :k1 .
:d1 :hasAccessories :m1 .
:d2 :hasAccessories :m2 .
:s :hasAccessories :k2 # <---- Note this is intentional bad design

I want to retrieve k2 as the only non-Computer-bound Accessory.

I do not have a reasoning so this query does not work:

SELECT DISTINCT ?a WHERE
{
  ?c a :Computer. 
  ?a a :Accessories . 
  FILTER NOT EXISTS {?c :hasAccessories ?a .}
}

Output: Empty


If not for the Storage s I could do this query to get all non-linked Accessories

SELECT DISTINCT ?c ?a WHERE
{  
  ?acc rdfs:subClassOf* :Accessories .
  ?a a ?acc . 
  FILTER NOT EXISTS {?c :hasAccessories ?a .}
}

Output: Empty


Going over the inheritances via this query does not filter out any of my results:

SELECT DISTINCT ?a WHERE
{ 
  ?computer rdfs:subClassOf* :Computer .
  ?c a ?computer. 
  
  ?acc rdfs:subClassOf* :Accessories .
  ?a a ?acc . 

  FILTER NOT EXISTS {?c :hasAccessories ?a .}
}

Output: k1, k2, m1, m2

I have a vague feeling why it happens, e.g. it would find the negative example l1 not hasAccessory k1, which puts k1 into the results. How do I need to adjust my query to only result in k2 without making use of the storage in anyway, i.e. check only for subClassesOf Computer?


Solution

  • Based on the comment by UninformedUser this works for the two special cases (unbound and not-bound by computer):

    SELECT DISTINCT ?a WHERE {    
       ?a a/rdfs:subClassOf* :Accessories .   
       FILTER NOT EXISTS {
             ?c a/rdfs:subClassOf* :Computer;
                :hasAccessories ?a . 
       } 
    }