Advanced features

The following language features are experimental, behavior is not guaranteed to stay consistent

LinkML Any type

LinkML is currently best suited to ‘strict’ schemas, and doesn’t have full support for being able to model arbitrary dictionaries/objects. Each slot must have a defined range, the range must be exactly one of a Class, Type, or Enum. It is currently impossible to define unions of these core ranges.

The linkml:Any type is an experimental feature for allowing arbitrary objects

classes:

  MetaObject:
    class_uri: linkml:Any

  ...

  Person:
   attributes:
     id:
     name:
     metadata:
       range: MetaObject

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
    any_of:
      - range: MissingValueEnum
      - range: VitalStatusEnum
enums:
  MissingValueEnum:
    permissible_values:
      INAPPLICABLE:
      NOT_COLLECTED:
      RESTRICTED:
      OTHER:
  VitalStatusEnum:
    permissible_values:
      LIVING:
      DEAD:
      UNDEAD:

Note that these constructs may ignored by some generators in the linkml 1.1 series.

In the 1.2 series:

  • generated python should use a Union

  • jsonschema should use conditionals

  • OWL should use UnionOf

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 patern 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.

See the developer documentation on inference for details of how to use this in code.