First, a couple of notes about your SPARQL query are in order. The first regards the difference between *
and +
in a property path, and the second regards the use of filter
and of values
. Then we can look at how to query for different types of subclass/superclass relationships from the data. The trick here is that some of the relationships that we're looking for are relationship that we would typically use an OWL reasoner for, but we're trying to do a bit of that OWL reasoning using SPARQL
A few notes about the original query
Property Paths, +
and *
Note that in the other question that you linked to, the property path uses the *
operator, which means a path of length zero or more. The path of length zero or more can be pretty important, because if you don't have an explicit triple in your data of the form
:MyClass owl:equivalentClass :MyClass
you won't get any matches for a query
?myClass owl:equivalentClass+ :MyClass
but you will get a result (:MyClass
) for
?myClass owl:equivalentClass* :MyClass
Actually, even though owl:equivalentClass
is a symmetric property (i.e., from a owl:equivalentClass b
we can infer b owl:equivalentClass a
), the triple might be present in only one direction in the data, so we'd actually need
?myClass (owl:equivalentClass|^owl:equivalentClass)* :MyClass
Using values
instead of filter
As an aside, note that in this query I used the IRI in the pattern directly; there's no need to filter
as in your original code:
filter(?s=<http://snomed.info/id/323283001>)
If you do want to bind a variable to the superclass, it's easier to do it with values
, as in
values ?superclass { <http://snomed.info/id/323283001> }
?subclass (rdfs:subClassOf|owl:equivalentClass)* ?superclass
OWL Reasoning through SPARQL queries
In general, the relationships between classes (e.g., subclass and superclass relationships) are things that you'd use an OWL reasoner to determine for you. However, some are simple enough and present in the RDF encoding of the OWL axioms that you can draw the same conclusions using a SPARQL query. For instance, if you want to find subclass relationships in a hierarchy based on rdfs:subClassOf
and owl:equivalentClass
, you can use a pattern like this one:
?subclass (rdfs:subClassOf|owl:equivalentClass|^owl:equivalentClass)* ?superclass
Now, as noted below, you may run into some issues with Protégé's SPARQL tab, so I'd suggest that you keep the |
usage to the binary case, so you'd actually write, in this case:
?subclass (rdfs:subClassOf|(owl:equivalentClass|^owl:equivalentClass))* ?superclass
Now, the data that you're actually looking at uses more complicated class expressions. When you have an OWL axiom of the form
A subClassOf (B and C)
what you're actually saying is that there is a class A, and there is a (typically anonymous) class that is an intersection class, and that it is the intersection of B and C. You don't have the axioms
A subClassOf B
A subClassOf C
available to you, although they do logically follow. The case you've got actually includes an existential restriction as well, along the lines of
A subClassOf (B and C and (p some D))
It's important to note that A's superclasses here are B, C, and (p some D). In particular, A is not a subclass of p or D. This might be easier to see with a concrete example:
TiredManWearingHat subClassOf (Man and TiredPerson and (wearing some Hat)
A tired man wearing a hat is clearly a man, is clearly a tired person, and is clearly something that is wearing a hat, but he is most certainly not a wearing (which doesn't even make sense), and he's certainly not a Hat. Here's an minimal ontology that has exactly that structure that we can work with:
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns="https://stackoverflow.com/q/21092246/1281433/data.owl#">
<owl:Ontology rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl"/>
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#C"/>
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#B"/>
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#A">
<rdfs:subClassOf>
<owl:Class>
<owl:intersectionOf rdf:parseType="Collection">
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#B"/>
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#C"/>
<owl:Restriction>
<owl:onProperty>
<owl:ObjectProperty rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#p"/>
</owl:onProperty>
<owl:someValuesFrom>
<owl:Class rdf:about="https://stackoverflow.com/q/21092246/1281433/data.owl#D"/>
</owl:someValuesFrom>
</owl:Restriction>
</owl:intersectionOf>
</owl:Class>
</rdfs:subClassOf>
</owl:Class>
</rdf:RDF>
I've already written an answer to Retrieving superclasses implied by OWL intersection classes that described how you can write a query about intersection classes, so I won't explain all of it here again, but I'll include a query that works for this case. The result I'm showing are what I got using Jena's command line SPARQL query tools.
prefix : <https://stackoverflow.com/q/21092246/1281433/data.owl#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix owl: <http://www.w3.org/2002/07/owl#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
select ?superclass where {
:A (rdfs:subClassOf|(owl:intersectionOf/rdf:rest*/rdf:first))* ?superclass .
}
--------------
| superclass |
==============
| :A |
| _:b0 |
| :B |
| :C |
| _:b1 |
--------------
Now, the blank nodes there are the anonymous intersection class and the anonymous restriction class. If you don't want to include them in the results, you can filter them out pretty easily:
prefix : <https://stackoverflow.com/q/21092246/1281433/data.owl#>
prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#>
prefix owl: <http://www.w3.org/2002/07/owl#>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
select ?superclass where {
:A (rdfs:subClassOf|(owl:intersectionOf/rdf:rest*/rdf:first))* ?superclass .
filter(!isBlank(?superclass))
}
--------------
| superclass |
==============
| :A |
| :B |
| :C |
--------------
You can add ^owl:equivalentClass|owl:equivalentClass
into that property path as well, if you want to follow equivalent classes, too.
Doing this in Protégé
As I said, the results above were using Jena's command line tools. You said you wanted to do this in Protégé, and that makes things a bit harder, because it seems to introduce some problems. The last query I showed only produces A and B in Protégé; it doesn't include C for some reason:
However, the treatment of blank nodes is a bit better (i.e., we can remove the filter
and get some useful output):
I'm not sure how to get the same output that Jena gives in Protégé, unfortunately, but I'm emailing their mailing list about it.