Result Quantifiers#
In EQL, there are two result quantifiers: the and an.
the is used to fetch a single solution and assert that there is exactly one solution. This behaves like one in SQLAlchemy.
an is used to fetch all solutions (or any specified number of solutions). This creates an iterator which lazily
evaluates the query. This behaves like all in SQLAlchemy if not constraining result count using at_least, at_most, or exactly.
Let’s start with an example of a working query that requires exactly one result.
from dataclasses import dataclass
from typing_extensions import List
from krrood.entity_query_language.entity import entity, let, the, Symbol, an
from krrood.entity_query_language.result_quantification_constraint import AtLeast, AtMost, Exactly, Range
from krrood.entity_query_language.failures import MultipleSolutionFound, LessThanExpectedNumberOfSolutions, GreaterThanExpectedNumberOfSolutions
@dataclass
class Body(Symbol):
name: str
@dataclass
class World(Symbol):
id_: int
bodies: List[Body]
world = World(1, [Body("Body1"), Body("Body2")])
query = the(entity(body := let(Body, domain=world.bodies),
body.name.endswith("1")))
print(query.evaluate())
Body(name='Body1')
If there are multiple results, we get an informative exception.
query = the(entity(body := let(Body, domain=world.bodies)))
try:
query.evaluate()
except MultipleSolutionFound as e:
print(e)
More than 1 solutions found for the expression The(n==1).
We can also get all results using an.
query = an(entity(body := let(Body, domain=None)))
print(*query.evaluate(), sep="\n")
Body(name='Body1')
Body(name='Body2')
Result Count Constraints#
EQL allows constraining the number of results produced by an(...) using the keyword arguments at_least, at_most, and exactly.
Below we reuse the same World and Body setup from above. The world contains exactly two bodies, so all the following examples will evaluate successfully.
# Require at least two results
query = an(
entity(body := let(Body, domain=world.bodies)),
at_least=2,
)
print(len(list(query.evaluate()))) # -> 2
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
Cell In[4], line 2
1 # Require at least two results
----> 2 query = an(
3 entity(body := let(Body, domain=world.bodies)),
4 at_least=2,
5 )
7 print(len(list(query.evaluate()))) # -> 2
TypeError: an() got an unexpected keyword argument 'at_least'
You can also bound the number of results within a range using both at_least and at_most:
query = an(
entity(body := let(Body, domain=world.bodies)),
quantification=Range(AtLeast(1), AtMost(3))
)
print(len(list(query.evaluate()))) # -> 2
If you want an exact number of results, use exactly:
query = an(
entity(body := let(Body, domain=world.bodies)),
quantification=Exactly(2),
)
print(len(list(query.evaluate()))) # -> 2
Handling Unmatched Result Counts#
The result count constraints will raise informative exceptions when the number of results does not match the expectation. You can handle these with try/except and print the error message, for example:
# The world from above has exactly two bodies: Body1 and Body2
# at_least too high -> LessThanExpectedNumberOfSolutions
query = an(
entity(body := let(Body, domain=world.bodies)),
quantification=AtLeast(3),
)
try:
list(query.evaluate())
except LessThanExpectedNumberOfSolutions as e:
print(e)
# at_most too low -> GreaterThanExpectedNumberOfSolutions
query = an(
entity(body := let(Body, domain=world.bodies)),
quantification=AtMost(1),
)
try:
list(query.evaluate())
except GreaterThanExpectedNumberOfSolutions as e:
print(e)
# exactly mismatch -> can raise either LessThan... or GreaterThan...
query = an(
entity(body := let(Body, domain=world.bodies)),
quantification=Exactly(1),
)
try:
list(query.evaluate())
except (LessThanExpectedNumberOfSolutions, GreaterThanExpectedNumberOfSolutions) as e:
print(e)