sparqlrdfowl

How do I query for the classes that make up an owl:unionOf?


I've got an ontology with properties whose domains are unions of classes. Given such a property I would like to be able to find those classes that are in the domain, but haven't been able to figure out how to do that.

Here's some sample data:

@prefix : http://example.com/forso/ .
@prefix forso: http://example.com/forso/ .
@prefix owl: http://www.w3.org/2002/07/owl# .
@prefix rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns# .
@prefix xml: http://www.w3.org/XML/1998/namespace .
@prefix xsd: http://www.w3.org/2001/XMLSchema# .
@prefix rdfs: http://www.w3.org/2000/01/rdf-schema# .
@base http://example.com/forso/ .
 
http://example.com/forso/ rdf:type owl:Ontology .

forso:TypeA rdf:type owl:Class .
forso:TypeB rdf:type owl:Class .
 
forso:PropertyDomain
    rdf:type owl:Class ;
    owl:equivalentClass [
        rdf:type owl:Class ;
        owl:unionOf ( forso:TypeA forso:TypeB )
    ] .
 
forso:TheProperty
    rdf:type owl:ObjectProperty ;
    rdf:type owl:FunctionalProperty , owl:AsymmetricProperty ;
    rdfs:domain forso:PropertyDomain .

I can get the domain of forso:TheProperty with

SELECT ?domain WHERE { 
  forso:TheProperty rdfs:domain ?domain .
}

from which I get forso:PropertyDomain. What I want is to then take forso:PropertyDomain and get forso:TypeA and forso:TypeB.

(In my actual use case, I'd like to use anonymous classes without an explicit forso:PropertyDomain, but if I can't that's ok).

As I understand it, forso:TypeA and forso:TypeB should be subclasses of forso:PropertyDomain, so I was expecting

SELECT * WHERE {
  ?cls rdfs:subClassOf forso:PropertyDomain .
}

to return forso:TypeA and forso:TypeB, but instead it returns nothing. Replacing rdfs:subClassOf with rdfs:subclassOf* returns only forso:PropertyDomain.

EDIT: And of course immediately after posting I figure it out:

SELECT ?cls WHERE {
  forso:TheProperty rdfs:domain ?domain .
  ?domain owl:equivalentClass [ 
    rdf:type owl:Class ;
    owl:unionOf [
      rdf:rest*/rdf:first ?cls 
    ]
  ] .
}

But that doesn't seem to work with anonymous classes.


Solution

  • The approach in your edit, which I'd compress a bit as:

    SELECT ?cls WHERE {
      :property rdfs:domain/owl:equivalentClass/owl:unionOf/rdf:rest\*/rdf:first ?cls
    }
    

    seems right for your sample data. When you say

    But that doesn't seem to work with anonymous classes.

    I'm guessing you mean when you have data like:

    :property rdfs:domain [ a owl:Class ; owl:unionOf ( :a : b ) ]
    

    then you'd need to drop the owl:equivalentClass part of that query. With property paths, you should be able to do something like:

    :property rdfs:domain/owl:equivalentClass*/owl:unionOf/rdf:rest*/rdf:first ?cls
    

    I.e., by using owl:equivalentClass* instead of just owl:equivalentClass, you can follow zero-or-more owl:equivalentClass triples, thereby handling the case that the domain is an anonymous node with an owl:unionOf property.