Part 7: Slots and inheritance#
Slots and attributes#
Previously we have seen examples of schemas that declare fields/slots using an attributes
slot under the relevant class.
In LinkML, “slots” (aka fields) are first-class entities that can be declared outside of classes. The attribute syntax is just a convenient layer on this, but explicit declaration of slots is often preferred for reuse.
(and yes, because LinkML is self-describing, we also talk about metamodel slots)
Let’s see this in action:
personinfo.yaml:
id: https://w3id.org/linkml/examples/personinfo
name: personinfo
prefixes:
linkml: https://w3id.org/linkml/
schema: http://schema.org/
personinfo: https://w3id.org/linkml/examples/personinfo/
ORCID: https://orcid.org/
imports:
- linkml:types
default_range: string
classes:
Person:
class_uri: schema:Person
slots: ## specified as a list
- id
- full_name
- aliases
- phone
- age
id_prefixes:
- ORCID
Container:
attributes:
persons:
multivalued: true
inlined_as_list: true
range: Person
# slots are first-class entities in the metamodel
# declaring them here allows them to be reused elsewhere
slots:
id:
identifier: true
full_name:
required: true
description:
name of the person
slot_uri: schema:name
aliases:
multivalued: true
description:
other names for the person
phone:
pattern: "^[\\d\\(\\)\\-]+$"
slot_uri: schema:telephone
age:
range: integer
minimum_value: 0
maximum_value: 200
The JSON-Schema that is generated should be the same as in the previous example:
gen-json-schema personinfo.yaml
And just as a reminder, we can look at the UML for the schema:
gen-yuml personinfo.yaml
Inheritance#
Now let’s say we want to extend the schema, and we want to include
another class Organization
. Organizations may have different
properties from people, but they may also share slots in common - for
example, having a unique identifier and a name
LinkML provides mechanisms for inheritance/polymorphism, we can define a “superclass” called NamedThing
, put shared slots in there, and inherit from it.
We also introduce a concept called “mixins” here, which allows for multiple inheritance:
personinfo-with-inheritance.yaml:
id: https://w3id.org/linkml/examples/personinfo
name: personinfo
prefixes:
linkml: https://w3id.org/linkml/
schema: http://schema.org/
personinfo: https://w3id.org/linkml/examples/personinfo/
ORCID: https://orcid.org/
imports:
- linkml:types
default_range: string
classes:
## --
## New parent class
## --
NamedThing:
abstract: true ## should not be instantiated directly
slots:
- id
- full_name
close_mappings:
- schema:Thing
Person:
is_a: NamedThing ## inheritance
mixins:
- HasAliases
class_uri: schema:Person
slots: ## note we only have slots specific to people
- phone
- age
id_prefixes:
- ORCID
Organization:
description: >-
An organization such as a company or university
is_a: NamedThing
class_uri: schema:Organization
mixins:
- HasAliases
slots:
- mission_statement
Container:
tree_root: true
attributes:
persons:
multivalued: true
inlined_as_list: true
range: Person
organizations:
multivalued: true
inlined_as_list: true
range: Organization
## --
## New mixin class
## --
HasAliases:
description: >-
A mixin applied to any class that can have aliases/alternateNames
mixin: true
attributes:
aliases:
multivalued: true
exact_mappings:
- schema:alternateName
slots:
id:
identifier: true
full_name:
required: true
description:
name of the person
slot_uri: schema:name
aliases:
multivalued: true
description:
other names for the person
phone:
pattern: "^[\\d\\(\\)\\-]+$"
slot_uri: schema:telephone
age:
range: integer
minimum_value: 0
maximum_value: 200
mission_statement:
Note that our container object now contains two kinds of lists: people and organization
Here is the schema:
gen-yuml personinfo-with-inheritance.yaml
Renders as:
Let’s take a look at the JSON schema:
gen-json-schema personinfo-with-inheritance.yaml > personinfo-with-inheritance.schema.json
You can see that even though JSON-Schema doesn’t support inheritance, slots from is-a parents and mixins are “rolled down” to their children:
{
"Person": {
"additionalProperties": false,
"description": "",
"properties": {
"age": {
"type": "integer"
},
"aliases": {
"items": {
"type": "string"
},
"type": "array"
},
"full_name": {
"description": "name of the person",
"type": "string"
},
"id": {
"type": "string"
},
"phone": {
"pattern": "^[\\d\\(\\)\\-]+$",
"type": "string"
}
},
"required": [
"id",
"full_name"
],
"title": "Person",
"type": "object"
}
}
Customizing slots in the context of classes: Slot Usage#
LinkML gives you the ability to reuse or inherit slots while customizing them for use in a particular class, using slot_usage
First let’s create a schema where we introduce a new general class Relationship
slot-usage-example.yaml:
id: https://w3id.org/linkml/examples/personinfo
name: personinfo
prefixes:
linkml: https://w3id.org/linkml/
imports:
- linkml:types
default_range: string
classes:
NamedThing:
slots:
- id
- full_name
Person:
is_a: NamedThing
attributes:
has_familial_relationships:
multivalued: true
range: FamilialRelationship
inlined_as_list: true
has_organizational_relationships:
multivalued: true
range: OrganizationalRelationship
inlined_as_list: true
Organization:
is_a: NamedThing
Relationship:
abstract: true
attributes:
duration:
range: integer
related_to:
range: NamedThing
relationship_type:
FamilialRelationship:
is_a: Relationship
slot_usage:
related_to:
range: Person
required: true
relationship_type:
range: FamilialRelationshipType
OrganizationalRelationship:
is_a: Relationship
slot_usage:
related_to:
range: Organization
required: true
relationship_type:
range: OrganizationalRelationshipType
Container:
attributes:
persons:
multivalued: true
inlined_as_list: true
range: Person
organizations:
multivalued: true
inlined_as_list: true
range: Organization
slots:
id:
identifier: true
full_name:
related_to:
relationship_type:
enums:
FamilialRelationshipType:
permissible_values:
SIBLING_OF:
PARENT_OF:
CHILD_OF:
OrganizationalRelationshipType:
permissible_values:
EMPLOYED_BY:
FOUNDED_BY:
LEADER_OF:
MEMBER_OF:
gen-yuml slot-usage-example.yaml
Here we have a fairly generic class Relationship
that holds a relationship a person can hold to another entity such as another person or an organization.
there are two subclasses, or for personal relationships (e.g. siblings) and other for person-to-organization relationships. these use the same generic slots (duration, relationship)type, and related_to). However, the latter two are constrained in a class-specific way.
data.yaml:
persons:
- id: ORCID:1234
full_name: Superman
has_organizational_relationships:
- related_to: ROR:1
relationship_type: MEMBER_OF
- id: ORCID:3000
full_name: Jor El
has_familial_relationships:
- related_to: ORCID:1234
relationship_type: PARENT_OF
organizations:
- id: ROR:1
full_name: Justice League
linkml-validate data.yaml -s slot-usage-example.yaml
…