Declare concepts
Concepts are the types in your semantic model that represent the entities in your domain, like Customer, Order, and Shipment.
This guide covers how to:
- Declare concepts in your model
- Create concept hierarchies with inheritance
- Inspect the concepts declared in a model
For how to define instances of those concepts, called entities, see Define Base Facts.
- PyRel is installed and configured. See Set Up Your Environment for instructions.
What a concept is
Section titled “What a concept is”A concept is a type in your semantic model that you declare with Model.Concept().
It names a kind of thing you want to talk about and gives that kind of thing a consistent meaning everywhere you use it.
For example, Customer, Order, and Shipment are concepts.
You use concepts to:
- Declare the schema of your model.
- Describe how things are related to each other.
- Write logic in terms of your domain concepts instead of raw tables and columns.
Concepts are types, not data.
Declaring a concept does not create any entity instances.
You create instances later with Concept.new() and Model.define().
Kinds of concepts
Section titled “Kinds of concepts”There are two broad kinds of concepts:
- Entity types are things in your domain that you expect to have identity.
Examples include
Customer,Order,Product, andShipment. Entity types are where you typically care about identity schemes, inheritance, and relationships. - Value types are the kinds of values you store on entities.
Examples include order IDs, SKUs, amounts, timestamps, and email addresses.
Value types are most often used as property value types and as identity field types.
They are often declared as subconcepts of primitive concepts like
StringandInteger.
Identity and entity keys
Section titled “Identity and entity keys”When you create an entity, the model assigns it an internal key that identifies it. That key is computed from the identifying property values in the concept’s identity scheme. This is similar to a primary key in SQL.
At a high level:
- If two records have the same identifying property values, they refer to the same entity.
- If the identifying property values differ, they refer to different entities.
You do not have to define an identity scheme for a concept. In that case, the model derives identity for you.
Primitive concepts
Section titled “Primitive concepts”Primitive concepts are built-in value types that represent common scalar domains. You use them when you declare identity properties and fields in relationships and properties.
Common primitive concepts include:
Integerfor whole numbers.Floatfor fractional numbers.Stringfor text.Booleanfor true/false values.DateandDateTimefor calendar dates and points in time.
When you need a value type that carries domain meaning, declare a named value type by creating a subconcept of a primitive.
For example, you can declare EmailAddress as a concept that extends String.
Declare a concept
Section titled “Declare a concept”Use the Model.Concept() method to declare a concept.
You can declare a concept with or without an identity scheme. Use the following table to decide which approach to use:
| What to use | When to use it |
|---|---|
| Use an identity scheme | When you want to define a clear identity scheme for your concept and have precise control over how entities are created and matched. This is the most common case for entity types. |
| Use implicit identity | When you are declaring a value type or a supertype that does not require a specific identity scheme. |
Use an identity scheme
Section titled “Use an identity scheme”There are two ways to declare a concept with an identity scheme. Use the following table to decide which approach to use:
| What to use | When to use it |
|---|---|
identify_by parameter | When you want to declare a concept and create its identifying properties in one step. This is the most common case. |
Concept.identify_by() | When you need to reference an existing property as an identity field. |
Pass the identify_by parameter to Model.Concept()
Section titled “Pass the identify_by parameter to Model.Concept()”You can declare a concept’s identity scheme by passing the identify_by parameter to Model.Concept() and specifying which properties make up identity in a dictionary:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
# Single property identityCustomer = m.Concept("Customer", identify_by={"id": Integer})Product = m.Concept("Product", identify_by={"id": Integer})Order = m.Concept("Order", identify_by={"id": Integer})
# Composite identityOrderItem = m.Concept("OrderItem", identify_by={"order": Order, "product": Product})- Each
m.Concept(...)call declares a concept type and returns aConceptobject. The Python variablesCustomer,Order, andOrderItemare just handles you can reference later. identify_by={...}declares which properties make up the concept’s identity scheme. These properties are required when you later create entities withConcept.new().CustomerandOrderare both identified by a single property:idandid, respectively.OrderItemis identified by a composite key made up oforderandproductproperties of typeOrderandProduct, respectively. Both properties together determine identity forOrderItem.
- The concept name you pass to
Model.Concept()is the canonical name for that concept in your model. It will appear as the key name when you view the model’s concepts. - 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.
identify_by={...}automatically creates properties on the concept with the specified names and types.
(Advanced) Use Concept.identify_by() to reference existing properties
Section titled “(Advanced) Use Concept.identify_by() to reference existing properties”Use Concept.identify_by() when a concept’s identity scheme needs to reference a property you have already declared:
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.new()decides 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_by()when 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.
Use implicit identity
Section titled “Use implicit identity”To declare a concept with implicit identity, call Model.Concept() without an identify_by parameter:
from relationalai.semantics import Model
m = Model("MyModel")
Customer = m.Concept("Customer")Order = m.Concept("Order")Shipment = m.Concept("Shipment")- Because no
identify_byparameter is provided, these concepts use implicit identity. The model derives an identity scheme for each concept based on the properties that are created when you later define entities withConcept.new().
Declare a subconcept
Section titled “Declare a subconcept”Use Model.Concept() with the extends parameter to declare a subconcept that inherits from one or more parent concepts.
The subconcept inherits all properties and relationships of its parent(s) and can also have its own additional properties and relationships.
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.
Inherit from one parent concept
Section titled “Inherit from one parent concept”Pass a list with one parent Concept object to the extends parameter to create a subconcept that inherits from a single parent:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
Order = m.Concept("Order", identify_by={"id": Integer})DelayedOrder = m.Concept("DelayedOrder", extends=[Order])DelayedOrderis a subconcept ofOrder.Orderis declared with identity (identify_by={"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.
If the parent has an identity scheme, the subconcept inherits that identity scheme and the same identifying properties.
DelayedOrder.filter_by(id=123)andOrder.filter_by(id=123)refer to the same entity. - Subconcepts can also have their own properties and relationships in addition to what they inherit from their parents.
For example,
DelayedOrdercould have adelay_reasonproperty thatOrderdoes not have.
Inherit from multiple parent concepts
Section titled “Inherit from multiple parent concepts”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 Integer, Model
m = Model("MyModel")
Shipment = m.Concept("Shipment", identify_by={"id": Integer})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.
- 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 first parent in the
extendslist that has a declared identity scheme is the one that determines the identity scheme for the subconcept. BecauseShipmenthas identity,TrackedShipmentinherits the identifying properties fromShipment.TrackedShipment.filter_by(id=123)andShipment.filter_by(id=123)refer to the same entity.
Use an alternative identity scheme
Section titled “Use an alternative identity scheme”Subconcepts can declare their own identity scheme that is different from their parent(s) by passing an identify_by parameter to Model.Concept() in addition to extends:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
Shipment = m.Concept("Shipment", identify_by={"id": Integer})ReturnShipment = m.Concept( "ReturnShipment", extends=[Shipment], identify_by={"rma": Integer})ReturnShipmentis a subconcept ofShipmentbut has an alternate identity scheme based on anrmaproperty instead ofid.- An alternative identity scheme allows
ReturnShipmententities to be identified by either theidproperty inherited fromShipmentor the newrmaproperty declared onReturnShipment. In other words, if aReturnShipmententity has anidof123and anrmaof456, the same entity can be referred to by eitherShipment.filter_by(id=123)orReturnShipment.filter_by(rma=456).
Partition sibling subconcepts
Section titled “Partition sibling subconcepts”If you need sibling subconcepts to be distinct from each other even when they share the same identity field values, you can include the concept type in the identity scheme by setting identity_includes_type=True on the shared parent concept:
from relationalai.semantics import Integer, Model
m = Model("MyModel")
Shipment = m.Concept( "Shipment", identify_by={"id": Integer}, identity_includes_type=True,)
OutboundShipment = m.Concept("OutboundShipment", extends=[Shipment])ReturnShipment = m.Concept("ReturnShipment", extends=[Shipment])identity_includes_type=Truemakes the subconcept name part of the identity key.- This keeps
OutboundShipmentandReturnShipmentdistinct even when they share the sameid. In other words,OutboundShipment.filter_by(id=123)andReturnShipment.filter_by(id=123)refer to different entities. This would mean that there are two distinctShipmententities withid=123. - The setting is applied on the shared base concept (
Shipment), so it affects all subconcepts that extend it.
View all of a model’s concepts
Section titled “View all of a model’s concepts”Viewing concepts helps you confirm what schema your model has declared so far, especially in notebooks and interactive exploration.
There are two ways to view the concepts declared in the current model instance. Use the following table to decide which approach to use:
| What to use | When to use it |
|---|---|
Model.concept_index | When you want to look up a concept by name. |
Model.concepts | When you want to iterate over all concepts declared in the model. |
Use Model.concept_index
Section titled “Use Model.concept_index”You can view look up a concept by name or view a mapping of all concept names to their corresponding Concept objects by inspecting Model.concept_index:
from relationalai.semantics import Model
m = Model("MyModel")Customer = m.Concept("Customer")
# Look up the concept by its declared namecustomer_concept = m.concept_index["Customer"]
# Print the names of all concepts in the modelprint(m.concept_index.keys())- The keys in
m.concept_indexare the concept names you pass toModel.Concept(). m.concept_index["Customer"]returns the underlyingConceptobject.
Use Model.concepts
Section titled “Use Model.concepts”You can get a list of all Concept objects declared in the model by inspecting Model.concepts:
from relationalai.semantics import Model
m = Model("MyModel")Customer = m.Concept("Customer")Order = m.Concept("Order")
# View a list of all Concept objects declared in the modelprint(m.concepts)m.conceptsis a list of every concept object created viaModel.Concept().- This can be useful for iterating over all concepts declared in the model.