The query and CRUD methods of the Python SDK return builders. A builder holds one operation and sends nothing to the database until you run it. This page lists the methods of each builder, and the objects that the streaming methods return. For examples, see Executing queries, Data manipulation and Streaming results.
| Builder | Returned by |
|---|---|
| CRUD builder | select(), create(), update(), upsert(), delete() and relate() |
| Insert builder | insert() |
| Query builder | query() |
| Into builder | .into() on an asynchronous query builder |
| Batch | batch() |
Connections, sessions and transactions return the same builders. The blocking classes start with Sync and the asynchronous classes with Async. Every class on this page except the batch is exported from surrealdb, so you can use the names in type annotations.
Running a builder
| Blocking connection | Asynchronous connection | |
|---|---|---|
| Run the builder | .execute(), a data clause such as .merge(data), or iteration | await builder, await builder.execute(), or async for |
| A write given its data | Runs at once and returns the result, not a builder | Returns a builder with the data set, to await |
| Read the records one at a time | for row in builder or .rows() | async for row in builder or .rows() |
These rules apply to every builder:
A builder runs once. Running it again returns the first result and sends no second request. That includes
.execute()after a data clause, and awaiting one builder from two tasks.A builder that has run cannot change. A data clause or a modifier after the builder has run raises
SurrealError: Cannot reconfigure a SyncCrudBuilder after it has executed.A streamed builder cannot run again.
.execute()orawaitafter.rows(),.statements()or.stream()raisesSurrealError, because the operation has already run.A builder that is never run warns. When Python discards a builder that never ran, the SDK emits a
RuntimeWarningthat names the call and the line it was made on.A blocking builder is not its result.
len()and indexing raiseTypeError, andbool()is alwaysTrue.A builder has one owner. Running a blocking builder from several threads sends one request, but changing it from several threads at the same time is not safe.
CRUD builder
SyncCrudBuilder[T] on a blocking connection, AsyncCrudBuilder[T] on an asynchronous connection. T is the type of the result: a record as a dict, None, a list of records, or the class that into= names.
Data clauses
A data clause sets the data of a write. On a blocking connection, it runs the statement and returns the result. On an asynchronous connection, it returns the builder, so you can add modifiers before await.
| Clause | SurrealQL | Taken by |
|---|---|---|
.content(data) | CONTENT | create(), update(), upsert(), relate() |
.replace(data) | REPLACE | create(), update(), upsert() |
.merge(data) | MERGE | create(), update(), upsert() |
.patch(data) | PATCH | create(), update(), upsert() |
A clause on a builder that does not take it raises SurrealError.
.content()
Sets the whole content of each record. create(target, data), update(target, data), upsert(target, data) and relate(from_, edge, to, data) use this clause.
builder.content(data)Returns: the result of the statement on a blocking connection, or the builder on an asynchronous connection.
.replace()
Replaces the whole content of each record, with the SurrealQL REPLACE clause. Unlike CONTENT, it raises an error when data changes a READONLY field.
builder.replace(data)Returns: the result of the statement on a blocking connection, or the builder on an asynchronous connection.
.merge()
Changes the fields that data names and keeps every other field. A nested object merges at every level, and an array replaces the stored array.
builder.merge(data)Returns: the result of the statement on a blocking connection, or the builder on an asynchronous connection.
.patch()
Applies a list of JSON Patch operations. data is the list, and each operation is a dictionary such as {"op": "replace", "path": "/age", "value": 36}.
builder.patch(data)Returns: the result of the statement on a blocking connection, or the builder on an asynchronous connection.
Modifiers
A modifier adds a clause to the statement and returns the builder, so you can chain modifiers. A modifier that the statement does not take raises SurrealError when you call it. On a blocking connection, call the modifiers before the data clause, because the data clause runs the statement.
| Modifier | SurrealQL | select | create | update | upsert | delete | relate |
|---|---|---|---|---|---|---|---|
.where() | WHERE | Yes | Yes | Yes | Yes | ||
.fetch() | FETCH | Yes | |||||
.limit() | LIMIT | Yes | |||||
.start() | START | Yes | |||||
.version() | VERSION | Yes | |||||
.output() | RETURN | Yes | Yes | Yes | Yes | Yes | |
.timeout() | TIMEOUT | Yes | Yes | Yes | Yes | Yes | Yes |
.where()
Limits the statement to the records that match a condition.
builder.where(condition, variables=None)Parameter | Type | Description |
|---|---|---|
condition | str | A SurrealQL expression, such as "name = $name". An empty condition raises ValueError. |
variables | Mapping[str, Value] | None | The values of the $namevariables in condition. The names _resource, _content, _data, _patches, _version, _from, _toand _edgeraise ValueError, because the builder uses them itself. |
Returns: the builder
db.select(Table("person")).where("age > $min_age", {"min_age": 30}).execute() .fetch()
Replaces each record link at the field paths that you name with the record that the link points to.
builder.fetch(*fields)fields are one or more field paths, such as "author" or "author.friends". A path that is not a field name, or field names separated by dots, raises ValueError.
Returns: the builder
.limit()
Returns at most count records.
builder.limit(count)count is a whole number of at least 0, or the call raises ValueError.
Returns: the builder
.start()
Skips the first offset records.
builder.start(offset)offset is a whole number of at least 0, or the call raises ValueError.
Returns: the builder
.version()
Reads the records as they were at a point in time. Needs a datastore that keeps versions, such as SurrealKV with versioning (surrealkv+versioned://). Other datastores raise ConfigurationError.
builder.version(at)at is a datetime.datetime or a Datetime. A datetime without a time zone is read as UTC.
Returns: the builder
.output()
Sets what the statement returns, with the SurrealQL RETURN clause.
builder.output(mode)Parameter | Type | Description |
|---|---|---|
mode | str | Sequence[str] | "after"for each record after the change, the default for every write except delete(). "before"for each record before the change, the default for delete(). "diff"for the change as JSON Patch operations, "none"for nothing, "null"for Null, or a list of field paths for only those fields. Another string raises ValueError. |
Returns: the builder
db.update(RecordID("person", "tobie")).output("diff").merge({"age": 37}) .timeout()
Makes the server stop the statement when it takes longer than duration, with the SurrealQL TIMEOUT clause. A statement that takes too long raises QueryError, with is_timed_out set to True. How long the client waits for a reply is a separate setting: see db.timeout().
builder.timeout(duration)duration is a Duration, a datetime.timedelta, a duration string such as "5s", or a whole number of seconds as an int. A duration of zero or less raises ValueError, and a float raises TypeError.
Returns: the builder
.execute()
Runs the statement, with the data clause and modifiers that you set, and returns its result.
builder.execute()Returns: T, the result of the statement. On an asynchronous connection, await it: await builder.execute() is the same as await builder.
.rows()
Reads the records that the statement returns, one at a time, as the server produces them. Nothing is sent until the iteration starts. See Streaming results.
builder.rows(*, into=None, require_streaming=False)Parameter | Type | Description |
|---|---|---|
into | type | None | A class to map each record onto. Defaults to the into=of the method that made the builder. |
require_streaming | bool | Trueraises UnsupportedFeatureErrorwhere the records cannot be streamed, instead of reading them the buffered way. Defaults to False. |
Returns: SyncRows[T], or AsyncRows[T] on an asynchronous connection
Iterating and awaiting
Iterating a CRUD builder is the same as iterating builder.rows(): for row in builder on a blocking connection, and async for row in builder on an asynchronous connection. Awaiting an asynchronous CRUD builder is the same as await builder.execute().
Insert builder
SyncInsertBuilder[T] on a blocking connection, AsyncInsertBuilder[T] on an asynchronous connection. insert(table, data) on a blocking connection runs at once, so you get this builder from insert(table) without data, or from any insert() on an asynchronous connection.
.content()
Sets the records to insert: a dict for one record, or a list of them.
builder.content(data)Returns: the inserted records as a list on a blocking connection, or the builder on an asynchronous connection
.relation()
Makes the statement INSERT RELATION, so each record is an edge record with an in and an out field. insert(table, data, relation=True) does the same.
builder.relation()Returns: the builder, also on a blocking connection
.output() and .timeout()
The insert builder takes .output() and .timeout(), which work as on the CRUD builder. It takes no other modifier.
.execute()
Runs the INSERT statement. A builder without data raises SurrealError.
builder.execute()Returns: list[T], the inserted records, also for one record
.rows()
Reads the inserted records one at a time. It takes the same arguments as the .rows() of the CRUD builder, and iterating the builder is the same as iterating .rows().
builder.rows(*, into=None, require_streaming=False)Returns: SyncRows[T], or AsyncRows[T] on an asynchronous connection
Query builder
SyncQueryBuilder on a blocking connection, AsyncQueryBuilder on an asynchronous connection, which query() returns.
.execute()
Runs the query and returns one result for each statement. When a statement fails, it raises the error of the first statement that failed, after the server has run the whole query.
builder.execute()Returns: list[Value], one entry for each statement, also for one statement. On an asynchronous connection, await builder is the same as await builder.execute().
.first()
Runs the query and returns the result of the first statement.
builder.first()Returns: Value, or None when the query has no statements
.into()
Maps the results of the query onto a class: a dataclass, a pydantic BaseModel, or any class that takes keyword arguments.
builder.into(cls, *, rows=False)Parameter | Type | Description |
|---|---|---|
cls | type | The class to map onto. A value that is not a class raises TypeError. |
rows | bool | Falsemaps the results of the statements, in order, onto the fields of cls, so clsneeds one field for each statement. Truemaps each record of the first statement's result onto cls. Defaults to False. |
Returns: on a blocking connection, the query runs and the call returns one cls, or a list[cls] with rows=True. On an asynchronous connection, an into builder to await. A result that does not match the class raises UnexpectedResponseError.
.rows()
Reads the rows of every statement of the query, one statement after the other, as the server produces them. A statement with one value gives that value as one row, and a statement whose result is NONE gives no row.
builder.rows(*, into=None, require_streaming=False)Parameter | Type | Description |
|---|---|---|
into | type | None | A class to map each row onto. Each row must then be a record. Defaults to None. |
require_streaming | bool | Trueraises UnsupportedFeatureErrorwhere the rows cannot be streamed. Defaults to False. |
Returns: SyncRows, or AsyncRows on an asynchronous connection
.statements()
Reads one complete result for each statement, as each statement finishes. The loop raises the error of the first statement that fails.
builder.statements(*, require_streaming=False)Returns: a stream of StatementResult values: a QueryStream, or an AsyncQueryStream on an asynchronous connection. You use it in the same way as SyncRows.
.stream()
Reads every value, error and completion of the query as a frame. A statement that fails gives an ErrorFrame and does not stop the loop. A query that cannot finish at all still raises.
builder.stream(*, require_streaming=False)Returns: a stream of ValueFrame, ErrorFrame and DoneFrame values: a QueryStream, or an AsyncQueryStream on an asynchronous connection
Iterating and awaiting
Iterating a query builder is the same as iterating builder.rows(). Awaiting an asynchronous query builder is the same as await builder.execute().
Into builder
AsyncQueryIntoBuilder[T], which .into() returns on an asynchronous query builder. It shares the request of its query builder, so awaiting the query builder and its into builder sends one request.
await builder.into(cls, rows=False)
await builder.into(cls, rows=False).execute()Returns: when awaited, one cls, or a list[cls] with rows=True
Batch
The object that batch() returns on a connection or a session. It collects statements and sends them in one request, as one transaction. See Executing queries for the rules.
.query()
Adds statements to the batch.
batch.query(query, vars=None)Parameter | Type | Description |
|---|---|---|
query | str | One or more SurrealQL statements. A statement that begins, commits or cancels a transaction raises ValueError. |
vars | Mapping[str, Value] | None | Variables for the statements. Every statement of the batch can use them, and a name that the batch already holds with another value raises ValueError. |
Returns: the batch, so you can chain calls
.execute()
Sends the statements, wrapped in BEGIN TRANSACTION and COMMIT TRANSACTION. When a statement fails, it raises the error of that statement, and none of the statements are applied. An empty batch, or a RETURN that is not the last statement, raises ValueError.
batch.execute()Returns: list[Value], one result for each statement that you added. On an asynchronous connection, await it: the batch itself is not awaitable.
Rows
SyncRows[T] on a blocking connection, AsyncRows[T] on an asynchronous connection, which .rows() returns. A row is one item of the result of a statement: usually a record, or a value such as a name from SELECT VALUE. Both classes are exported for type annotations. The streams that .statements() and .stream() return, QueryStream and AsyncQueryStream, work in the same way.
| Use | Blocking | Asynchronous |
|---|---|---|
| Read the rows | for row in rows: | async for row in rows: |
| Stop the query when the block ends | with rows as stream: | async with rows as stream: |
| Stop the query | rows.close() | await rows.aclose() |
A stream is read once: a second loop raises SurrealError. The query starts when the loop starts. Rows are provisional until the loop ends without an exception, because a statement that fails raises from the loop after its rows have arrived.
StatementResult
One complete result of a statement, which .statements() yields and DoneFrame holds. It is a frozen dataclass.
| Field | Type | Description |
|---|---|---|
index | int | The position of the statement in the query, from 0 |
value | Value | The result of the statement: a list of rows, or one value when single is True |
time | str | The time that the server took, as text such as "1.5ms" |
query_type | str | None | "live" for a LIVE SELECT, "kill" for a KILL, otherwise None |
single | bool | True when value is one value, such as the result of RETURN 1 or SELECT ... FROM ONLY |
Frames
The values that .stream() yields. Each one is a frozen dataclass with the index of the statement that it belongs to.
ValueFrame
One row of a statement, or the whole value of a statement with one value. The value is provisional until the DoneFrame of the same statement.
| Field | Type | Description |
|---|---|---|
index | int | The position of the statement in the query |
value | Value | The row or the value |
ErrorFrame
A statement that failed. It takes back every ValueFrame of the same statement. The statements after it still run.
| Field | Type | Description |
|---|---|---|
index | int | The position of the statement in the query |
error | ServerError | The error of the statement, such as a ThrownError |
time | str | The time that the server took |
query_type | str | None | "live", "kill" or None, as for StatementResult |
DoneFrame
A statement that is complete.
| Field | Type | Description |
|---|---|---|
index | int | The position of the statement in the query |
result | StatementResult | The complete result of the statement |
See also
Surreal for the methods that return these builders
SurrealTransaction for builders inside a transaction
Streaming results for
.rows(),.statements()and.stream()Errors for the errors that a builder raises