Writing Basic Queries#
This guide covers the fundamental building blocks of EQL: defining variables with variable(), selecting them with
entity(), and filtering them with .where().
Defining Variables#
All EQL queries start with symbolic variables. A variable represents a set of possible values (the domain) of a certain type.
1. Variables with Explicit Domains#
The most common way to define a variable is to provide a type and a collection of objects (the domain).
from krrood.entity_query_language.factories import variable
# Define a variable 'r' of type 'Robot' from a list
robots_list = [ExampleRobot("R1"), ExampleRobot("R2")]
r = variable(ExampleRobot, domain=robots_list)
2. Variables with Implicit Domains#
If no domain is provided, EQL will attempt to use a default domain. For types inheriting from Symbol,
it uses the global SymbolGraph.
# 'r' will use all Robots in the SymbolGraph
r = variable(ExampleRobot, domain=None)
Hint
Always provide a domain if you know it. It significantly improves query performance by narrowing the search space early.
Selecting Entities#
The entity() function specifies what you want to retrieve from the query. It returns a Query object that you can
further refine.
from krrood.entity_query_language.factories import entity
# We want to select the objects represented by 'r'
query = entity(r)
Note
If you need to select multiple variables at once, use set_of() instead.
Aliasing and Named Results#
When selecting multiple variables or complex expressions using set_of(), the query returns a result set where each item is a dictionary-like object (UnificationDict). To make it easier to refer to these results—both within the query and when iterating over results—you can use aliasing.
EQL supports both the walrus operator (:=) and standard Python assignment (=) for this purpose.
1. Inline Aliasing with the Walrus Operator (:=)#
The walrus operator (available in Python 3.8+) is the most concise way to alias an expression directly within the selection function.
from krrood.entity_query_language.factories import set_of, count
# Alias the count(r) expression to 'c' inline
query = set_of(r.type, c := count(r)).grouped_by(r.type)
for result in query.evaluate():
# Use the alias 'c' to index the result dictionary
print(f"Type: {result[r.type]} | Count: {result[c]}")
2. Aliasing with Normal Assignment (=)#
Alternatively, you can assign a symbolic expression to a Python variable before passing it to the query:
# Define the alias before the query
avg_batt = average(r.battery)
query = set_of(r.type, avg_batt).grouped_by(r.type)
for result in query.evaluate():
# Use the 'avg_batt' variable to access the result
print(f"Type: {result[r.type]} | Average Battery: {result[avg_batt]}%")
Why use Aliasing?#
Readability: Shorter names make complex queries easier to follow.
Result Access: It provides a clear handle for retrieving values from the result dictionary.
Code Reuse: You can define an expression once and use it in multiple parts of the query, such as in
.where(),.having(), or.ordered_by().
# Using an alias in multiple places
query = set_of(r.type, c := count(r)) \
.grouped_by(r.type) \
.having(c > 5) \
.ordered_by(c, descending=True)
Note
Aliasing is especially powerful when combined with aggregations, as it allows you to name the summary statistics you calculate.
Adding Filters with .where()#
The .where() method is used to add constraints to your query. You can pass multiple conditions, which are treated as
a logical AND. Constraints can be Comparison Operators (==, !=, <, <=, >, >=), custom functions,
predicates, or any boolean function/attribute that is accessed from a variable (e.g. 'abc'.startswith('a')).
# Select robots named 'R1'
query = entity(r).where(r.name == "R1")
Full Example: Finding High-Battery Robots#
Let’s combine these concepts into a complete, runnable example.
from dataclasses import dataclass
from krrood.entity_query_language.factories import variable, entity, an, Symbol
# Define our model
@dataclass
class ExampleRobot:
name: str
battery: int
# Prepare data
robots = [ExampleRobot("R2D2", 100), ExampleRobot("C3PO", 20), ExampleRobot("BB8", 80)]
# 1. Define the variable
r = variable(ExampleRobot, domain=robots)
# 2. Build the query with filters
# We want robots with battery levels greater than 50
query = an(entity(r).where(r.battery > 50))
# 3. Execute and see the results
for robot in query.evaluate():
print(f"Found {robot.name} with {robot.battery}% battery")
Found R2D2 with 100% battery
Found BB8 with 80% battery