semantic-graphql icon indicating copy to clipboard operation
semantic-graphql copied to clipboard

Add support for owl:unionOf when resolving rdfs:range on an owl:ObjectProperty

Open Astn opened this issue 8 years ago • 11 comments

When using ontologies such as the OWL compliant version of schema.org created by topbraid (http://topbraid.org/schema) @ http://topbraid.org/schema/schema.ttl

It is common to encounter a union of resources as the object of rdfs:range. Example:

###  http://schema.org/acquiredFrom
<http://schema.org/acquiredFrom> rdf:type owl:ObjectProperty ;
                                 rdfs:domain <http://schema.org/OwnershipInfo> ;
                                 rdfs:range [ rdf:type owl:Class ;
                                              owl:unionOf ( <http://schema.org/Organization>
                                                            <http://schema.org/Person>
                                                          )
                                            ] ;
                                 rdfs:comment "The organization or person from which the product was acquired."^^xsd:string ;
                                 rdfs:label "acquired from"^^xsd:string .

I propose that we handle this case by adding all of the resources found in the owl:unionOf to a GraphQLUnionType and return that from the call to getGraphqlPolymorphicObjectType.

I have been working on a solution for this particular issue, and should be able to submit a pull request for review shortly.

Astn avatar Feb 23 '17 15:02 Astn

re @Astn, there is a major difference between a GraphQLUnionType (badly named, it returns only one type, the resource must belong to either of the classes), and a owl:unionOf class (which is many classes at the same time, the resource must belong to all the classes).

Also, what about an interface ?

dherault avatar Feb 23 '17 15:02 dherault

Mmm my bad it seems I'm wrong about owl:unionOf

dherault avatar Feb 23 '17 15:02 dherault

Ok owl:unionOf is a logical disjunction so it could be either or both classes

dherault avatar Feb 23 '17 15:02 dherault

A GraphQLUnion type would handle the "either" case, but not if it is "both". A GraphQLInterface type would handle fragments on "both", but we'd still have to resolve only one type (because GraphQL requires a single type) for a particular resource.

Let's say a field returns an owl:unionOf(Dog, Cat) and a user queries a resource that is both a Dog and a Cat (whatever) using a fragment on Dog and another one on Cat, for that resource we would have to resolve only one type (Dog or Cat) not both, and only one fragment would be collected.

This is a typical case where GraphQL and Semantic data do not mix well.

dherault avatar Feb 23 '17 15:02 dherault

I've been thinking about a fix that would solve many GraphQL vs Semantic conflicts like this one : it would be to add some magic into the resolveType function, by introspecting the AST and knowing what the user wants. Does it need a Dog ? Then return 'Dog' as the type. A Cat ? 'Cat'. A Dog and a Cat ? Return a special DogCat GraphQL type.

dherault avatar Feb 23 '17 15:02 dherault

From the brief testing I was doing earlier, this case is already handled O.K. I'm sure it could be better. Likely is the case that anything can be an owlThing. So is there a way for us to create a fragment on an objectProperty that has a rdfs:range of owlThing? My currently implementation does not allow such a thing.

But so far, I am able to formulate queries such as:

query {
  foo(bar:"bar"){
    __typename
    ... on I_sdo_Action {
      startTime
      agent {
        ... on sdo_Person {
          url
        }
      }
    }
    ... on sdo_FollowAction {
      followee {
        ... on I_sdo_Thing {
          url
        }
      }
    }
    ... on sdo_MarryAction {
      object {
        ... on I_sdo_Thing {
          url
        }
      }
    }
  }
}

Astn avatar Feb 23 '17 16:02 Astn

So it appears that if the components of a Union implement a common interface, then you are able to pull the common fields from it right now. Not sure of what to do when some Thing is both a Cat and a Dog.

Astn avatar Feb 23 '17 16:02 Astn

Yes the "both" case is still missing in the GraphQLUnion type solution. But the "both" case is the essence of owl:unionOf. Otherwise simply listing the classes instead of using an owl:unionOf would do.

dherault avatar Feb 23 '17 16:02 dherault

Do you know any way that graphql supports some kind of a Both case? All I can think of at the moment, which I think you suggested earlier would be to generate another type that represented the logical disjunction. But that then still has the issue that derived types or more specific types are also allowed.

So in the case of

###  http://schema.org/acquiredFrom
<http://schema.org/acquiredFrom> rdf:type owl:ObjectProperty ;
                                 rdfs:domain <http://schema.org/OwnershipInfo> ;
                                 rdfs:range [ rdf:type owl:Class ;
                                              owl:unionOf ( <http://schema.org/Organization>
                                                            <http://schema.org/Person>
                                                          )
                                            ] ;
                                 rdfs:comment "The organization or person from which the product was acquired."^^xsd:string ;
                                 rdfs:label "acquired from"^^xsd:string .

The resource returned as the object of http://schema.org/aquiredFrom could be a schema:CafeOrCoffeeShop because

schema:CafeOrCoffeeShop subClassOf schema:LocalBusiness subClassOf schema:Organization

schema:CafeOrCoffeeShop
  rdf:type owl:Class ;
  rdfs:comment "A cafe or coffee shop." ;
  rdfs:label "Cafe or coffee shop" ;
  rdfs:subClassOf schema:FoodEstablishment ;

schema:FoodEstablishment
  rdf:type owl:Class ;
  rdfs:comment "A food-related business." ;
  rdfs:label "Food establishment" ;
  rdfs:subClassOf schema:LocalBusiness ;

schema:LocalBusiness
  rdf:type owl:Class ;
  rdfs:comment "A particular physical business or branch of an organization. Examples of LocalBusiness include a restaurant, a particular branch of a restaurant chain, a branch of a bank, a medical practice, a club, a bowling alley, etc." ;
  rdfs:label "Local business" ;
  rdfs:subClassOf schema:Organization ;
  rdfs:subClassOf schema:Place ;

So that makes me conclude that in the simple case of

schema:error
  rdf:type owl:ObjectProperty ;
  rdfs:comment "For failed actions, more information on the cause of the failure." ;
  rdfs:domain schema:Action ;
  rdfs:label "error" ;
  rdfs:range owl:Thing ;

you should be able to formulate the following query because schema:APIReference is an owl:Thing

query {
   schemaActions {
      schemaerror {
         ... on schemaAPIReference {
            id
         }
      }
   }
}

So where do we go from here?

Astn avatar Feb 23 '17 16:02 Astn

Yes, you're right, that's why I wanted the "type" of fields to be GraphQLInterfaceType instead of GraphQLObjectType. This kind of fragments would then be available, without breaking changes. I never made the swich, it's on the TODO list thought.

dherault avatar Feb 23 '17 17:02 dherault

So I think we have two separate issues here.

  1. This one #3 = Handle unions in rdfs:range position.
  2. Need to define = How to handle subtypes or implementers of some interface.

I would still like to try to solve this #3 without tackling the Interface / subtype issue yet. Currently there is no support for unions, so IMO some support is a step forward.

Astn avatar Feb 23 '17 17:02 Astn