Concept
Concept( name: str, extends: list[Concept], identify_by: dict[str, Property | Concept], model: Model, identity_includes_type: bool = False,)Represents an entity type (concept) in a semantic model.
Concepts are typically created via Model.Concept and act as typed
placeholders for entities in definitions and queries.
A concept also serves as the entry point for its relationships. Properties
and relationships are commonly attached by attribute assignment
(for example Person.name = m.Property(...)). Accessing Person.name
later yields a chained value suitable for comparisons and selection.
Parameters
Section titled “Parameters”
(namestr) - Name of the concept.
(extendslist[Concept]) - Base concepts this concept extends.
(identify_bydict[str,Property|Concept]) - Mapping from identity field name to its type (aPropertyorConcept). When provided, the mapping is used to create identity properties on the concept using generic/default readings. For identity defined after creation, or to use custom readings, seeConcept.identify_by.
(modelModel) - Model this concept belongs to.
(identity_includes_typebool, default:False) - Whether the concept’s type participates in the entity key.
Examples
Section titled “Examples”Create a concept in a model:
>>> from relationalai.semantics import Model>>> m = Model()>>> Person = m.Concept("Person")Assign properties to the concept via attribute assignment:
>>> from relationalai.semantics import String, Integer>>> Person.name = m.Property(f"{Person} is named {String:name}")>>> Person.age = m.Property(f"{Person} is {Integer:age} years old")Use Concept.new to create instances of the concept in a Model.define block:
>>> m.define(... Person.new(name="Alice", age=10),... Person.new(name="Bob", age=20),... )Use the concept object and its properties as placeholders in queries:
>>> m.select(Person.name).where(Person.id == 1).to_df()Use the extends parameter to create a concept hierarchy:
>>> # Create an Adult concept that extends Person>>> Adult = m.Concept("Adult", extends=[Person])Call concept (see Concept.__call__) to express membership in that concept:
>>> # If a Person's age is at least 18, they are an Adult>>> m.where(Person.age >= 18).define(Adult(Person))Users typically create concepts via Model.Concept rather than
instantiating this class directly.
Methods
Section titled “Methods”.__setattr__()
Section titled “.__setattr__()”Concept.__setattr__(name: str, value: Any) -> NoneRegister relationships declared via attribute assignment.
Assigning a Relationship (including Property) to a
concept stores the relationship in the concept’s relationship registry.
This is what enables the common declaration pattern Person.age = m.Property(...).
Relationship names are tracked case-insensitively (the attribute name is
lowercased). If the relationship does not already have a short_name,
it is set from the attribute name.
Non-relationship assignments fall back to normal attribute setting.
Parameters:
(namestr) - Attribute name being assigned.
(valueAny) - Value being assigned. If this is aRelationship(includingProperty), it is treated as a declared relationship/property.
Raises:
relationalai.util.error.RAIException- If a relationship with the same name (case-insensitive) is already defined on this concept.
.__getattr__()
Section titled “.__getattr__()”Concept.__getattr__(item: str) -> ChainReturn a chained relationship reference for attribute access.
Concepts expose declared relationships as Python attributes. Accessing
Person.name returns a Chain that can be used in filters and
selections (for example Person.name == "Alice" or Person.pets.name == "Fluffy").
If the concept does not have a relationship with the given name, this method looks for it
recursively in base concepts (via extends).
If the attribute name starts with an underscore, this method defers to
normal Python attribute access so internal attributes (like _name)
remain accessible.
Parameters:
(itemstr) - Relationship/property name to access.
Returns:
Chain- A chained value representing the relationship reading.
Raises:
relationalai.util.error.RAIException- Raised in the following cases:- A relationship with this name is not declared and the
model.implicit_propertiesconfig flag is disabled. - Relationships are accessed on a core concept.
- A relationship with this name is not declared and the
Examples:
Declare a property and use attribute access in a query:
>>> from relationalai.semantics import Model>>> m = Model()>>> Person = m.Concept("Person")>>> m.define(Person.new(name="Bob"), age=20))>>> # Access the `name` properties via attribute access>>> m.select(Person.name).to_df()Access properties from a base concept:
>>> Adult = m.Concept("Adult", extends=[Person])>>> m.where(Person.age >= 18).define(Adult(Person))>>> m.select(Adult.name).to_df().__call__()
Section titled “.__call__()”Concept.__call__(*args: Any, **kwargs: Any) -> ExpressionReturn an expression asserting membership in this concept.
Calling a concept creates a composable Expression that you can
use in Model.where and Model.define. This is used to
express membership in the concept, for example Adult(Person) asserts
that Person is an Adult.
Parameters:
(*argsAny, default:()) - Positional arguments to include in the membership expression. Although multiple arguments can be provided, the most common usage is to pass a single concept object (for example,Adult(Person)).
(**kwargsAny, default:{}) - Extra named values attached to the resulting expression. Currently, these keyword arguments are ignored.
Returns:
Expression- A composable expression asserting membership in this concept.
Raises:
relationalai.util.error.RAIException- If an argument is not valid in an expression (for example, passing a table schema).NotImplementedError- If a Python value cannot be converted to a supported literal.
Examples:
Define membership in a derived concept:
>>> from relationalai.semantics import Model, Integer, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} is {Integer} years old")>>> Adult = m.Concept("Adult", extends=[Person])>>> m.define(Adult(Person)).where(Person.age >= 18)Assert membership in a Model.where clause:
>>> m.select(Person.age).where(Adult(Person)).to_df()Notes:
This method does not create entities. To create new instances of a
concept, use Concept.new.
.new()
Section titled “.new()”Concept.new(*args: StatementAndSchema, **kwargs: Any) -> NewCreate a statement that constructs a new entity of this concept.
Concept.new(...) returns a New expression. It becomes part
of the model when you pass it to Model.define (or the
convenience function define).
Use keyword arguments to set properties on the new entity. Keyword names
resolve like attribute access (case-insensitively), so Person.new(name="Alice")
corresponds to Person.name. If a keyword refers to a property that has
not been declared and the model.implicit_properties config flag is disabled,
an exception is raised; otherwise the property is created implicitly.
You can also pass a TableSchema (typically from Table.to_schema)
to create one entity per source row and fill in missing keyword fields
from table columns.
Parameters:
(*argsStatementAndSchema, default:()) - Optional statements and/or table schemas to attach to the entity constructor. If aTableSchemais provided, its columns are used to fill in keyword fields that are not explicitly present in**kwargs.
(**kwargsAny, default:{}) - Property/relationship values to set for the new entity. Values may be Python literals (for example"Alice"or1) or DSL values (for example a variable or expression).
Returns:
New- A composable “new entity” statement that can be passed toModel.defineand then used in other statements to refer to the created entity.
Raises:
relationalai.util.error.RAIException- Raised in the following cases:- The concept defines identity properties (via
identify_by) but one or more required identity values are missing when the resultingNewis defined/compiled. - A keyword refers to a relationship/property that is not declared
and the
model.implicit_propertiesconfig flag is disabled. - The concept belongs to the core library (relationships cannot be accessed on core concepts).
- The concept defines identity properties (via
Examples:
Create entities with properties and query one value:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"id": Integer})>>> Person.name = m.Property(f"{Person} has name {String}")>>> m.define(Person.new(id=1, name="Alice"), Person.new(id=2, name="Bob"))>>> m.where(Person.id == 1).select(Person.name).to_df()Create one entity per row of an external table schema:
>>> customers = m.Table("DB.SCHEMA.CUSTOMERS", schema={"id": Integer, "name": String})>>> Customer = m.Concept("Customer", extends=[Person])>>> m.define(Customer.new(customers.to_schema()))Notes:
Think of Concept.new as find-or-create. When you define the returned
New, the model uses the concept type plus its identity values to
decide which entity you mean. If an entity with the same identity already
exists, the statement refers to it; otherwise a new entity is created.
If the concept declares identity properties (via the identify_by keyword
argument at concept creation time, or later via Concept.identify_by),
you must provide values for those identity properties in Concept.new.
If the concept has no declared identity, the model derives identity from
the values you pass (and, when creating from a TableSchema, from each
source row).
As a result:
- Calling
Person.newtwice with the same identity values refers to the same entity. - Defining the same
Newexpression more than once is redundant; it does not create a second, distinct entity. - To create multiple distinct entities that share the same non-identity
properties (for example, two people both aged 30), define an explicit
identity (e.g. an
idproperty viaConcept.identify_by) and pass different identity values.
Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Declare concepts │ ├── Declare a concept withModel.Concept│ ├── Declare a concept with an identity scheme │ └── Declare a subconcept withextends└── Define base facts └── Define entities and their properties withConcept.new
.to_identity()
Section titled “.to_identity()”Concept.to_identity(*args: Any, **kwargs: Any) -> NewReturn an identity-only entity reference for this concept.
The returned New is identity-only: it refers to an existing
entity by its identity key. It is compiled as a lookup (it cannot create
or update entities).
Use to_identity(...) when you know the full identity of the entity
you want. In contrast, Concept.filter_by builds a general property
filter and may match multiple entities.
Parameters:
(*argsAny, default:()) - Optional statements and/or table schemas, expanded likeConcept.new.
(**kwargsAny, default:{}) - Identity values (and optional extra property filters).
Returns:
New- A composable entity reference.
Raises:
relationalai.util.error.RAIException- Raised in the following cases:- Required identity values are missing.
- A keyword refers to a relationship/property that is not declared
and the
model.implicit_propertiesconfig flag is disabled. - The identity-only expression is passed directly to
Model.define.
Examples:
>>> from relationalai.semantics import Model, String, define, select>>> m = Model()>>> Person = m.Concept("Person", identify_by={"id": String})>>> Person.name = m.Property(f"{Person} is named {String}")>>> define(Person.new(id="P1", name="Alice"))>>> select(Person.name).where(Person.to_identity(id="P1")).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model └── Define base facts
.identify_by()
Section titled “.identify_by()”Concept.identify_by(*properties: Property | Chain) -> ConceptMark one or more properties as identity for this concept.
Concept.identify_by(...) declares which properties form the unique
identity key for entities of this concept. Identity is used by
Concept.new to decide which entity you mean (find-or-create by key).
This is an in-place configuration method: it appends the provided
properties to this concept’s identity list and returns self for
chaining.
This method differs from the identify_by=... argument used when
creating a concept (for example via
Model.Concept): the
constructor form creates identity properties automatically, while
identify_by(...) marks existing properties (including custom
readings created with Relationship.alt)
as identity.
Parameters:
-
(*propertiesProperty|Chain, default:()) - One or more properties that form the identity key. In typical usage you pass relationship attributes such asPerson.ssn(aChain). You may also pass aReadingproduced byRelationship.alt, as long as the underlying relationship is aProperty.Each provided property must have this concept as its first field (i.e., it must be a property of this concept).
Returns:
Concept- This concept (returned to enable fluent chaining).
Raises:
relationalai.util.error.RAIException- Raised in the following cases:- An argument is not a property (or a property reading).
- A property’s first field is not this concept.
Examples:
Declare a property, mark it as identity, define a couple entities, and query by the identity value:
>>> from relationalai.semantics import Model, String>>> m = Model()>>> Person = m.Concept("Person")>>> Person.ssn = m.Property(f"{Person} has {String:ssn}")>>> Person.identify_by(Person.ssn)>>> m.define(... Person.new(ssn="123-45-6789", name="Alice"),... Person.new(ssn="987-65-4321", name="Bob")... )>>> m.select(Person.name).where(Person.ssn == "123-45-6789").to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model └── Declare concepts
.filter_by()
Section titled “.filter_by()”Concept.filter_by(**kwargs: Any) -> FilterByReturn an expression that refers to entities filtered by property values.
Concept.filter_by(...) builds a composable
FilterBy expression that matches
entities of this concept whose properties/relationships match the
provided keyword arguments. Keyword names are resolved like attribute
access (case-insensitively), so Person.filter_by(name="Alice") uses
the Person.name property. Values may be Python literals (e.g., "Alice", 1)
or DSL values (e.g., variables/expressions).
This is a general property filter and may match multiple entities. If
you want to refer to a single entity by identity, use Concept.to_identity.
Parameters:
(**kwargsAny, default:{}) - Property/relationship values to match. Keys are treated case-insensitively.
Returns:
FilterBy- A composable expression that can be used inModel.whereor chained from a fragment viaFragment.where.
Raises:
relationalai.util.error.RAIException- Raised in the following cases:- A keyword refers to a relationship/property that is not declared
and the
model.implicit_propertiesconfiguration flag is disabled. - Relationships are accessed on a core concept.
- A keyword refers to a relationship/property that is not declared
and the
NotImplementedError- If a Python value cannot be converted to a supported literal.
Examples:
Filter entities by a property value:
>>> from relationalai.semantics import Model>>> m = Model()>>> Person = m.Concept("Person")>>> m.define(Person.new(id=1, name="Alice"), Person.new(id=2, name="Bob"))>>> m.where(Person.filter_by(name="Alice")).select(Person.id).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model └── Define base facts
.require()
Section titled “.require()”Concept.require(*items: Variable | Fragment) -> FragmentReturn a fragment that adds scoped requirements for this concept.
This is a convenience shorthand for starting a Model.where
domain on this concept and then adding one or more requirements, such
as model.where(Person).require(...). Compared to calling
Model.require (or require)
directly, this form scopes the requirement to entities matched by the
concept domain, making it a common way to express per-entity constraints
(for example, “every Person must have an age”).
Parameters:
Returns:
Fragment- A composable fragment representing the requirement(s), which can be further chained with methods likeFragment.where,Fragment.select, andFragment.require.
Examples:
Require that every person has a positive age:
>>> from relationalai.semantics import Model, define>>> m = Model()>>> Person = m.Concept("Person")>>> define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=20))>>> Person.require(Person.age > 0)Referenced By:
RelationalAI Documentation
└── Build With RelationalAI
└── Understand how PyRel works > Build a semantic model
└── Define requirements
├── How a requirement works
└── Add a per-entity requirement with Concept.require.ref()
Section titled “.ref()”Concept.ref(name="") -> RefReturn a named reference variable for entities of this concept.
Concept.ref(...) creates a fresh Ref
that you can use as a placeholder for an (unknown) entity of this
concept when writing queries and definitions. This is most useful when you
need multiple distinct variables of the same concept (for example
u and v nodes in a graph).
The optional name is used as the reference’s display/name in
generated readings and debugging output. When omitted, a default name
is chosen based on the concept.
Parameters:
(namestr, default:"") - Human-friendly name for the reference variable.
Returns:
Examples:
Create two independent reference variables and query pairs:
>>> from relationalai.semantics import Model>>> m = Model()>>> Person = m.Concept("Person")>>> m.define(... Person.new(id=1, name="Alice", age=30),... Person.new(id=2, name="Bob", age=30),... Person.new(id=3, name="Carol", age=25),... )>>> p, q = Person.ref("person1"), Person.ref("person2")>>> m.select(p.name, q.name).where(p.age == q.age, p.id < q.id).to_df().annotate()
Section titled “.annotate()”Concept.annotate(*annos: Expression | Relationship) -> ConceptAttach one or more annotations to this concept.
An annotation is recorded on the concept and emitted into the compiled model. Annotations are primarily used by backends and internal tooling for debugging and support workflows (for example, to attach metadata such as tracking labels). In general you should not need to use annotations unless RelationalAI support instructs you to add them for diagnostic purposes.
Parameters:
(*annosExpression|Relationship, default:()) - One or more annotation nodes to attach.
Returns:
Concept- This concept (returned to enable fluent chaining).
Raises:
relationalai.util.error.RAIException- Raised when an annotation argument has an unsupported type.
.__format__()
Section titled “.__format__()”Concept.__format__(format_spec: str) -> strReturn the placeholder used when formatting this concept in readings.
Concepts are commonly interpolated into f-strings when you declare a
relationship or property reading (for example
m.Relationship(f"{Person} has pet {String:pet_name}")).
In that context, Reading parses the formatted output and resolves
it back to the correct concept.
When format_spec is provided, it is treated as the field name to
use for this concept occurrence in the reading (whitespace is stripped).
When omitted, a default field name is derived from the concept name.
Parameters:
(format_specstr) - Optional field name to use in the reading.
Returns:
str- A placeholder string of the form"{Name#id}"or"{Name#id:field_name}".
Examples:
Declare a relationship reading and name a field via a format spec:
>>> from relationalai.semantics import Model, String>>> m = Model()>>> Person = m.Concept("Person")>>> Person.pets = m.Relationship(f"{Person} has pet {String:pet_name}")Notes:
This method is primarily for building relationship/property readings. It is not intended as a general-purpose display formatting hook.
Inheritance Hierarchy
Section titled “Inheritance Hierarchy”Subclassed By
Section titled “Subclassed By”semantics > frontend ├── base │ ├── Data │ ├── NumberConcept │ └── Table └── core └── AnyNumber
Used By
Section titled “Used By”semantics ├── frontend │ ├── base │ │ ├── Chain │ │ │ └── __getitem__ │ │ ├── DerivedColumn │ │ ├── Expression │ │ │ └── __getitem__ │ │ ├── Field │ │ ├── FieldRef │ │ ├── FilterBy │ │ ├── Literal │ │ ├── Model │ │ │ ├── Concept │ │ │ └── Table │ │ ├── New │ │ ├── Property │ │ ├── Ref │ │ ├── Relationship │ │ │ └── __getitem__ │ │ └── Table │ └── core │ └── AnyNumber ├── reasoners > graph > core │ └── Graph └── std ├── constraints │ ├── anyof │ ├── exclusive │ └── oneof ├── decimals │ ├── is_decimal │ ├── parse │ ├── precision │ ├── scale │ └── size └── numbers └── is_number
Returned By
Section titled “Returned By”semantics > frontend > base ├── Concept │ ├── annotate │ └── identify_by └── Model └── Concept
Referenced By
Section titled “Referenced By”RelationalAI Documentation ├── Build With RelationalAI │ └── Understand how PyRel works │ ├── Build a semantic model │ │ ├── Declare concepts │ │ │ ├── Declare a concept withModel.Concept│ │ │ ├── Declare a concept with an identity scheme │ │ │ └── Declare a subconcept withextends│ │ ├── Define base facts │ │ │ └── Define entities and their properties withConcept.new│ │ └── Define requirements │ │ ├── How a requirement works │ │ └── Add a per-entity requirement withConcept.require│ └── Use advanced reasoning > Graph reasoning │ └── Create a graph │ └── Define nodes withnode_concept└── Release Notes └── Python API Release Notes └── What’s New in Version 1.0.0 └── Breaking Changes