Using Schemaview to introspect Risk Atlas Nexus¶
The SchemaView class in the linkml-runtime provides a method for dynamically introspecting and manipulating schemas. This can be used to programatically explore or edit the Risk Atlas Nexus.
Introspection¶
The following cells show some examples of how you can examine the contents of the schema.
from linkml_runtime.utils.schemaview import SchemaView
from linkml_runtime.linkml_model.meta import (
ClassDefinition,
)
from risk_atlas_nexus.library import RiskAtlasNexus
ran = risk_atlas_nexus = RiskAtlasNexus()
view = ran.get_schema() # get the schemaview object
# Alternatively, load a schema from file
# view = SchemaView("<YOUR_LOCAL_PATH>/risk-atlas-nexus/src/risk_atlas_nexus/ai_risk_ontology/schema/ai-risk-ontology.yaml")
[2025-08-25 22:52:49:941] - INFO - RiskAtlasNexus - Created RiskAtlasNexus instance. Base_dir: None
Return a list of all imports:¶
view.imports_closure()
['linkml:types', 'common', 'ai_risk', 'ai_system', 'ai_eval', 'energy', 'eu_ai_act', 'ai_intrinsic', 'ai-risk-ontology']
Check size of classes, slots, subsets¶
We will use these for comparison later in the notebook, to verify if new items are added or removed from graph
# view length of (classes, slots, subsets)
len(view.all_classes()), len(view.all_slots()), len(view.all_subsets())
(50, 128, 0)
See the ancestors of a class¶
You may wish to know the ancestors of a class in the graph, so can use this method to return the closure of class_parents method.
view.class_ancestors("Risk")
['Risk', 'RiskConcept', 'Entity']
See the CURIEs or URIs of a classes ancestors¶
Return the CURIE or URI for each schema element which is an ancestor of the specified class. The method allows to also expand the CURIE to a URI; defaults to False.
[view.get_uri(c) for c in view.class_ancestors("Risk")]
['airo:Risk', 'airo:RiskConcept', 'schema:Thing']
[view.get_uri(c, expand=True) for c in view.class_ancestors("Risk")]
['https://w3id.org/airo#Risk', 'https://w3id.org/airo#RiskConcept', 'http://schema.org/Thing']
See the ancestors of a class without mixins¶
You may wish to know the ancestors of a class in the graph, but without the mixin classes.
view.class_ancestors("Risk", mixins=False)
['Risk', 'Entity']
See the ancestors of a slot¶
See the ancestors, or children of a slot, if any
view.slot_ancestors("refersToRisk")
['refersToRisk']
view.slot_children("refersToRisk")
[]
Examine mappings and annotations of a slot¶
Let's take as an example the slot "refersToRisk"
refersToRisk = view.get_slot("refersToRisk")
refersToRisk
SlotDefinition({
'name': 'refersToRisk',
'description': ('Indicates the incident (subject) is a materialisation of the indicated risk '
'(object)'),
'from_schema': 'https://ibm.github.io/risk-atlas-nexus/ontology/ai_risk',
'exact_mappings': ['dpv:refersToRisk'],
'domain': 'RiskIncident',
'range': 'Risk',
'multivalued': True,
'inlined': False
})
# get the exact mappings
refersToRisk.exact_mappings
['dpv:refersToRisk']
# get mappings related to this slot from the schemaview
view.get_mappings(refersToRisk.name)
{'self': ['nexus:refersToRisk'],
'native': ['nexus:refersToRisk'],
'exact': ['dpv:refersToRisk'],
'narrow': [],
'broad': [],
'related': [],
'close': [],
'undefined': []}
# get mappings related to this slot from the schemaview, expanded URIs
view.get_mappings(refersToRisk.name, expand=True)
{'self': ['https://ibm.github.io/risk-atlas-nexus/ontology/refersToRisk'],
'native': ['https://ibm.github.io/risk-atlas-nexus/ontology/refersToRisk'],
'exact': ['https://w3c.github.io/dpv/2.1/dpv/#refersToRisk'],
'narrow': [],
'broad': [],
'related': [],
'close': [],
'undefined': []}
# the classes that make use of this slot
view.get_classes_by_slot(refersToRisk)
['RiskIncident']
e = view.get_element("refersToRisk")
e
SlotDefinition({
'name': 'refersToRisk',
'description': ('Indicates the incident (subject) is a materialisation of the indicated risk '
'(object)'),
'from_schema': 'https://ibm.github.io/risk-atlas-nexus/ontology/ai_risk',
'exact_mappings': ['dpv:refersToRisk'],
'domain': 'RiskIncident',
'range': 'Risk',
'multivalued': True,
'inlined': False
})
my_new_class = ClassDefinition("my_new_class", is_a="Entity", description="my new class description", slots=None)
view.add_class(my_new_class)
# we can see class count has gone up
# view length of (classes, slots, subsets)
print(len(view.all_classes()), len(view.all_slots()), len(view.all_subsets()))
# we can see the new class
view.get_class("my_new_class")
50 128 0
ClassDefinition({
'name': 'my_new_class',
'description': 'my new class description',
'from_schema': 'https://ibm.github.io/risk-atlas-nexus/ontology/ai-risk-ontology',
'is_a': 'Entity'
})
Example: extending the schema¶
Another option could be to define a whole new schema, and then to merge it into the other schema. You could load it from a file, or construct it dynamically.
# Load from a file
new_schema = view.load_import("<YOUR_LOCAL_PATH>/docs/examples/notebooks/example_samples/sample_additional_schema")
new_schema
SchemaDefinition({
'name': 'sample_additional_schema',
'description': 'A sample_additional_schema for an example notebook',
'id': 'https://ibm.github.io/risk-atlas-nexus/ontology/sample_additional_schema',
'imports': ['linkml:types'],
'prefixes': {'linkml': Prefix({'prefix_prefix': 'linkml', 'prefix_reference': 'https://w3id.org/linkml/'}),
'airo': Prefix({'prefix_prefix': 'airo', 'prefix_reference': 'https://w3id.org/airo#'}),
'nexus': Prefix({
'prefix_prefix': 'nexus',
'prefix_reference': 'https://ibm.github.io/risk-atlas-nexus/ontology/'
}),
'dpv': Prefix({'prefix_prefix': 'dpv', 'prefix_reference': 'https://w3c.github.io/dpv/2.1/dpv/#'}),
'ai': Prefix({'prefix_prefix': 'ai', 'prefix_reference': 'https://w3c.github.io/dpv/2.1/ai/#'})},
'default_curi_maps': ['semweb_context'],
'default_prefix': 'nexus',
'default_range': 'string',
'classes': {'SampleItem': ClassDefinition({
'name': 'SampleItem',
'description': 'A sample_additional_schema item',
'abstract': True,
'slots': ['id', 'name', 'description', 'url', 'dateCreated', 'dateModified'],
'class_uri': 'schema:Thing'
})},
'source_file': '/Users/ingevejs/Documents/workspace/ingelise/risk-atlas-nexus/docs/examples/notebooks/example_samples/sample_additional_schema.yaml'
})
Merge the schema¶
Merge the new schema, and observe the class count rising.
view.merge_schema(new_schema)
# we can see class count has gone up
# view length of (classes, slots, subsets)
print(len(view.all_classes()), len(view.all_slots()), len(view.all_subsets()))
52 131 0
Construct dynamically¶
Construct the schema dynamically
from linkml.utils.schema_builder import SchemaBuilder
sb = SchemaBuilder('test-schema')
sb.add_class('TestClassTwo', slots=['someslot', 'someslot_two'])
sb.add_class('TestClassThree', slots=['someslot', 'someslot_three'])
new_schema2 = sb.schema
print(new_schema2)
SchemaDefinition({
'name': 'test-schema',
'id': 'http://example.org/test-schema',
'default_prefix': 'http://example.org/test-schema/',
'slots': {'someslot': SlotDefinition({'name': 'someslot'}),
'someslot_two': SlotDefinition({'name': 'someslot_two'}),
'someslot_three': SlotDefinition({'name': 'someslot_three'})},
'classes': {'TestClassTwo': ClassDefinition({'name': 'TestClassTwo', 'slots': ['someslot', 'someslot_two']}),
'TestClassThree': ClassDefinition({'name': 'TestClassThree', 'slots': ['someslot', 'someslot_three']})}
})
view.merge_schema(new_schema2)
# we can see class count has now gone up
# view length of (classes, slots, subsets)
print(len(view.all_classes()), len(view.all_slots()), len(view.all_subsets()))
52 131 0