Declare concepts
Declare concept types and identity keys to define the stable schema for your semantic model. Use this guide to turn a model design into a small, intentional concept hierarchy before you declare relationships, load data, or write rules.
- PyRel is installed and importable in Python. See Set Up Your Environment for instructions.
- You have a
raiconfig.yamlfile in your project, or you are prepared to pass config programmatically when you create the model. - You have a model design with concepts and relationships you want to declare. See Design a Model for how to design a semantic model.
Declare a concept with Model.Concept
Section titled “Declare a concept with Model.Concept”Declaring a concept creates an entity type, like Customer or Order.
Use it to turn your model design into a concrete schema you can attach properties and relationships to.
It does not load data or create entities.
To declare a concept, call Model.Concept and pass the concept name:
from relationalai.semantics import Model
m = Model("MyModel")
Customer = m.Concept("Customer")Order = m.Concept("Order")Shipment = m.Concept("Shipment")m = Model("MyModel")creates a model you can add concepts to.- Each
m.Concept("...")call declares a concept type (an entity type in your schema) and returns a concept object. The Python variablesCustomer,Order, andShipmentare just handles you can reference later. - Declaring concepts does not create any entity instances.
You create instances later (for example with
Concept.newandModel.define).
- The concept name you pass to
Model.Conceptis the canonical name for that concept in your model. It is used in generated readings and in query results. - Declaring a concept does not create any entity instances. It just defines a type that you can create entities of later.
- You can declare as many concepts as you need to represent the entities in your model design.
Declare a concept with an identity scheme
Section titled “Declare a concept with an identity scheme”Identity is the key the model uses to decide whether two records refer to the same entity. Declare identity for concepts you expect to join, query, or merge across sources.
Identity is used when you create entities with Concept.new.
If you do not declare identity, Concept.new derives identity from the values you pass.
That is easy to change by accident, which can merge distinct entities or create duplicates.
Define an identity scheme during concept declaration
Section titled “Define an identity scheme during concept declaration”For most workflows, pass identify_by to Model.Concept:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
# Single-field identityCustomer = m.Concept("Customer", identify_by={"customer_id": Integer})Order = m.Concept("Order", identify_by={"order_id": Integer})
# Composite identityOrderItem = m.Concept( "OrderItem", identify_by={"order_id": Integer, "line_number": Integer},)identify_by={...}declares which fields make up identity for the concept. When you later callConcept.new, those fields are used to compute the entity key.CustomerandOrderuse single-field identity (customer_idandorder_id).OrderItemuses composite identity (order_id+line_number). You must provide both fields when you createOrderItementities.
identify_by={...}also creates properties on the concept. For example, you can referenceCustomer.customer_idandOrderItem.line_number.- Those generated properties use a default reading like
"{concept} has {type:field_name}".
(Advanced) Mark existing properties as part of an identity scheme
Section titled “(Advanced) Mark existing properties as part of an identity scheme”Use Concept.identify_by when identity must reference a property you already declared.
This comes up when you declared the property first to use a custom reading via Model.Property:
from relationalai.semantics import Model, String
m = Model("MyModel")Product = m.Concept("Product")
# Declare the property first when you need a custom reading.Product.sku = m.Property(f"{Product} has SKU {String:sku}")
# Then mark the existing property reading as identity.Product.identify_by(Product.sku)Product = m.Concept("Product")declares the concept type first.m.Property(...)creates a property reading. Assigning it toProduct.skuattaches that property to theProductconcept.Product.identify_by(Product.sku)then uses that existing property as the identity field. This affects howProduct.newdecides whether two records refer to the sameProductentity.- The reading string (
f"{Product} has SKU {String:sku}") is a compact way to define the property’s name and type.
- For most cases, you should prefer
identify_by={...}when you callModel.Concept. UseConcept.identify_bywhen identity needs to reference a property you already declared. - Only pass properties of the concept you are configuring when you call
Concept.identify_by. It raises an error if you pass a property from a different concept.
Declare a subconcept with extends
Section titled “Declare a subconcept with extends”Use the Model.Concept method’s extends parameter when one concept is a more specific type of another.
The subconcept inherits the parent’s properties.
Common examples of subconcepts include:
- A named value type that is a specific kind of primitive type.
For example,
EmailAddressas a subconcept ofString, orPriceas a subconcept ofNumber. - A specific category of a more general concept.
For example,
CriticalTicketas a subconcept ofTicket, orElectricVehicleas a subconcept ofVehicle.
The following sections show how to declare single-parent and multi-parent subconcepts, and how to keep siblings distinct by including the concept type in the identity scheme.
Declare a single-parent subconcept
Section titled “Declare a single-parent subconcept”Pass a list with one parent Concept object to the extends parameter to create a subconcept that is a strict specialization of another:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
Order = m.Concept("Order", identify_by={"order_id": Integer})DelayedOrder = m.Concept("DelayedOrder", extends=[Order])DelayedOrderis a subconcept ofOrder.Orderis declared with identity (identify_by={"order_id": Integer}), so it has an explicit entity key.DelayedOrder = m.Concept("DelayedOrder", extends=[Order])declares a new concept type that inherits fromOrder.- Because
Orderhas identity,DelayedOrderinherits the identifying properties fromOrder.
- Subconcepts inherit all properties from their parent concepts.
In this example,
DelayedOrderinherits theorder_idproperty fromOrder. - Subconcepts can also have their own properties and relationships in addition to what they inherit from their parents.
For example, you could add a
delay_reasonproperty toDelayedOrderwithout affectingOrder.
Declare a multi-parent subconcept
Section titled “Declare a multi-parent subconcept”You can pass a list with multiple parent Concept objects to the extends parameter to create a subconcept that inherits from multiple bases:
from relationalai.semantics import Model
m = Model("MyModel")
Shipment = m.Concept("Shipment")Trackable = m.Concept("Trackable")
TrackedShipment = m.Concept("TrackedShipment", extends=[Shipment, Trackable])TrackedShipmentinherits from bothShipmentandTrackable.ShipmentandTrackableare two separate base concepts.TrackedShipment = m.Concept("TrackedShipment", extends=[Shipment, Trackable])declares a subconcept that inherits from both. Use this pattern when you want a concept to share behavior or properties across multiple bases.
- When you declare a multi-parent subconcept, it inherits properties from all of its parents.
If two parents define a property with the same name, the parent listed first in
extendswins. - The subconcept’s identity key comes from the first parent in
extendsthat has a declared identity scheme. If another parent also declares identity fields, you still must pass those values toConcept.new. - If none of the parents declare identity, the subconcept has no declared identity scheme.
In that case,
Concept.newderives identity from the values you pass.
(Advanced) Include a concept’s type in its identity scheme
Section titled “(Advanced) Include a concept’s type in its identity scheme”Use identity_includes_type when you have sibling subconcepts that share the same identity fields, but you still want them to be different entities.
It is False by default, so identity is based only on the fields in identify_by (for example shipment_id).
When it is False, two sibling subconcepts can “collide” and resolve to the same entity key when you call Concept.new.
To include the concrete subconcept type in the key, set identity_includes_type to True on the shared base concept:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
Shipment = m.Concept( "Shipment", identify_by={"shipment_id": Integer}, identity_includes_type=True,)
OutboundShipment = m.Concept("OutboundShipment", extends=[Shipment])ReturnShipment = m.Concept("ReturnShipment", extends=[Shipment])identity_includes_type=Truemakes the subconcept type part of the identity key.- This keeps
OutboundShipmentandReturnShipmentdistinct even when they share the sameshipment_id. - The setting is applied on the shared base concept (
Shipment), so it affects all subconcepts that extend it. - Set
identity_includes_typebefore creating entities, so you don’t change identity keys mid-stream.
To see what this changes:
-
Create two entities with the same identity values:
m.define(OutboundShipment.new(shipment_id=1),ReturnShipment.new(shipment_id=1),) -
Confirm you have two entities by selecting
Shipment.shipment_id:m.select(Shipment.shipment_id).to_df()Output shipment_id0 11 1
- You have two shipments with
shipment_id=1, but they are different entities because their keys include their concept type. - If
identity_includes_typewereFalse, those twoConcept.newcalls would compute the same entity key, and you would end up with one entity that appears to be bothOutboundShipmentandReturnShipment. - With
identity_includes_type=True, the key includes the concrete concept type, so the entities stay distinct even when they share the sameshipment_id.
View all concepts declared in a model
Section titled “View all concepts declared in a model”Viewing concepts helps you confirm what schema your model has declared so far, especially in notebooks and interactive exploration.
Choose Model.concepts when you want everything the model has created.
Choose Model.concept_index when you want to look up a concept by name.
Use Model.concepts
Section titled “Use Model.concepts”To view all concepts created in a model, inspect Model.concepts:
from relationalai.semantics import Model
m = Model("MyModel")Customer = m.Concept("Customer")Order = m.Concept("Order")
# Print all concepts declared in the modelprint(m.concepts)m.conceptsis a list of every concept object created viaModel.Concept.- This is a good first stop when you are not sure which concept types were declared in the current model instance.
Use Model.concept_index
Section titled “Use Model.concept_index”To look up a concept by name, use Model.concept_index:
from relationalai.semantics import Model
m = Model("MyModel")Customer = m.Concept("Customer")
# Look up the concept by its declared nameprint(m.concept_index["Customer"])- The keys in
m.concept_indexare the concept names you pass toModel.Concept. m.concept_index["Customer"]returns the underlyingConceptobject.