Skip to content

relationalai.dsl.Producer

class Producer

Instances of the Producer class act as variables in a rule or query context. When the context is evaluated, all possible values for the variable are produced. Instead of constructing Producer instances directly, they are returned as the result of various operations.

The Producer class is the base class for different kinds of producers, including:

  • Instance, returned when you call a Type.
  • InstanceProperty, returned when you access a property of an object.
  • Expression, returned as the result of an operation on a producer, such as a mathematical or Boolean expression, or an aggregation.

The key idea behind the Producer class is that it is used to represent a variable in a rule or query context:

import relationalai as rai
# Create a model named 'people' with Person and Adult types.
model = rai.Model("people")
Person = model.Type("Person")
Adult = model.Type("Adult")
# Add some people to the model.
with model.rule():
# Type.add() returns an Instance, which is a producer.
alice = Person.add(name="Alice", age=8)
bob = Person.add(name="Bob", age=36)
# Instance producers have a .set() method for setting properties and
# additional types on objects.
bob.set(parent_of=alice)
# Create a rule that sets the Adult type on each person whose age is at least 18.
with model.rule():
# Calling a type returns an Instance producer.
person = Person() # person produces Person objects.
# person.age returns an InstanceProperty, which is also a producer.
# Comparing the age to 18 returns an Expression, yet another producer.
person.age >= 18 # Filter to people who are 18 or older.
person.set(Adult) # Set the adult type on the people who pass the filter.

You can think of the rule as executing once for each Person object in the model. Each line in the rule describes a step in a pipeline that the objects produced by person pass through:

  1. person Produces a Person object.
  2. person.age >= 18 only lets through objects with an age of 18 or more.
  3. person.set(Adult) sets the Adult type on the objects that pass through.

The object for Alice never reaches the last step because her age is less than 18. But the object for Bob does reach the last step, and the Adult type is set on it.

Multiple producers may be mixed in the same rule or query:

# Get pairs of people where the first person is younger than the second.
with model.query() as select:
person1, person2 = Person(), Person()
person1.age < person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name1 name2
# 0 Alice Bob

Both person1 and person2 produce Person objects. The query is evaluated for each pair of possible values for person1 and person2, of which there are four:

  • Alice and Alice
  • Alice and Bob
  • Bob and Alice
  • Bob and Bob

Only the pair (Alice, Bob) passes through the comparison and into select() since Alice is 8 and Bob is 36.

Producers can be used as context managers. This is especially useful for Expression producers, which can be used to create a subcontext that applies to a subset of objects:

Minor = model.Type("Minor")
with model.rule():
person = Person()
# If the person's age is less than 18, set the Minor type.
with person.age < 18:
person.set(Minor)
# If the person's age is at least 18, set the Adult type.
with person.age >= 18
person.set(Adult)

Thinking of the rule as a pipeline, the with statement creates a subcontext whose contents only apply to objects for which the Expression is true, but doesn’t block objects from passing through to the rest of the rule.

For example, the Alice object passes through the first with statement and gets the Minor type set on it since she is 8. Her object still passes through to the second with statement, but the Adult type is not set on her because the expression is false. Bob, on the other hand, passes through the first with statement without setting the Minor type, but the Adult type is set on him in the second with statement.

The following methods support mathematical operations on producers:

NameDescriptionReturns
__add__()Supports the + operator between producers and other values.Expression
__sub__()Supports the - operator between producers and other values.Expression
__mul__()Supports the * operator between producers and other values.Expression
__truediv__()Supports the / operator between producers and other values.Expression
__floordiv__()Supports the // operator between producers and other values.Expression
__pow__()Supports the ** operator between producers and other values.Expression
__mod__()Supports the % operator between producers and other values.Expression

The following methods support comparison operations on producers:

NameDescriptionReturns
__eq__()Supports == comparison between producers and other values.Expression
__ne__()Supports != comparison between producers and other values.Expression
__ge__()Supports >= comparison between producers and other values.Expression
__gt__()Supports > comparison between producers and other values.Expression
__le__()Supports <= comparison between producers and other values.Expression
__lt__()Supports < comparison between producers and other values.Expression

Instances of Producer support arbitrary attribute access and can be used as a context manager in a with statement:

NameDescriptionReturns
__getattribute__()Get a property of an object.InstanceProperty
__enter__()Enter the producer’s context.None
__exit__()Exit the producer’s context.None
Producer.__add__(other: Any) -> Expression

Returns an Expression that produces the sum of the Producer values and other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

You may sum a Producer with a number literal:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
age_after_next_birthday = person.age + 1
response = select(person.name, age_after_next_birthday)
print(response.results)
# Output:
# v
# name v
# 0 Fred 40
# 1 Wilma 37

You may also sum two Producer objects:

with model.rule():
fred = Person(name="Fred")
fred.set(cash=100.0, savings=200.0)
with model.rule():
wilma = Person(name="Wilma")
wilma.set(cash=90.0, savings=310.0)
with model.query() as select:
person = Person()
# `person.cash` and `person.savings` return `InstanceProperty`
# objects, which are also `Producer` objects.
total_assets = person.cash + person.savings
response = select(person.name, total_assets)
print(response.results)
# Output:
# name v
# 0 Fred 300.0
# 1 Wilma 400.0
Producer.__enter__() -> None

Producer objects can be used as context managers in a with statement to apply restrictions in a rule or query conditionally. In a with statement, Python calls the context manager’s .__enter__() method before executing the with block. After the with block executes, the with statement automatically executes the Producer.__exit__() method.

None.

You may use Producer objects in a with statement to set a property or Type conditionally:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
Adult = model.Type("Adult")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
Person.add(name="Pebbles", age=6)
# People who 18 years old or older are adults.
with model.rule():
person = Person()
with person.age >= 18:
person.set(Adult)
with model.query() as select:
adult = Adult()
response = select(adult.name, adult.age)
print(response.results)
# Output:
# name age
# 0 Fred 39
# 1 Wilma 36

The with person.age >= 18 block temporarily restricts person.age to values greater than or equal to 18. After Python executes the with block, the Producer.__exit__() method is called to remove the restriction. This allows you to write multiple conditional with statements in the same rule or query:

Child = model.Type("Child")
with model.rule():
person = Person()
with person.age >= 18:
person.set(Adult)
with person.age < 18:
person.set(Child)
with model.query() as select:
child = Child()
response = select(child.name, child.age)
print(response.results)
# Output:
# name age
# 0 Pebbles 6
Producer.__eq__(other: Any) -> Expression

Returns an Expression that restricts Producer to values equal to other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__eq__() method is called when you use the == operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are equal to 36.
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age == 36
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Wilma 36

You may use == with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age == person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# 0 Fred Fred
# 1 Wilma Wilma
Producer.__exit__() -> None

Producer objects can be used as context managers in a with statement to apply restrictions in a rule or query conditionally. In a with statement, Python calls the context manager’s .__enter__() method before executing the with block. After the with block executes, the with statement automatically executes the .__exit__() method.

See Producer.__enter__() for more information.

None

Producer.__floordiv__(other: Any) -> Expression

Returns an Expression that produces the quotient of the Producer values and other, rounded towards negative infinity. The type of the result is the same as the type of the producer’s values. .__floordiv__() is called when the // operator is used.

NameTypeDescription
otherAnyThe denominator of the floor division operation.

An Expression object.

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39, account_balance=-123.45)
Person.add(name="Wilma", age=36, account_balance=123.45)
with model.query() as select:
person = Person()
half_age = person.age // 2
response = select(person.name, half_age)
print(response.results)
# Output:
# name result
# 0 Fred 19
# 1 Wilma 18

The type of the result is the same as the type of the numerator in the division. Since the age property is an integer, the result is also an integer.

For negative numbers, the result is rounded towards negative infinity:

with model.query() as select:
person = Person()
response = select(person.account_balance // 2)
print(response.results)
# Output:
# result
# 0 -62.0
# 1 61.0
Producer.__ge__(other: Any) -> Expression

Returns an Expression that restricts Producer to values greater than or equal to other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__ge__() method is called when you use the >= operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are greater than or equal
# to 36. `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age >= 36
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Fred 39
# 1 Wilma 36

You may use >= with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age > person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# 0 Fred Wilma
Producer.__getattribute__(name: str) -> InstanceProperty | None

Restricts the values produced to those for which a property named name is set and returns an InstanceProperty object. .__getattribute__() is called whenever you access a property using dot notation, such as book.title, or person.age.

NameTypeDescription
namestrThe name of the property to get.

An InstanceProperty object, except for Expression objects, in which case .__getattribute__() returns None.

It is essential to keep in mind that property access adds a constraint to your context. For example, the following query only returns objects in Person that have a name and age property:

# Add a person with an age property.
with model.rule():
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person` to objects with an `age` property.
person.age
response = select(person.name)
# Fred is not included in the results because he has no `age` property.
print(response.results)
# Output:
# name
# 0 Wilma
Producer.__gt__(other: Any) -> Expression

Returns an Expression object that restricts the values represented by the Producer object to the values that are strictly greater than other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__gt__() method is called when you use the > operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are strictly greater
# than 36. `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age > 36
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Fred 39

You may use > with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age > person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# 0 Fred Wilma
Producer.__le__(other: Any) -> Expression

Returns an Expression that restricts Producer to values less than or equal to other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__le__() method is called when you use the <= operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are greater than or equal
# to 36. `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age <= 39
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Fred 39
# 1 Wilma 36

You may use <= with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age <= person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# 0 Wilma Fred
Producer.__lt__(other: Any) -> Expression

Returns an Expression that restricts Producer to values strictly less than other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__lt__() method is called when you use the < operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are strictly less
# than 39. `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age < 39
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Wilma 36

You may use < with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age < person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# 0 Wilma Fred
Producer.__mod__(other: Any) -> Expression

Returns an Expression object representing the remainder of dividing the Producer and another value.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object that produces values of the same type as other.

You may calculate the remainder of dividing a Producer by a number literal:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
# Get people with an even age.
with model.query() as select:
person = Person()
person.age % 2 == 0
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Wilma 36

You may also calculate the remainder of dividing two Producer objects:

with model.query() as select:
fred = Person(name="Fred")
wilma = Person(name="Wilma")
response = select(fred.age % wilma.age)
print(response.results)
# Output:
# v
# 0 3
Producer.__mul__(other: Any) -> Expression

Returns an Expression the produces the product of the Producer values and other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

You may multiply a Producer object by a number literal:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
double_age = person.age * 2
response = select(person.name, double_age)
print(response.results)
# Output:
# name v
# 0 Fred 78
# 1 Wilma 72

You may also multiply two Producer objects:

with model.rule():
fred = Person(name="Fred")
fred.set(hours=30.0, wage=20.0)
with model.rule():
wilma = Person(name="Wilma")
wilma.set(hours=40.0, wage=30.0)
with model.query() as select:
person = Person()
# `person.hours` and `person.wage` return `InstanceProperty`
# objects, which are also `Producer` objects.
pay = person.hours * person.wage
response = select(person.name, pay)
print(response.results)
# Output:
# name v
# 0 Fred 600.0
# 1 Wilma 1200.0
Producer.__ne__(other: Any) -> Expression

Returns an Expression that restricts Producer to values not equal to other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

The Producer.__ne__() method is called when you use the != operator with a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# Restrict `person.age` to values that are equal to 36.
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
person.age != 36
response = select(person.name, person.age)
print(response.results)
# Output:
# name age
# 0 Fred 39

You may use != with two Producer objects:

with model.query() as select:
person1, person2 = Person(), Person()
person1.age != person2.age
response = select(person1.name, person2.name)
print(response.results)
# Output:
# name name2
# name name2
# 0 Fred Wilma
# 1 Wilma Fred
Producer.__pow__(exp: Any) -> Expression

Returns an Expression the produces the values of the Producer to the power of exp.

NameTypeDescription
expAnyA numeric value or another Producer object.

An Expression object.

You may raise a Producer object by a number literal:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
square_age = person.age ** 2
response = select(person.name, square_age)
print(response.results)
# Output:
# name result
# 0 Fred 1521
# 1 Wilma 1296

You may also use a Producer as the exponent:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
val = 1.01 ** person.age # A producer can be the exponent.
response = select(person.name, val)
print(response.results)
# Output:
# name result
# 0 Fred 1.474123
# 1 Wilma 1.430769
Producer.__radd__(other: Any) -> Expression

Returns an Expression that produces the sum of the Producer values and other. .__radd__() is implemented so that you may use a non-Producer object as the left operand.

Producer.__rfloordiv__(other: Any) -> Expression

Returns an Expression that produces the quotient of other and the Producer values, rounded towards negative infinity. The type of the result is the same as the type of other. .__rfloordiv__() is implemented so that you may use a non-Producer object as the left operand with the // operator.

Producer.__rmod__(other: Any) -> Expression

Returns an Expression object representing the remainder of dividing other by the values of the Producer. .__rmod__() is implemented so that you may use a non-Producer object as the left operand.

Producer.__rmul__(other: Any) -> Expression

Returns an Expression that produces the product of Producer values and other. .__rmul__() is implemented so that you may use a non-Producer object as the left operand.

Producer.__rpow__(base: Any) -> Expression

Returns an Expression that produces the power of base raised to the values produced by the Producer. .__rpow__() is implemented so that you may use a non-Producer object as the base of the exponent.

Producer.__rsub__(other: Any) -> Expression

Returns an Expression that produces the difference between the Producer values and other. .__rsub__() is implemented so that you may use a non-Producer object as the left operand.

Producer.__rtruediv__(other: Any) -> Expression

Returns an Expression that produces the quotient of the Producer values and other. .__rtruediv__() is implemented so that you may use a non-Producer object as the left operand.

Producer.__sub__(other: Any) -> Expression

Returns an Expression that produces the difference between the Producer values and other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

You may subtract a number literal from a Producer object:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# `person.age` returns an `InstanceProperty` object,
# which are also `Producer` objects.
years_as_adult = person.age - 18
response = select(person.name, years_as_adult)
print(response.results)
# Output:
# name v
# 0 Fred 21
# 1 Wilma 18

You may also subtract two Producer objects:

with model.rule():
fred = Person(name="Fred")
fred.set(expected_retirement_age=65)
with model.rule():
wilma = Person(name="Wilma")
wilma.set(expected_retirement_age=62)
with model.query() as select:
person = Person()
# `person.age`, and `person.expected_retirement_age` return
# `InstanceProperty` objects, which are also `Producer` objects.
years_to_retirement = person.retirement_age - person.age
response = select(person.name, years_to_retirement)
print(response.results)
# Output:
# name v
# 0 Fred 26
# 1 Wilma 26
Producer.__truediv__(other: Any) -> Expression

Returns an Expression that produces the quotient of the Producer values and other.

NameTypeDescription
otherAnyA numeric value or another Producer object.

An Expression object.

A Producer object may be divided by a numeric literal:

import relationalai as rai
model = rai.Model("people")
Person = model.Type("Person")
with model.rule():
Person.add(name="Fred", age=39)
Person.add(name="Wilma", age=36)
with model.query() as select:
person = Person()
# `person.age` returns an `InstanceProperty` object,
# which is also a `Producer` object.
percent_life_completed = person.age / 76
response = select(person.name, percent_life_completed)
print(response.results)
# Output:
# v
# name v
# 0 Fred 0.513158
# 1 Wilma 0.473684

You may also divide two Producer objects:

with model.rule():
fred = Person(name="Fred")
fred.set(savings=200.0, savings_goal=1000.0)
with model.rule():
wilma = Person(name="Wilma")
wilma.set(savings=300.0, savings_goal=500.0)
with model.query() as select:
person = Person()
# `person.savings`, and `person.savings_goal`return
# `InstanceProperty` objects, which are also `Producer` objects.
percent_goal_completed = savings / savings_goal
response = select(person.name, percent_goal_completed)
print(response.results)
# Output:
# name v
# 0 Fred 0.2
# 1 Wilma 0.6