Skip to content

Commit 64d26c5

Browse files
authored
feat: basic queries (#12)
* fix lint issues * add query query_one count
1 parent 7710b87 commit 64d26c5

12 files changed

Lines changed: 449 additions & 73 deletions

File tree

.zed/settings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"languages": {
33
"Python": {
4-
"language_servers": ["ruff", "!pyright", "ty", "baseright"],
4+
"language_servers": ["ruff", "ty", "basedpyright"],
55
"format_on_save": "on",
66
"formatter": [
77
{

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ print(f"Query took {time}ms")
9999
-|-
100100
execute | run a surql query
101101
async_execute | run a surql query asynchronously
102+
query | query a list of records and assert the expected type
103+
query_one | query one single record and assert its expected type
104+
count | count how many records match a query
102105
insert_analytics_data | insert a record in the analytics table
103106
safe_insert_error | insert a record in the errors table
104107
error_exists | check if there’s an ingestion error related to a document id

packages/demo-simple/README.md

Whitespace-only changes.

packages/demo-simple/main.py

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
from dataclasses import asdict, dataclass
2+
from textwrap import dedent
3+
4+
from surrealdb import RecordID
5+
6+
from kaig.db import DB
7+
from kaig.db.queries import WhereClause, order_limit_start
8+
9+
10+
@dataclass
11+
class User:
12+
id: str | None
13+
username: str
14+
team: RecordID | None = None
15+
16+
17+
def main():
18+
table_user = "user"
19+
table_notification = "notification"
20+
tables = [table_user, table_notification]
21+
22+
# -- DB connection
23+
url = "ws://localhost:8000/rpc"
24+
db_user = "root"
25+
db_pass = "root"
26+
db_ns = "kaig"
27+
db_db = "demo-simple"
28+
db = DB(url, db_user, db_pass, db_ns, db_db, tables=tables)
29+
30+
# Remove this if you don't want to clear all your tables on every run
31+
db.clear()
32+
33+
# Only required if you have vector_tables or graph_relations in your DB
34+
# db.init_db()
35+
36+
# These are SurrealDB's RecordIDs. We don't necessarily need a `team` table
37+
# and records to use them
38+
team_green = RecordID("team", "green")
39+
team_blue = RecordID("team", "blue")
40+
41+
# -- Define event
42+
query = f"""
43+
DEFINE EVENT create_user ON TABLE {table_user}
44+
WHEN $event = "CREATE" AND $after.team = None
45+
THEN (
46+
CREATE {table_notification} SET
47+
msg = "User created without a team",
48+
user = $after.id,
49+
created_at = time::now()
50+
);
51+
"""
52+
_ = db.sync_conn.query(query)
53+
54+
# -- Create one record
55+
new_user = User(id=None, username="kaig", team=team_green)
56+
user = db.query_one(
57+
f"CREATE ONLY {table_user} CONTENT $record",
58+
{"record": asdict(new_user)},
59+
User,
60+
)
61+
print(f"\n--- Created: ---\n{user}")
62+
# User(id=RecordID(table_name=user, record_id=vv1gmrwwq6cvw45xg4wf), username='kaig', team=RecordID(table_name=team, record_id=green))
63+
64+
# Another way is using the DB connection object directly, which is the
65+
# SurrealDB Python SDK: https://surrealdb.com/docs/sdk/python
66+
#
67+
# doc = db.sync_conn.create(table, {"username": "kaig"})
68+
69+
# -- Create many records
70+
new_users = [
71+
asdict(User(id=None, username="1", team=team_green)),
72+
asdict(User(id=None, username="2", team=team_blue)),
73+
asdict(User(id=None, username="3", team=team_blue)),
74+
asdict(User(id=None, username="4", team=None)),
75+
asdict(User(id=None, username="5", team=None)),
76+
]
77+
_ = db.query(f"INSERT INTO {table_user} $list", {"list": new_users}, User)
78+
79+
# -- Query all documents
80+
users = db.query(f"SELECT * FROM {table_user}", {}, User)
81+
print(f"\n--- All: --\n{users}")
82+
# [User(id=RecordID(table_name=user, record_id=25sslmmrv4p4rm4vpbw0), username='5', team=None), User(id=RecordID(table_name=user, record_id=8nbpgmkw9u438lx3g47r), username='1', team=RecordID(table_name=team, record_id=green)), User(id=RecordID(table_name=user, record_id=ifrfajan8c44cn7vjz09), username='3', team=RecordID(table_name=team, record_id=blue)), User(id=RecordID(table_name=user, record_id=rcycrch7qie3ytgd1giy), username='2', team=RecordID(table_name=team, record_id=blue)), User(id=RecordID(table_name=user, record_id=sa39vpybhmt7h4r405hb), username='4', team=None), User(id=RecordID(table_name=user, record_id=vv1gmrwwq6cvw45xg4wf), username='kaig', team=RecordID(table_name=team, record_id=green))]
83+
84+
# -- Query one document
85+
assert user is not None
86+
result = db.query_one(
87+
"SELECT * FROM ONLY $record", {"record": user.id}, User
88+
)
89+
print(f"\n--- Query one: ---\n{result}")
90+
# User(id=RecordID(table_name=user, record_id=vv1gmrwwq6cvw45xg4wf), username='kaig', team=RecordID(table_name=team, record_id=green))
91+
92+
# -- With filters, sort, and pagination
93+
where = WhereClause()
94+
where = where.and_("team", team_green)
95+
where_clause, where_vars = where.build()
96+
order_limit_start_clause = order_limit_start("username", "DESC", 5, 0)
97+
query = dedent(f"""
98+
SELECT *
99+
FROM {table_user}
100+
{where_clause}
101+
{order_limit_start_clause}
102+
""")
103+
filtered = db.query(query, where_vars, User)
104+
print(f"\n--- Filtered: --\n{filtered}")
105+
# [User(id=RecordID(table_name=user, record_id=vv1gmrwwq6cvw45xg4wf), username='kaig', team=RecordID(table_name=team, record_id=green)), User(id=RecordID(table_name=user, record_id=8nbpgmkw9u438lx3g47r), username='1', team=RecordID(table_name=team, record_id=green))]
106+
107+
count = db.count(table_user, "", {})
108+
print(f"\n--- Total user count: {count}")
109+
110+
# -- Query notifications
111+
notifications = db.query(f"SELECT * FROM {table_notification}", {}, dict)
112+
print(f"\n--- Notifications: --\n{notifications}")
113+
# [{'created_at': datetime.datetime(2025, 9, 24, 22, 3, 58, 899922, tzinfo=datetime.timezone.utc), 'id': RecordID(table_name=notification, record_id=gj1hpep0n8dflso4pt78), 'msg': 'User created without team', 'user': RecordID(table_name=user, record_id=sa39vpybhmt7h4r405hb)}, {'created_at': datetime.datetime(2025, 9, 24, 22, 3, 58, 899955, tzinfo=datetime.timezone.utc), 'id': RecordID(table_name=notification, record_id=i45i8n9t4qww5r9zswzp), 'msg': 'User created without team', 'user': RecordID(table_name=user, record_id=25sslmmrv4p4rm4vpbw0)}]
114+
115+
116+
if __name__ == "__main__":
117+
main()
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[project]
2+
name = "demo-simple"
3+
version = "0.1.0"
4+
description = "Add your description here"
5+
readme = "README.md"
6+
requires-python = ">=3.12"
7+
dependencies = ["kaig"]
8+
9+
[tool.uv.sources]
10+
kaig = { workspace = true }

0 commit comments

Comments
 (0)