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

52 views Asked by At

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?

2

There are 2 answers

0
Daraan On BEST ANSWER

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 . 
   } 
}
2
Bergi On

Indeed, you are selecting all the things that are computers and all the things that are accessories, given that they are not linked.

What you want instead is to select all the things that are accessories and the things they are linked to, given that those are not computers. So flip the conditions:

SELECT ?non-computer ?acc WHERE {
  ?acc a/rdfs:subClassOf* :Accessories .
  ?non-computer :hasAccessories ?acc .
  FILTER NOT EXISTS {
    ?non-computer a/rdfs:subClassOf* :Computer
  }
}

You may not actually need to check whether ?acc is an accessory (ie. belongs to a subclass of :Accessories), being the object in a :hasAccessories tuple should be sufficient.