Advanced features#
The following language features are experimental, behavior is not guaranteed to stay consistent
LinkML Any type#
Most of the time in LinkML, the ranges of slots are “committed” to being one of the following:
a class
a type
an enum
Note that even if you don’t explicitly declare a range, the default_range is used,
(and the default value of default_range is string
, reflecting the most common use case).
In some cases you want to make slots more flexible, for example to allow for arbitrary objects.
The linkml:Any
states that the range of a slot can be any object. This isn’t a builtin type,
but any class in the schema can take on this roll be being declared as linkml:Any
using class_uri
:
classes:
Any:
class_uri: linkml:Any
...
Person:
attributes:
id:
metadata:
range: Any
This means all the following are valid:
name: person with string metadata
metadata: a string
name: person with an object as metadata
metadata:
name: a string
age: an integer
name: person with an integer
metadata: 42
Boolean constraints#
The following LinkML constructs can be used to express boolean constraints:
These can be applied at the class or slot level. The range of each of these is an array of expressions.
Unions as ranges#
any_of can be used to express that a range must satisfy any of a set of ranges.
One way this can be used is to compose enums together, for example if we have a vital_status
enum
that can take on any a set of enums from VitalStatus OR a missing value with the type of missing value defined by an enum:
slots:
vital_status:
required: true
range: Any
any_of:
- range: MissingValueEnum
- range: VitalStatusEnum
enums:
MissingValueEnum:
permissible_values:
INAPPLICABLE:
NOT_COLLECTED:
RESTRICTED:
OTHER:
VitalStatusEnum:
permissible_values:
LIVING:
DEAD:
UNDEAD:
Note that the range of vital_status
is declared as Any
, which is further constrained by the any_of
expression.
Currently, it is important to always have a range declaration (even if it is Any
), because LinkML constraint semantics are
monotonic (i.e. new constraints can be specified but existing ones cannot be overridden - see slots for more on this).
If this range declaration were not explicitly stated, then the default_range
of string would be applied.
In future, LinkML may allow limited forms of non-monotonicity around default ranges, see:
https://github.com/linkml/linkml/issues/1483
Rules#
Any class can have a rules block, consisting of (optional) preconditions and postconditions. This can express basic if-then logic:
classes:
Address:
slots:
- street_address
- country
rules:
- preconditions:
slot_conditions:
country:
any_of:
- equals_string: USA
- equals_string: USA_territory
postconditions:
slot_conditions:
postal_code:
pattern: "[0-9]{5}(-[0-9]{4})?"
telephone:
pattern: "^\\+1 "
description: USA and territories must have a specific regex pattern for postal codes and phone numbers
See above for implementation status
Defining slots#
A subset of slots for a class can be declared as defining slots, indicating that membership of the class can be inferred based on ranges of those slots
classes:
Interaction:
slots:
- subject
- object
ProteinProteinInteraction:
is_a: Interaction
slot_usage:
subject:
range: Protein
object:
range: Protein
defining_slots:
- subject
- object
This indicates that if we have an interaction object I
, and the subject and object slot values for I
are both of type Protein, then I
can be inferred to be of type ProteinProteinInteraction
When translating to OWL, this will make an equilance axiom:
ProteinProteinInteraction = Interaction and subject some Protein and object some Protein
And using an OWL reasoner will give the intended inferences.
This feature is experimental, and may be replaced by a more general rules mechanism in future.
equals_expression#
equals_expression can be used to specify that the value of a slot should be equal to an evaluable expression, where that expression can contain other slot values as terms.
For example, a schema may allow two separate age slots for specifying
age in years or in months. equals_expression
is used to specify one in terns
of another:
slots:
...
age_in_years:
range: decimal
minimum_value: 0
maximum_value: 999
equals_expression: "{age_in_months} / 12"
age_in_months:
range: decimal
equals_expression: "{age_in_years} * 12"
is_juvenile:
range: boolean
equals_expression: "{age_in_years} < 18"
The expression is specified using a simple subset of Python. Slot names may be enclosed in curly braces - if any of the slot values is None then the entire expression evaluates to None.
You can insert missing values by using the --infer
option when
running linkml-convert
.
Documentation of the expression language is available here.
See the developer documentation on inference for details of how to use this in code.
URI or CURIE as a range#
In some cases, you may want to use a URI or CURIE as a range. To do this,
create a class with class_uri
set to the URI or CURIE and use that class
as the range.