An OWL ontology may have classes A, B, and C, and axiom (in DL notation):
A ⊑ (B ⊓ C)
or in approximate Manches
It sounds like you've got a class that is a subclass of some intersection class. E.g., you might have
Student ⊑ Person ⊓ enrolledIn some Course
In the Protégé OWL ontology editor, this would look like:
If you write a SPARQL query for subclasses, e.g.,
prefix rdfs:
select ?subclass ?superclass where {
?subclass rdfs:subClassOf ?superclass
}
and you don't have a reasoner inferring additional data, you won't see Student as a subclass in your results, but you might see an blank (anonymous) node:
---------------------------------------------------------
| subclass | superclass |
=========================================================
| | _:b0 |
---------------------------------------------------------
To understand why this is the case, you need to take a look at the RDF serialization of the ontology. In this case, it's (in RDF/XML):
Or in the more human-readable Turtle (which is also more like the SPARQL query syntax):
@prefix : .
@prefix rdfs: .
@prefix owl: .
@prefix xsd: .
@prefix rdf: .
:Student a owl:Class ;
rdfs:subClassOf [ a owl:Class ;
owl:intersectionOf ( :Person [ a owl:Restriction ;
owl:onProperty :enrolledIn ;
owl:someValuesFrom :Course
] )
] .
:Person a owl:Class .
:enrolledIn a owl:ObjectProperty .
:Course a owl:Class .
a owl:Ontology .
There is, in fact, a Student rdfs:subClassOf [ ... ]
triple in the data, but the [ ... ]
is a blank node; it's an anonymous owl:Class
that is the intersection of some other classes. A reasoner would be able to tell you that if X ⊑ (Y and Z) then X ⊑ Y and X ⊑ Z, but a SPARQL query on its own won't do that. You could make a more complex SPARQL query like this that would, though:
prefix rdfs:
prefix owl:
prefix rdf:
select ?subclass ?superclass where {
{ ?subclass rdfs:subClassOf ?superclass }
union
{ ?subclass rdfs:subClassOf [ owl:intersectionOf [ rdf:rest* [ rdf:first ?superclass ] ] ] }
}
--------------------------------------------------------------------------------------
| subclass | superclass |
======================================================================================
| | _:b0 |
| | |
| | _:b1 |
--------------------------------------------------------------------------------------
The two blank nodes are the anonymous intersection class, and the anonymous restriction class (enrolledIn some Course). If you only want IRI results, you can use a filter
:
prefix rdfs:
prefix owl:
prefix rdf:
select ?subclass ?superclass where {
{ ?subclass rdfs:subClassOf ?superclass }
union
{ ?subclass rdfs:subClassOf [ owl:intersectionOf [ rdf:rest* [ rdf:first ?superclass ] ] ] }
filter( isIRI( ?superclass ) )
}
--------------------------------------------------------------------------------------
| subclass | superclass |
======================================================================================
| | |
--------------------------------------------------------------------------------------
Now, as final touch, if you want to make your query a bit smaller, since the only difference in those two union
ed patterns is the path that connects ?subclass
and ?superclass
, you can actually write this with just one property path. (Although, as noted in Sparql query Subclass or EquivalentTo, you might run into some issues with Protégé if you do this.) The idea is that you can rewrite this:
{ ?subclass rdfs:subClassOf ?superclass }
union
{ ?subclass rdfs:subClassOf [ owl:intersectionOf [ rdf:rest* [ rdf:first ?superclass ] ] ] }
as this, by using property paths, which also removes the need for the blank nodes:
?subclass ( rdfs:subClassOf |
( rdfs:subClassOf / owl:intersectionOf / rdf:rest* / rdf:first ) ) ?superclass
and you can simplify that a bit more to
?subclass rdfs:subClassOf/((owl:intersectionOf/rdf:rest*/rdf:first)+) ?superclass
and you can even remove one level of parentheses from that, to make it
?subclass rdfs:subClassOf/(owl:intersectionOf/rdf:rest*/rdf:first)+ ?superclass
but then you'd have to start remembering the precedence rules, and that's not much fun. The query works though:
prefix rdfs:
prefix owl:
prefix rdf:
select ?subclass ?superclass where {
?subclass rdfs:subClassOf/(owl:intersectionOf/rdf:rest*/rdf:first)+ ?superclass
filter( isIRI( ?superclass ) )
}
--------------------------------------------------------------------------------------
| subclass | superclass |
======================================================================================
| | |
--------------------------------------------------------------------------------------