Model.match()#

relationalai
#Model.match(multiple: bool = False, dynamic: bool = True) -> Context

Matches objects to one or more subqueries. Similar to a Python match or a SQL CASE statement. Must be used in a rule or query context.

Parameters#

NameTypeDescription
multipleboolWhether or not the context matches multiple queries. If True, objects match all subqueries that filter them. If False, objects match only the first.
dynamicboolWhether or not the context is dynamic. Dynamic queries support Python control flow as macros. See Context for more information.

Returns#

A Context object.

Example#

Model.match() is a context manager and should be called in a with statement. It must be called from within a rule or query context:

#import relationalai as rai

model = rai.Model("students")
Student = model.Type("Student")

with model.rule():
    Student.add(name="Fred", grade=87)
    Student.add(name="Johnny", grade=65)
    Student.add(name="Mary", grade=98)

with model.rule():
    student = Student()
    with model.match():
        # Match students to subqueries based on their grade.
        with student.grade >= 90:
            student.set(letter_grade="A")
        with student.grade >= 80:
            student.set(letter_grade="B")
        with student.grade >= 70:
            student.set(letter_grade="C")
        # Default case when no other conditions are met.
        with model.case():
            student.set(letter_grade="F")

# Which students got a B?
with model.query() as select:
    student = Student(letter_grade="B")
    response = select(student.name, student.grade, student.letter_grade)

print(response.results)
# Output:
#    name  grade letter_grade
# 0  Fred     87            B

You can think of consecutive with statements inside of a Model.match() context as branches of an if-else statement. In the example above, the letter_grade property of student objects is set based on their grade property. Only the first matching with statement is applied to each object.

To match multiple subqueries, set the multiple parameter to True:

#with model.rule():
    student = Student()
    with model.match(multiple=True):
        with student.grade >= 90:
            student.set(letter_grade="A")
        with student.grade >= 80:
            student.set(letter_grade="B")
        with student.grade >= 70:
            student.set(letter_grade="C")
        with model.case():
            student.set(letter_grade="F")

# Which students got a B?
with model.query() as select:
    student = Student(letter_grade="B")
    response = select(student.name, student.grade, student.letter_grade)

print(response.results)
# Output:
#    name  grade letter_grade
# 0  Fred     87            B
# 1  Mary     98            B

Mary has .letter_grade set to "B". She also has .letter_grade set to "A" and "C" because her grade meets the conditions in the first three of the four with statements.

Set dynamic to True to use Python control flow inside of the .match() context. This allows you to leverage Python as a macro language to generate subqueries dynamically:

#with model.rule():
    student = Student()
    with model.match(dynamic=True):
        for grade, letter in [(90, "A"), (80, "B"), (70, "C")]:
            with student.grade >= grade:
                student.set(letter_grade=letter)
        with model.case():
            student.set(letter_grade="F")

.match().__enter__() returns an optional ContextSelect object that may be used to group objects matched by the context for further manipulation. For instance, the following example calculates student letter grades in a query, not a rule, and filters students who got a B:

## Which students got a B?
# This time, we're calculating the letter grade in a query, not a rule.
with model.query() as select:
    student = Student()
    with model.match(dynamic=True) as matched:
        for grade, letter in [(90, "A"), (80, "B"), (70, "C")]:
            with student.grade >= grade:
                matched.add(student, letter_grade=letter)
        with model.case():
            matched.add(student, letter_grade="F")
    # Filter students who got a B.
    matched.letter_grade == "B"
    response = select(students.name, students.grade, students.letter_grade)

print(response.results)
# Output:
#    name  grade  v
# 0  Fred     87  B

You can only use with statements directly under a Model.match() block:

## INCORRECT
with model.rule():
    with model.match():
        student = Student()  # Raises an error
        with student.grade >= 90:
            student.set(letter_grade="A")
        # ...

# CORRECT
with model.rule():
    student = Student()
    with model.match():
        with student.grade >= 90:
            student.set(letter_grade="A")
        # ...

# - OR -

with model.rule():
    with model.match():
        with model.case():
            student = Student()  # OK because it's nested under a `with` statement
            student.grade >= 90
            student.set(letter_grade="A")
        # ...

See Also#