A Range holds a span of values between two bounds, and maps to the SurrealDB range type in both directions. Use one as the ID of a RecordID to target every record whose ID is in the span, or as a value in a query. This page documents Range and its two bound classes.
from surrealdb import BoundExcluded, BoundIncluded, RangeSource: range.py
Constructor
Range(begin, end)Parameter | Type | Description |
|---|---|---|
begin | BoundIncluded | BoundExcluded | None | The start of the range, or Nonefor a range with no start. |
end | BoundIncluded | BoundExcluded | None | The end of the range, or Nonefor a range with no end. |
Range is a dataclass with the fields begin and end. An open end given as None is stored as Null, which is how SurrealDB sends an open end, so a range that you build compares equal to the same range read back.
Bound classes
Each end of a range is a bound that either includes or excludes its value. Both classes take the value as their only argument and keep it in their value attribute, and both are subclasses of Bound, which is also exported.
| Class | The value is part of the range | Example |
|---|---|---|
BoundIncluded(value) | Yes | BoundIncluded(1) |
BoundExcluded(value) | No | BoundExcluded(10) |
str() renders a range as SurrealQL range text:
print(Range(BoundIncluded(1), BoundExcluded(10)))
print(Range(BoundIncluded(1), BoundIncluded(10)))
print(Range(BoundExcluded(1), BoundIncluded(10)))
print(Range(None, BoundExcluded(10)))
print(Range(BoundIncluded(1), None))1..10
1..=10
1>..=10
..10
1..Ranges of records
A RecordID whose ID is a Range targets every record in the table whose ID is in the range. select(), update() and delete() with such a target act on all of those records, and return a list. Type checkers still describe the result as one record, because the target is a RecordID.
from surrealdb import BoundIncluded, Range, RecordID, Surreal
with Surreal("ws://localhost:8000") as db:
db.signin({"username": "root", "password": "secret"})
db.use("surrealdb", "docs")
for n in range(1, 5):
db.create(RecordID("person", n), {"name": f"Person {n}"})
span = RecordID("person", Range(BoundIncluded(2), BoundIncluded(3)))
people = db.select(span).execute()
print([person["name"] for person in people])['Person 2', 'Person 3']A plain string such as "person:2..3" is a table name, and a StringRecordId refuses a range, so a RecordID with a Range is the way to target a range of records.
Ranges as values
Pass a Range as a query variable to test whether a value is in the range with IN. A range that the server returns reads back as a Range.
from surrealdb import BoundIncluded, Range, Surreal, Table
with Surreal("ws://localhost:8000") as db:
db.signin({"username": "root", "password": "secret"})
db.use("surrealdb", "docs")
for year, title in [(2019, "First"), (2022, "Second"), (2025, "Third")]:
db.create(Table("post"), {"title": title, "year": year})
years = Range(BoundIncluded(2020), BoundIncluded(2025))
sql = """
SELECT VALUE title FROM post
WHERE year IN $years ORDER BY title
"""
print(db.query(sql, {"years": years}).first())
print(repr(db.query("RETURN 1..5").first()))['Second', 'Third']
Range(begin=BoundIncluded(value=1), end=BoundExcluded(value=5))Equality and hashing
Two ranges are equal when both their bounds are equal, and two bounds are equal when they are of the same class and hold equal values. A range is hashable when the values in its bounds are hashable.
See also
Python values for every value class
RecordID for record targets
Ranges in SurrealQL