Skip to content

Latest commit

 

History

History
528 lines (420 loc) · 20.5 KB

schema_mapping.md

File metadata and controls

528 lines (420 loc) · 20.5 KB

RDFS To GraphQl Schema Mapping

Attention: Deprecated version of the mapping

UyperGraphQL Schema Config Default Mapping Vocabulary
hgqls:object rdfs:Class
hgqls:field rdf:Property
hgqls:implements rdfs:subClassOf
hgqls:impliesField rdfs:subPropertyOf
hgqls:fieldObject rdfs:domain, owl:domainIncludes
hgqls:fieldOutputType rdfs:range, owl:rangeIncludes
hgqls:sameAs owl:sameAs, owl:equivalentProperty, owl:equivalentClass
hgqls:implementsMutually NOT used
hgqls:sharedOutputType NOT used

The table above describes the default mapping behavior. Using the vocabulary of the left column allows to define and modify the mapping behavior. In the config file a resource is assigned to a corresponding GraphQL feature with rdf:type. It is also possible to define multiple resources for one feature such as shown in the table for fieldObject and fieldOutputType. But defining multiple resources means that these resources are trated equally in the query process. For more details see the description of the corresponding feature mapping or in the Query handling section.

Note: The supported vocabulary or config names can change during the process of implementation. The defualt implementation will use the following mapping configuration:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix hgqls:   <http://hypergraphql.org/schema-mapping/>.
@prefix owl:  <http://www.w3.org/2002/07/owl#>.

rdfs:class a hgqls:object.
rdfs:predicate a hgqls:field.
rdfs:subClassOf a hgqls:implements.
rdfs:subPropertyOf a hgqls:impliedField.
rdfs:domain a hgqls:fieldObject.
owl:domainInclude a hgqls:fieldObject.
rdfs:range a hgqls:fieldOutputType.
owl:rangeIncludes a hgqls:fieldOutputType.
owl:equivalentClass a hgqls:sameAs.
owl:equivalentProperty a hgqls:sameAs.
owl:sameAs a hgqls:sameAs.

In the following this documentation uses the HGQLS config vocabulary to explain the meaning in the GraphQL schema. The image of the mapping is static only the preimage is configurable. Therefore the examples used in the documentation can also be used for a different schema vocabulary by simply changing the names of the default vocables. The semantic meaning of the HGQLS config vocabulary is close to the meaning of the default vocabulary. Changing the schema vocabulary requires that the new vocabulary has a similar meaning to ensure that the HGQL endpoint acts as anticipated. In the following examples the default vocabulary is used to explain the mapping and semantic meaning of the mapping image.

Note: Changing the Vocabulary to an vocabulary that differs vastly from the semantic meaning of the default vocabulary then it recomended to check wether the extraction query still extracts the desired schema.

Examples:

Naming Conventions

RDF data uses IRIs for resource names therefore the names used in the mapped schema also need to be unique. Due to the inconvinience in the usage of the full IRI we will use abriviations. A typical IRI has the following syntax namespace:name and will be converted to namespaceAbbriviation_name.

Note: GraphQL only supports Latin ASCII, therefore names must be converted to the supported character set. How this is done is shown in here

Mapping to ...

...objectType

The rdfs:Class is mapped to an objectType and an interfaceType in GraphQL. The intermediate step of mapping to the interface is necessary to support possible sub-classes of the class. To mark the interface as manually added the description of the interface contains the string autogenerated For example the following RDFS is mapped as follows.

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:Person rdf:type rdfs:Class.

Resulting GraphQl Schema: |

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
}

interface ex_Person  @service(id:"ex-sparql"){

}

type ex_Person implements ex_Person @service(id:"ex-sparql"){

}

|

...implements

If C1 is a subclass of C2 then rdfs:subClassOf is represented in GraphQl with the C1 implementing the interface of C2. This means that a Class inherits all fields of its super class. Any subclass is also a class and has therefore its own interface as described in section rdfs:Class.

Unfortunately GraphQL does not support interface inheritance otherwise only the interface would inherit.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:Person rdf:type rdfs:Class.
ex:Child  rdfs:subClassOf ex:Person.

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_Child:   _@href(iri: "http://example.org/Child")
}

interface ex_Person  @service(id:"ex-sparql"){

}

interface ex_Child @service(id:"ex-sparql"){

}

type ex_Person implements ex_Person @service(id:"ex-sparql"){

}

type ex_Child implements ex_Child & ex_Person @service(id:"ex-sparql"){

}

...field

The rdf:Property is mapped to an field in the Object of the properties domain. The range of the property will result in the type of the field. At the moment it is not defined what happens if a property is not completely defined with domain and range.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:drives rdf:type  rdf:Property;
          rdfs:range  ex:Car;
          rdfs:domain ex:Person.

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
}
interface ex_Person_Interface{
   ex_drives: [ex_Car]
}
type ex_Person implements ex_Person_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_Car] @service(id:"ex-sparql")
}

Multiple Domains

If a field has multiple field objects (property with multiple domains) then each of the refered objects has this field.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:drives rdf:type  rdf:Property;
          rdfs:range  ex:Car;
          rdfs:domain ex:Person;
          rdfs:domain ex:AI.

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_AI:  _@href(iri: "http://example.org/AI")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
}
interface ex_Person_Interface{
   ex_drives: [ex_Car]
}
interface ex_AI_Interface{
   ex_drives: [ex_Car]
}

type ex_Person implements ex_Person_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_Car] @service(id:"ex-sparql")
}
type ex_AI implements ex_AI_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_Car] @service(id:"ex-sparql")
}

Multiple Field Output Types

If a field has multiple output types then a interfaceType is created containing the intersection of the fields of all possible output types. The mapping to an interface with an intersection of the fields as minimum set of fields allows querying multiple types at the same time. If the types have NO fields in common then it is maps to an empty interface. The empty interface gets the standard UGQL fields _id and _type assigned so that this fields can be queried over all types. If type specific fields are required, then these can be queried with an InlineFragment defining to the specific type.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:drives rdf:type  rdf:Property;
          rdfs:range  ex:Car;
          rdfs:range ex:Bicycle;
          rdfs:domain ex:Person .

ex:color rdf:type rdf:Property;
         rdfs:domain ex:Car;
         rdfs:domain ex:Bicycle .

ex:saddle_height rdf:type rdf:Property;
         rdfs:domain ex:Bicycle .

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_AI:  _@href(iri: "http://example.org/AI")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
}
interface ex_drives_OutputType{
   ex_color: [String]
}
interface ex_Person_Interface{
   ex_drives: [ex_drives_Output]
}
interface ex_Car_Interface{
   ex_color: [String]
}
interface ex_Bicycle_Interface{
   ex_color: [String]
   ex_saddle_height: [String]
}

type ex_Person implements ex_Person_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_drives_Output] @service(id:"ex-sparql")
}

type ex_Car implements ex_Car_Interface & ex_drives_Output @service(id:"ex-sparql"){
    ex_color: [String] @service(id:"ex-sparql")
}

type ex_Bicycle implements ex_Bicycle & ex_drives_Output @service(id:"ex-sparql"){
    ex_color: [String] @service(id:"ex-sparql")
    ex_saddle_height: [String] @service(id:"ex-sparql")
}

Possible Query:

{
    ex_Person{
        ex_drives{
            ex_color
            ...on Bicycle{
                ex_saddle_height
            }
        }
    }
}

...implies Fields

Resources that are mapped to hgqls:impliesField must be a predicate with the field mapping as range and domain. It has the same semantic meaning as rdfs:subProperty If a property P1 is a subProperty of P2 then all resources that are linked by P1 are also linked by P2. To map this to GraphQl we simply add P2 as field to the object defined by the domian of P1. The type of the field is the range of P2. To mark the generated field P2 as manually generated we add a directive @autogenerated(impliedBy:[<P1>]) . If the directive and the field already exist then only P1 is added to the value list in the directive.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:controls rdf:type  rdf:Property;
            rdf:range ex:Vehicle;
            rdf:domain  ex:Person.

ex:drives rdfs:subPropertyOf  ex:controls;
          rdfs:range  ex:Car;
          rdfs:domain ex:Person.

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
}
interface ex_Person_Interface{
   ex_drives: [ex_Car]
   ex_controls: [ex_Vehicle]
}

type ex_Person implements ex_Person_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_Car] @service(id:"ex-sparql")
   ex_controls: [ex_Vehicle] @service(id:"ex-sparql") @autogenerated(impliedBy: ["drives"])
}

rdfs:label and rdfs:comment

Labels and comments are currently not included in the mapping. This section only shows a potential integration of those information into the mapped schema. Both properties provide additional information of schema resources. rdfs:label provides a human readable name of a resource's name and rdfs:comment links to an description of a resource. The human-readable information about schema resources may help developers to form their queries and it is therefore helpfull if these information are also provided in the GraphQl schema. Both properties are mapped to one description in GraphQl with the following format: <label>: <comment>

If one of the properties is missing a placeholder <missing label> or <missing comment> is used. It is recommended to use block strings to place the description in the schema to avoid trouble with white space, line terminators, quote, and backslash characters.

Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:Person rdf:type  rdfs:Class;
          rdfs:label  "Person";
          rdfs:comment  "A person is a human of any age."
ex:Child  rdfs:subClassOf ex:Person;
          rdfs:comment  "A child is person with the age between 0 and 18."
ex:rel rdf:type rdf:Property;
           rdfs:label "related";
           rdfs:comment "Describes which Persons are related to each other.";
           rdfs:domain  ex:Person;
           rdfs:range ex:Person.

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_Child:   _@href(iri: "http://example.org/Child")
   ex_rel:     _@href(iri: "http://example.org/rel")
}

"""autogenerated"""
interface ex_Person  @service(id:"ex-sparql"){
  """related: Describes which Persons are related to each other."""
  ex_rel: [ex_Person]
}

"""Person: A person is a human of any age."""
type ex_Person implements ex_Person @service(id:"ex-sparql"){
  """related: Describes which Persons are related to each other."""
  ex_rel: [ex_Person]
}

"""<missing label>: A person is a human of any age."""
type ex_Child implements ex_Person @service(id:"ex-sparql"){
  """related: Describes which Persons are related to each other."""
  ex_rel: [ex_Person]
}

...sameAs

The hgqls:sameAs allows to define a vocabulary for equivalence relations between schema entities. Equivalences between objects and between fields are here defined and internaly kept apart. Leading to the default vocabulary of owl:equivalentClass, owl:equivalentProperty and owl:sameAs as it is often misused for equivalence relations. An equivalentClass defines that two classes share the same intentional meaning and it does not mean that the classes are equal. To transfer this behavior to GraphQl we use the suggestion from the OWL definition.

$A \;\; owl\!:\!equivalentClass B \iff A \;\; rdfs\!:\!subClassOf \;\; B \; \land \; B \;\; rdfs\!:\!subClassOf \;\; A$

To realise equivalence in GraphQl we mark objects and fields with the directive @schema(sameAs:[<Resource>]) .

NOTE: The sameAs-property often links to URIs that are only accessible with forein SPARQL endpoints

The added directive ensures that during the query translation to SPARQL apart form the queried entity also all equivalent entities are queried.

Example

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix owl:  <http://www.w3.org/2002/07/owl#>.
@prefix ex:   <http://example.org/>.
@prefix foaf: <http://xmlns.com/foaf/0.1/>.

ex:Person rdf:type rdfs:Class;
         owl:equivalentClass foaf:Person.
ex:drives rdf:type  rdf:Property;
          rdfs:range  ex:Car;
          rdfs:domain ex:Person.
ex:rides rdf: rdf:Property;
         rdfs:range ex:Bicycle;
         dfs.domain ex:Person.
ex:drives owl:equivalantProperty ex:rides

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   foaf_Person _@href(iri: "http://xmlns.com/foaf/0.1/")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
   ex_rides:  _@href(iri: "http://example.org/rides")
}

interface ex_Person_Interface{
   ex_drives: [ex_drives_Output]
   ex_rides: [ex_rides_Output] 
}

interface foaf_Person_Interface{
   ex_drives: [ex_drives_Output]
   ex_rides: [ex_rides_Output] 
}

union ex_drives_Output = Car | Bicycle
union ex_rides_Output = Car | Bicycle

type ex_Person implements ex_Person_Interface & foaf_Person_Interface @schema(sameAs:"foaf_Person") @service(id:"ex-sparql"){
   ex_drives: [ex_drives_Output] @service(id:"ex-sparql") @schema(sameAs:"ex_rides")
   ex_rides: [ex_rides_Output] @service(id:"ex-sparql") @schema(sameAs:"ex_drives")
}

type foaf_Person implements foaf_Person_Interface & ex_Person_Interface @schema(sameAs:"ex_Person") @service(id:"ex-sparql"){
   ex_drives: [ex_drives_Output] @service(id:"ex-sparql") @schema(sameAs:"ex_rides")
   ex_rides: [ex_rides_Output] @service(id:"ex-sparql") @schema(sameAs:"ex_drives")
}

...implements mutually

This mapping feature is the same as the hgqls:sameAs relation between objects (classes in RDF if default vocabulary is used) but without the directive meaning that equivalences defined here lead not to an altered query translation.

This feature is not used in the default mapping

For the example of this feature the mapping configuration is altered to owl:equivalentClass a hgqls:implementsMutually Example

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix owl:  <http://www.w3.org/2002/07/owl#>.
@prefix ex:   <http://example.org/>.

ex:footballTeam rdf:type  rdfs:Class.

ex:soccerTeam rdf:type rdfs:Class;
                      owl:equivalentClass ex:footballTeam.

Resulting GraphQl Schema:

type __context{
   ex_footballTeam:  _@href(iri: "http://example.org/footballTeam")
   ex_soccerTeam:   _@href(iri: "http://example.org/soccerTeam")
}


interface ex_footballTeam  @service(id:"ex-sparql"){

}


interface ex_soccerTeam  @service(id:"ex-sparql"){

}

type ex_footballTeam implements ex_footballTeam & ex_soccerTeam @service(id:"ex-sparql"){

}

type ex_soccerTeam implements ex_soccerTeam & ex_footballTeam @service(id:"ex-sparql"){

}

...shared OutputType

This mapping feature is the same as the hgqls:sameAs relation between fields (properties in RDF if default vocabulary is used) but without the directive meaning that equivalences defined here lead not to an altered query translation.

This feature is not used in the default mapping

For the example of this feature the mapping configuration is altered to owl:equivalentClass a hgqls:sharedOutputType Example If fields share the output type with other fields, then the output type of each field is extended with the output type of the other field. This property has the same semantic meaning as owl:equivalentProperty. Example:

@prefix rdf:   <http://www.w3.org/1999/02/22-rdf-syntax-ns#>.
@prefix rdfs:   <http://www.w3.org/2000/01/rdf-schema#>.
@prefix ex:   <http://example.org/>.

ex:Person rdf:type rdfs:Class.
ex:drives rdf:type  rdf:Property;
          rdfs:range  ex:Car;
          rdfs:domain ex:Person.
ex:rides rdf: rdf:Property;
         rdfs:range ex:Bicycle;
         dfs.domain ex:Person.
ex:drives owl:equivilantProperty ex:rides

Resulting GraphQl Schema:

type __context{
   ex_Person:  _@href(iri: "http://example.org/Person")
   ex_AI:  _@href(iri: "http://example.org/AI")
   ex_Car:     _@href(iri: "http://example.org/Car")
   ex_drives:  _@href(iri: "http://example.org/drives")
   ex_rides:  _@href(iri: "http://example.org/rides")
}

interface ex_Person_Interface{
   ex_drives: [ex_drives_Output]
   ex_rides: [ex_rides_Output]
}

union ex_drives_Output = Car | Bicycle
union ex_rides_Output = Car | Bicycle

type ex_Person implements ex_Person_Interface @service(id:"ex-sparql"){
   ex_drives: [ex_drives_Output] @service(id:"ex-sparql")
   ex_rides: [ex_rides_Output] @service(id:"ex-sparql")
}

Datatypes

Support of more primitive data types is planned for future updates.

RDFS GraphQl
Literal String

Examples

Example application of those default mappings can be found here