Model
Model( name: str = "", exclude_core: bool = False, is_library: bool = False, config: Config | None = None,)Create and manage a semantic model.
A Model stores the concepts, relationships, definitions, and
requirements that make up your semantic model. Create them using methods
like Model.Concept, Model.Relationship, Model.define,
and Model.require. Query methods like Model.where and
Model.select return a composable Fragment that you can further
refine and materialize with Fragment.to_df.
Many methods on Model can be accessed as top-level functions from
the relationalai.semantics module. For example, you can use
define instead of Model.define to
add definitions to a model if you have a single active model (the common
case in interactive use). However, if you have multiple models or want to
be explicit about which model you’re using, it’s best to call methods
directly on the model instance.
Parameters
Section titled “Parameters”
(namestr, default:"") - Name of the model.
(exclude_corebool, default:False) - If True, do not include the core library by default.
(is_librarybool, default:False) - If True, this model is treated as a library and is not added to the global list of active models.
(configConfig, default:None) - Configuration used when executing queries. Defaults toNonewhich means configuration is loaded from the filesystem (for example, from araconfig.yamlfile if present).
Examples
Section titled “Examples”Define a few entities and query them:
>>> from relationalai.semantics import Model>>> m = Model()>>> Person = m.Concept("Person")>>> m.define(... Person.new(name="Alice", age=10),... Person.new(name="Bob", age=30),... )>>> m.where(Person.age > 18).select(Person.name).to_df()Attributes
Section titled “Attributes”.all_models
Section titled “.all_models”Model.all_models: list[Model]Class-level list of models created in this Python session (excluding library-only models). Most users do not need this.
Model.name: strThe model name (optional; used for identification/debugging).
.config
Section titled “.config”Model.config: relationalai.config.config.ConfigConfiguration settings used when executing queries against this model.
If you have a raconfig.yaml file, or other valid configuration source,
these settings are automatically loaded into new models by default.
You can also override them by passing a custom config when creating the model.
See the relationalai.config documentation for more details.
.defines
Section titled “.defines”Model.defines: KeyedSet[Fragment]Definitions added via Model.define.
.requires
Section titled “.requires”Model.requires: KeyedSet[Fragment]Requirements added via Model.require.
.exports
Section titled “.exports”Model.exports: KeyedSet[Fragment]Export artifacts created when exporting query results (advanced).
.libraries
Section titled “.libraries”Model.libraries: list[type[Model]]Libraries included with this model (the core library is included by
default unless you set exclude_core=True).
.concepts
Section titled “.concepts”Model.concepts: list[Concept]Concepts created in this model via Model.Concept.
.tables
Section titled “.tables”Model.tables: list[Table]Table references created in this model via Model.Table.
.relationships
Section titled “.relationships”Model.relationships: list[Relationship]Relationships and properties created in this model via Model.Relationship.
.enums
Section titled “.enums”Model.enums: list[type[ModelEnum]]Enum types created in this model via Model.Enum.
.concept_index
Section titled “.concept_index”Model.concept_index: dict[str, Concept]Dictionary mapping concept names to Concept objects for quick lookup.
.table_index
Section titled “.table_index”Model.table_index: dict[str, Table]Dictionary mapping table names to Table objects for quick lookup.
.relationship_index
Section titled “.relationship_index”Model.relationship_index: dict[str, Relationship]Dictionary mapping relationship/property names to Relationship objects for quick lookup.
.enums_index
Section titled “.enums_index”Model.enums_index: dict[str, type[ModelEnum]]Dictionary mapping enum names to enum types for quick lookup.
Methods
Section titled “Methods”.Concept()
Section titled “.Concept()”Model.Concept( name: str, extends: list[Concept] = [], identify_by: dict[str, Property | Concept] = {}, identity_includes_type: bool = False,) -> ConceptCreate a concept type in this model.
This is the primary way to introduce a new Concept (entity type)
into a Model.
Parameters:
-
(namestr) - Name of the concept. -
(extendslist[Concept], default:[]) - Base concepts this concept extends. Relationships declared on base concepts are inherited by (and available on) the derived concept. -
(identify_bydict[str,Property|Concept], default:{}) - Identity fields for this concept. The keys are identity attribute names and the values specify their types (most commonly aConceptsuch assemantics.frontend.core.Integerorsemantics.frontend.core.String).Identity keys are used by
Concept.newto refer to an entity by key (find-or-create). -
(identity_includes_typebool, default:False) - If True, the subtype is part of an entity’s key. Example: whenAandBshare a base identified byid,A.new(id=1)andB.new(id=1)create two different entities. If False, both calls refer to the same entity (same key fields), which can cause collisions across a type hierarchy.
Returns:
Concept- The newly created concept.
Raises:
relationalai.util.error.RAIException- Ifextendscontains non-Concept values, or ifidentify_bycontains values that are not Concepts or Properties.
Examples:
Create a concept and define a couple of entities:
>>> from relationalai.semantics import Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> m.define(... Person.new(name="Alice"),... Person.new(name="Bob"),... )Create a derived concept:
>>> Adult = m.Concept("Adult", extends=[Person])>>> m.where(Person.age >= 18).define(Adult(Person))Define an identity key:
>>> from relationalai.semantics import Integer>>> Item = m.Concept("Item", identify_by={"id": Integer})>>> m.define(Item.new(id=1))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└── Derive facts with logic ├── Write conditional definitions withModel.whereandModel.define└── Match multiple entities of the same type withConcept.ref
.Table()
Section titled “.Table()”Model.Table(path: str, schema: dict[str, Concept] = {}) -> TableCreate a table reference in this model.
A Table represents an external table name (such as a SQL table)
that you can use as a source of schema/data for defining entities, or as
a target for exports via Fragment.into.
Parameters:
(pathstr) - Table name or path (for example"MY_DB.MY_SCHEMA.CUSTOMERS").
(schemadict[str,Concept], default:{}) - Optional mapping of column names to their types.
Returns:
Table- The created table reference.
Examples:
Create a table reference for use as an export target:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"id": Integer})>>> Person.name = m.Property(f"{Person} is named {String:name}")>>> m.define(Person.new(id=1, name="Alice"))>>> out = m.Table("DB.SCHEMA.PERSONS_EXPORT")>>> m.select(Person.id, Person.name).into(out).exec()Use a table with a known schema as an input when defining entities:
>>> from relationalai.semantics import Model, Integer, String>>> m = Model()>>> source = m.Table("DB.SCHEMA.CUSTOMERS", schema={"id": Integer, "name": String})>>> Customer = m.Concept("Customer", extends=[Person])>>> m.define(Customer.new(source.to_schema()))Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Declare data sources │ ├── Choose the right source type │ └── Use a Snowflake table withModel.Table├── Define base facts └── Query a model └── Export results to Snowflake tables withintoandexec
.Relationship()
Section titled “.Relationship()”Model.Relationship( reading: str = "", fields: list[Field] = [], short_name: str = "") -> RelationshipCreate a relationship in this model.
This is the primary way to introduce a new Relationship type into a
Model. The most common way to define a relationship is with a Python
f-string reading that embeds concept/type placeholders. Each embedded
placeholder becomes a field of the relationship, in order.
Reading strings have the following format:
- Use an f-string and embed types with
{...}, e.g.f"{Person} has {Pet}". - Name fields with
{Type:name}, e.g.f"{Person:owner} has {Pet:pet}". - For full control (including multi-field relationships), pass an
explicit
fields=[Field(...), ...]list instead of a reading.
Parameters:
(readingstr, default:"") - A reading that describes the relationship fields. Typically an f-string, for examplef"{Person} has {Pet}"orf"{Integer:i} has {String:name}".
(fieldslist[Field], default:[]) - Explicit fields for the relationship. If provided andreadingis empty, a default reading is generated from the fields.
(short_namestr, default:"") - Optional identifier used as the key inmodel.relationship_index.
Returns:
Relationship- The created relationship.
Raises:
ValueError- If neitherreadingnorfieldsis provided.
Examples:
Create a relationship between two concepts and define one fact:
>>> from relationalai.semantics import Model, String, define, where>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Pet = m.Concept("Pet", identify_by={"name": String})>>> Person.name = m.Property(f"{Person} has {String:name}")>>> Person.pets = m.Relationship(f"{Person} has {Pet}")>>> define(... alice := Person.new(name="Alice"),... boots := Pet.new(),... alice.pets(boots),... )>>> where(Person.pets).select(Person.name).to_df()Create a relationship by specifying fields directly and give it a
stable name via short_name:
>>> from relationalai.semantics import Model, String, define>>> from relationalai.semantics.frontend.base import Field>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> has_pet = m.Relationship(... fields=[Field("person", Person), Field("pet_name", String)],... short_name="has_pet",... )>>> define(alice := Person.new(name="Alice"), has_pet(alice, "boots"))>>> has_pet.to_df()Referenced By:
RelationalAI Documentation
└── Build With RelationalAI
└── Understand how PyRel works > Build a semantic model
└── Declare relationships and properties
└── Declare a relationship with Model.Relationship.Property()
Section titled “.Property()”Model.Property( reading: str = "", fields: list[Field] = [], short_name: str = "") -> PropertyCreate a property in this model.
This is the primary way to introduce a new Property into a
Model. The most common way to define a property is with a Python
f-string reading that embeds concept/type placeholders. Each embedded
placeholder becomes a field of the property, in order.
Reading strings have the following format:
- Use an f-string and embed types with
{...}, e.g.f"{Person} has {Integer:age}". - Name fields with
{Type:name}(thenamebecomes the attribute you access, e.g.Person.age). - For full control, pass an explicit
fields=[Field(...), ...]list.
Parameters:
(readingstr, default:"") - A reading that describes the property fields. Typically an f-string, for examplef"{Person} has {Integer:age}".
(fieldslist[Field], default:[]) - Explicit fields for the property. If provided andreadingis empty, a default reading is generated from the fields.
(short_namestr, default:"") - Optional identifier used as the key inmodel.relationship_index.
Returns:
Property- The created property.
Raises:
ValueError- If neitherreadingnorfieldsis provided.
Examples:
Declare a property on a concept, define a couple of entities, and query:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=30))>>> m.where(Person.age >= 18).select(Person.age).to_df()Create a property by specifying fields directly and give it a stable
name via short_name:
>>> from relationalai.semantics import Integer, Model, String>>> from relationalai.semantics.frontend.base import Field>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> age = m.Property(... fields=[Field("person", Person), Field("age", Integer)],... short_name="age",... )>>> Person.age = ageReferenced By:
RelationalAI Documentation
└── Build With RelationalAI
└── Understand how PyRel works > Build a semantic model
├── Declare concepts
└── Declare relationships and properties
└── Declare a property with Model.Property.select()
Section titled “.select()”Model.select(*args: StatementAndSchema) -> FragmentReturn a selection fragment in this model.
This starts a new Fragment with a select clause. The
returned fragment is composable and can be chained with
Fragment.where, Fragment.define, and
Fragment.require, then materialized via Fragment.to_df.
You can select most values and expressions, including:
- Concepts, relationships/properties, and fields
- Variables and expressions (including arithmetic and comparisons)
- Aggregates and grouped expressions (for example
per(x).sum(y)) - Python literals like strings, integers, floats, and booleans
Passing a TableSchema
(typically created via Table.to_schema)
expands to selecting each column of the table. Selecting a comparison
expression like Person.age >= 18 produces a boolean output column.
Parameters:
(*argsStatementAndSchema, default:()) - Items to select. Each item may be asemantics.frontend.base.Statementor aTableSchema.
Returns:
Fragment- A composable query fragment representing the selection.
Examples:
Select a value and a computed boolean column:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=30))>>> m.select(Person.name, Person.age >= 18).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Declare data sources │ ├── Use a Snowflake table withModel.Table│ ├── Use a DataFrame withModel.data│ ├── Use inline Python data withModel.data│ └── Create model constants withModel.Enum├── Derive facts with logic │ └── Understand PyRel logic constructs └── Query a model ├── Understand fragments and materialization └── Select values withselect
.where()
Section titled “.where()”Model.where(*args: Statement) -> FragmentReturn a fragment filtered by the given conditions.
This starts a new Fragment with a where clause. The
returned fragment is composable and can be chained with
Fragment.select, Fragment.define, and
Fragment.require, then materialized via Fragment.to_df.
Multiple conditions passed in a single call are combined with AND. You
can add additional filters by chaining
Fragment.where.
Parameters:
(*argsStatement, default:()) - One or moresemantics.frontend.base.Statementitems to use as filter conditions.
Returns:
Fragment- A composable query fragment representing the filter(s).
Examples:
Filter and select:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=30))>>> m.where(Person.age > 21).select(Person.name).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Derive facts with logic │ ├── Understand PyRel logic constructs │ └── Write conditional definitions withModel.whereandModel.define├── Define requirements │ └── Add a scoped requirement withModel.where└── Query a model └── Filter results withwhere
.require()
Section titled “.require()”Model.require(*args: Statement) -> FragmentReturn a fragment that adds requirements (constraints).
This starts a new Fragment with a require clause. The
returned fragment is composable and can be chained with
Fragment.where to make a requirement conditional, or with
Fragment.require to add additional requirements.
A requirement is an existence check: it fails when there are no matches.
Roughly speaking, m.require(expr) asserts EXISTS(expr) and fails
when NOT EXISTS(expr).
For example, m.require(Person.age > 0) fails in the following cases:
- There are no
Personentities. - There are
Personentities, but none have anagevalue. - There are
agevalues, but none are greater than zero.
It does not fail just because some Person entities are missing an
age value, or because some ages are non-positive, as long as there
is at least one match for Person.age > 0.
To enforce a per-entity requirement, scope it with
Model.where (for example,
where(Person).require(Person.age > 0)) or use Concept.require
(for example, Person.require(Person.age > 0)). Both of those forms are
equivalent and the choice is mostly a matter of style.
Multiple requirements passed in a single call are enforced together.
You can also apply requirements to an existing fragment by calling
Fragment.require.
Parameters:
(*argsStatement, default:()) - One or moresemantics.frontend.base.Statementitems to enforce as requirements.
Returns:
Fragment- A composable fragment representing the requirement(s).
Examples:
Require that at least one match exists:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=1), Person.new(name="Bob", age=2))>>> # Passes because there exists a Person with age >= 0.>>> m.require(Person.age >= 0)Scope a requirement to enforce it per entity:
>>> # Requires every Person to have an age >= 0.>>> m.where(Person).require(Person.age >= 0)Notes:
Requirements can affect performance:
- Adding more requirements generally increases compilation and execution
work, especially if a requirement introduces large joins, broad
disjunctions (for example via
Model.union), or aggregates over large domains. - A well-chosen requirement can also improve performance by narrowing what needs to be considered (for example by reducing the number of matches that flow into later joins or aggregates).
- Prefer scoped requirements (for example
m.require(...).where(...)) when the constraint should only apply under certain conditions.
Referenced By:
RelationalAI Documentation
└── Build With RelationalAI
└── Understand how PyRel works > Build a semantic model
└── Define requirements
├── How a requirement works
└── Add a global requirement with Model.require.define()
Section titled “.define()”Model.define(*args: Statement) -> FragmentReturn a fragment that adds definitions (facts or logic).
This starts a new Fragment with a define clause. The
returned fragment is composable and can be chained with
Fragment.where to make definitions conditional, or with
Fragment.define to add additional definitions.
Calling Model.define also
registers the resulting definition fragment with the model, so the
definitions are included when the model is compiled and evaluated by
later queries.
Parameters:
(*argsStatement, default:()) - One or moresemantics.frontend.base.Statementitems to define. Common examples include entity creations (Person.new(...)) and relationship/property facts.
Returns:
Fragment- A composable fragment representing the definition(s).
Examples:
Define some base facts:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=30))Define a derived concept under a condition:
>>> Adult = m.Concept("Adult", extends=[Person])>>> m.where(Person.age >= 18).define(Adult(Person))Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Declare concepts │ └── Declare a concept withModel.Concept├── Define base facts │ ├── Define entities and their properties withConcept.new│ └── Define relationship facts └── Derive facts with logic ├── Understand PyRel logic constructs ├── Write conditional definitions withModel.whereandModel.define└── Compute derived property values
.union()
Section titled “.union()”Model.union(*items: Value) -> UnionCombine items with a logical OR or a set-style union.
This constructs a Union object that represents a disjunction over multiple branches.
Unlike the | operator (see Variable.__or__), which picks the
first branch that can succeed, union combines the results from all branches.
union is commonly used in two ways:
- As an OR-combinator in
Model.whereclauses, for examplem.where(m.union(cond1, cond2)). - As a union of multiple derived tables/fragments that return the same number of values. The resulting union behaves like a derived table whose columns can be selected or unpacked.
Nested unions are flattened, so m.union(m.union(a, b), c) is
equivalent to m.union(a, b, c).
Parameters:
(*itemsValue, default:()) - One or moresemantics.frontend.base.Valueitems to union. Each branch must return the same number of values. Branches used purely as filters (for example comparisons, orNot) contribute zero output values and are intended to be used as conditions in awhereclause.
Returns:
Union- A composable union object.
Raises:
relationalai.util.error.RAIException- Raised if the union branches return different numbers of values.
Examples:
Use union to OR conditions in a where clause:
>>> from relationalai.semantics import Integer, Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Person.age = m.Property(f"{Person} has {Integer:age}")>>> m.define(Person.new(name="Alice", age=10), Person.new(name="Bob", age=70))>>> m.where(m.union(Person.age < 18, Person.age >= 65)).select(Person.name).to_df()Union two fragments that select the same number of values:
>>> Edge = m.Concept("Edge")>>> Edge.src = m.Property(f"{Edge} has {Integer:src}")>>> Edge.dst = m.Property(f"{Edge} has {Integer:dst}")>>> m.define(Edge.new(src=1, dst=2), Edge.new(src=4, dst=3))>>> e, src, dst = m.union(... m.where(Edge, Edge.src <= Edge.dst).select(Edge, Edge.src, Edge.dst),... m.where(Edge, Edge.src > Edge.dst).select(Edge, Edge.dst, Edge.src),... )>>> m.select(src, dst).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Derive facts with logic │ └── Write conditional definitions withModel.whereandModel.define└── Define requirements └── How a requirement works
.data()
Section titled “.data()”Model.data( data: DataFrame | list[tuple] | list[dict], columns: list[str] | None = None) -> DataCreate a temporary table from in-memory Python or pandas data.
This converts the provided data into a pandas DataFrame and wraps it
as a Data object. The resulting object behaves like a table in
the semantics DSL: you can refer to its columns in Model.select,
Model.where, and Model.define, or convert it to a schema
via Table.to_schema.
This method is the underlying implementation used by the convenience
wrapper data.
Parameters:
-
(datapandas.DataFrame|list[tuple] |list[dict]) - The input data. Supported forms are:- A pandas
DataFrame - A list of tuples (each tuple is a row)
- A list of dicts (each dict is a row)
- A pandas
-
(columnslist[str], default:None) - Column names to use whendatais a list of tuples. If omitted, the data will use default integer column labels0,1,2, … . Those default labels are exposed ascol0,col1,col2, … so you can writed.col0,d.col1, etc.
Returns:
Data- A table-like object backed by the provided data.
Raises:
TypeError- Ifdatacannot be converted into a pandasDataFrame.
Examples:
Create data from a list of dicts and use it directly:
>>> from relationalai.semantics import Model>>> m = Model()>>> people_rows = m.data([... {"name": "Alice", "age": 10},... {"name": "Bob", "age": 30},... ])>>> m.select(people_rows.name, people_rows.age).to_df()Provide column names when loading tuple rows:
>>> measurements = m.data([(0, 72.5), (1, 71.9)], columns=["minute", "temperature"])>>> m.select(measurements.minute, measurements.temperature).to_df()Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model ├── Declare data sources │ ├── Choose the right source type │ ├── Use CSV data withModel.data│ ├── Use a DataFrame withModel.data│ └── Use inline Python data withModel.data└── Define base facts
.not_()
Section titled “.not_()”Model.not_(*items: Statement) -> NotReturn a negated condition for use in query filters.
Use Model.not_ inside Model.where (or Fragment.where)
to say “exclude anything that matches this pattern”. For example,
m.not_(Person.pets) can be read as “people with no pets”.
If you pass multiple statements, they are treated as a single grouped
condition and the whole group is negated. In other words,
m.not_(a, b) means “NOT (a AND b)”, not “(NOT a) AND (NOT b)”.
Note that NOT (a AND b) is equivalent to (NOT a) OR (NOT b).
Parameters:
(*itemsStatement, default:()) - One or moresemantics.frontend.base.Statementitems to negate (for example, comparisons, relationship/property patterns, or other filter statements).
Returns:
Not- A negation expression.
Examples:
Filter to people who have no pets:
>>> from relationalai.semantics import Model, String>>> m = Model()>>> Person = m.Concept("Person", identify_by={"name": String})>>> Pet = m.Concept("Pet", identify_by={"name": String})>>> Person.pets = m.Relationship(f"{Person} has {Pet}")>>> m.define(... alice := Person.new(name="Alice"),... bob := Person.new(name="Bob"),... boots := Pet.new(name="boots"),... bob.pets(boots),... )>>> m.where(Person, m.not_(Person.pets)).select(Person.name).to_df()Exclude people who have a pet named “boots”:
>>> m.where(Person, m.not_(Person.pets.name == "boots")).select(Person.name).to_df()Contrast grouped vs separate negation:
>>> # Exclude people people who have no pets and people who have a pet named "boots".>>> m.where(Person, m.not_(Person.pets, Person.pets.name == "boots"))>>> # Exclude only people who have no pets.>>> m.where(Person, m.not_(Person.pets))Referenced By:
RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works > Build a semantic model └── Derive facts with logic └── Write conditional definitions withModel.whereandModel.define
.distinct()
Section titled “.distinct()”Model.distinct(*items: Value) -> DistinctMark values as distinct (unique) for use in Model.select and
aggregates.
This constructs a Distinct wrapper tied to this model. Use it
to request “unique values only” in two common situations:
- As a SQL
SELECT DISTINCT-style wrapper by passing it as the only argument toModel.select, e.g.m.select(m.distinct(x, y)). - As an argument wrapper for aggregates to make them count/sum/etc. over
unique values, e.g.
count(m.distinct(Person.name)).
Parameters:
(*itemsValue, default:()) - One or moresemantics.frontend.base.Valueitems to consider when removing duplicates.
Returns:
Distinct- An object you pass toModel.selector to aggregate functions to request “unique values only”.
Raises:
relationalai.util.error.RAIException- Raised when the returned object is used incorrectly (for example, not applied to the entireselect(...), or not applied to all aggregate arguments).
Examples:
Count unique names (like SQL COUNT(DISTINCT name)):
>>> from relationalai.semantics import Model, String, count>>> m = Model()>>> Person = m.Concept("Person")>>> Person.name = m.Property(f"{Person} has {String:name}")>>> m.define(... Person.new(name="Chris"),... Person.new(name="Chris"),... Person.new(name="Joe"),... )>>> m.select(count(m.distinct(Person.name))).to_df()Select unique rows by wrapping all selected values:
>>> name_counts = count(Person).per(Person.name)>>> m.select(m.distinct(Person.name, name_counts)).to_df()Used By
Section titled “Used By”. ├── agent > cortex │ ├── tool │ │ ├── DefaultTool │ │ └── ToolRegistry │ │ └── add │ └── verbalize │ ├── ModelVerbalizer │ └── SourceCodeVerbalizer └── semantics > frontend > base ├── Concept ├── DSLBase ├── Data ├── DerivedTable ├── Distinct ├── Fragment ├── Literal ├── Match ├── Not ├── NumberConcept ├── Property ├── Reading ├── Relationship ├── Table ├── Union └── Variable
Referenced By
Section titled “Referenced By”RelationalAI Documentation └── Build With RelationalAI └── Understand how PyRel works ├── Configure PyRel │ ├── Overview │ │ ├── Where configuration comes from │ │ └── How validation works │ └── Configure snowflake auth │ └── Get the connection by creating a session └── Build a semantic model ├── Create a model instance │ ├── Create a Model instance │ └── Configure a model’s connection ├── Declare concepts │ ├── Declare a concept withModel.Concept│ ├── Declare a concept with an identity scheme │ ├── Declare a subconcept withextends│ └── View all concepts declared in a model ├── Declare relationships and properties │ ├── Declare a property withModel.Property│ ├── Declare a relationship withModel.Relationship│ └── View all relationships declared in a model ├── Declare data sources │ ├── Choose the right source type │ ├── Use a Snowflake table withModel.Table│ ├── Use CSV data withModel.data│ ├── Use a DataFrame withModel.data│ ├── Use inline Python data withModel.data│ └── Create model constants withModel.Enum├── Define base facts │ ├── Define entities and their properties withConcept.new│ └── Define relationship facts ├── Derive facts with logic │ ├── Understand PyRel logic constructs │ ├── Write conditional definitions withModel.whereandModel.define│ ├── Compute derived property values │ └── Match multiple entities of the same type withConcept.ref├── Define requirements │ ├── How a requirement works │ ├── Add a global requirement withModel.require│ └── Add a scoped requirement withModel.where└── Query a model ├── Understand fragments and materialization ├── Select values withselect├── Filter results withwhere└── Export results to Snowflake tables withintoandexec