Skip to content

API Reference

/

Values

Range

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.

Import
from surrealdb import BoundExcluded, BoundIncluded, Range

Source: range.py

Syntax
Range(begin, end)

Parameter

Type

Description

beginBoundIncluded | BoundExcluded | None

The start of the range, or

None

for a range with no start.

endBoundIncluded | BoundExcluded | None

The end of the range, or

None

for 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.

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.

ClassThe value is part of the rangeExample
BoundIncluded(value)YesBoundIncluded(1)
BoundExcluded(value)NoBoundExcluded(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))
Output
1..10
1..=10
1>..=10
..10
1..

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])
Output
['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.

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()))
Output
['Second', 'Third']
Range(begin=BoundIncluded(value=1), end=BoundExcluded(value=5))

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.

Was this page helpful?