Querying

SurrealEngine provides a powerful QuerySet API for building and executing database queries with a Django-style interface. This guide covers the query building features and operators available in SurrealEngine.

Basic Querying

Use the objects manager to build queries on your document models:

from surrealengine import Document, StringField, IntField

class User(Document):
    name = StringField(required=True)
    age = IntField()
    status = StringField()

    class Meta:
        collection = "users"

# Get all users
users = await User.objects.all()

# Filter users
active_users = await User.objects.filter(status="active")

# Chain filters
young_active = await User.objects.filter(status="active").filter(age__lt=30)

Field Selection

Omit Fields (v0.5.0+)

Exclude specific fields from query results to reduce data transfer:

# Omit sensitive fields
users = await User.objects.omit("password", "email").all()

# Combine with filters
users = await User.objects.filter(status="active").omit("password")

Select Specific Fields

Use only() to select only specific fields:

# Only get name and age
users = await User.objects.only("name", "age").all()

Query Performance

Timeout (v0.5.0+)

Set a timeout for query execution to prevent long-running queries:

# 5 second timeout
users = await User.objects.timeout("5s").all()

# 1 minute timeout
users = await User.objects.timeout("1m").filter(status="active")

Tempfiles (v0.5.0+)

Enable or disable temporary files for large query results:

# Enable tempfiles for large result sets
users = await User.objects.tempfiles(True).all()

# Disable tempfiles (default)
users = await User.objects.tempfiles(False).all()

Index Control (v0.5.0+)

Bypass indexes for specific queries:

# Don't use any indexes
users = await User.objects.no_index().filter(name="John")

Query Explanation (v0.5.0+)

Analyze query execution plans:

# Get basic execution plan
plan = await User.objects.filter(status="active").explain()
print(plan)

# Get full execution plan with trace
full_plan = await User.objects.filter(status="active").explain(full=True)
print(full_plan)

# Using the builder pattern
plan = await User.objects.filter(status="active").with_explain().all()

# Synchronous version
plan = User.objects.filter(status="active").explain_sync()

Advanced Filter Operators

Set Operators (v0.5.0+)

SurrealEngine supports advanced set operators for filtering:

# Contains any - check if field contains any of the values
users = await User.objects.filter(tags__contains_any=["python", "javascript"])

# Contains all - check if field contains all of the values
users = await User.objects.filter(tags__contains_all=["python", "javascript", "rust"])

# Contains none - check if field contains none of the values
users = await User.objects.filter(tags__contains_none=["php", "perl"])

# Inside - check if value is in the list
users = await User.objects.filter(status__inside=["active", "pending"])

# Not inside - check if value is not in the list
users = await User.objects.filter(status__not_inside=["banned", "deleted"])

# All inside - check if all field values are in the list
users = await User.objects.filter(tags__all_inside=["python", "javascript", "rust", "go"])

# Any inside - check if any field value is in the list
users = await User.objects.filter(tags__any_inside=["python", "javascript"])

# None inside - check if no field values are in the list
users = await User.objects.filter(tags__none_inside=["php", "perl"])

Standard Operators

# Equality
users = await User.objects.filter(name="John")
users = await User.objects.filter(name__eq="John")

# Inequality
users = await User.objects.filter(status__ne="banned")

# Greater than / Less than
users = await User.objects.filter(age__gt=18)
users = await User.objects.filter(age__gte=18)
users = await User.objects.filter(age__lt=65)
users = await User.objects.filter(age__lte=65)

# String operations
users = await User.objects.filter(name__contains="John")
users = await User.objects.filter(name__startswith="J")
users = await User.objects.filter(name__endswith="son")
users = await User.objects.filter(email__regex=r".*@example\.com")

# In operator
users = await User.objects.filter(status__in=["active", "pending"])

Grouping and Aggregation

Group By (v0.5.0+)

Group query results by one or more fields:

# Group by single field
results = await User.objects.group_by("status").all()

# Group by multiple fields
results = await User.objects.group_by("status", "age").all()

# Group all results (v0.5.0+)
results = await User.objects.group_by(all=True).all()

Complex Queries with Q Objects

Use Q objects for complex boolean logic:

from surrealengine import Q

# OR condition
users = await User.objects.filter(
    Q(status="active") | Q(status="pending")
)

# AND condition
users = await User.objects.filter(
    Q(status="active") & Q(age__gt=18)
)

# NOT condition
users = await User.objects.filter(~Q(status="banned"))

# Complex nested conditions
users = await User.objects.filter(
    Q(status="active") & (Q(age__lt=18) | Q(age__gt=65))
)

Ordering and Pagination

# Order by field
users = await User.objects.order_by("name")

# Descending order
users = await User.objects.order_by("-age")

# Multiple fields
users = await User.objects.order_by("status", "-age")

# Limit results
users = await User.objects.limit(10)

# Skip results
users = await User.objects.offset(20)

# Pagination
page = await User.objects.page(1, 20)  # page 1, 20 items per page

Synchronous Queries

All query methods have synchronous equivalents:

# Synchronous query
users = User.objects.filter(status="active").all_sync()

# Synchronous explain
plan = User.objects.filter(status="active").explain_sync()

# Get single object
user = User.objects.get_sync(id="user:123")

Query Chaining

All QuerySet methods return a new QuerySet, allowing for flexible query building:

# Build query progressively
query = User.objects.filter(status="active")
query = query.filter(age__gte=18)
query = query.omit("password")
query = query.timeout("5s")
query = query.order_by("-created_at")

# Execute
users = await query.all()

# Or chain in one statement
users = await (User.objects
    .filter(status="active")
    .filter(age__gte=18)
    .omit("password")
    .timeout("5s")
    .order_by("-created_at")
    .all())