Python quickstart
The same shape as the CLI quickstart, in Python. A database that is a directory, several data models inside it, and branching and history over all of them. This page is about what the SDK does differently, so it is worth reading after that one rather than instead of it.
Install and open
pip install stratadb
open creates the database if it is not there, and the handle is a context
manager, so it closes itself.
import stratadb
with stratadb.open("./quickstart") as db:
db.kv.put("greeting", "hello")
print(db.kv.get("greeting"))
b'hello'
For tests, stratadb.open(cache=True) gives you an in-memory database with no
directory and nothing to clean up.
Bytes out of key-value, objects out of JSON
Note that b above. Key-value stores bytes and hands bytes back, so decode
when you want text.
db.kv.get("greeting").decode()
'hello'
JSON is the opposite. It gives you the Python value, already parsed, and a path reads one field without loading the document.
db.json.set("user:ada", "$", {"name": "Ada", "role": "engineer"})
db.json.get("user:ada", "$.role")
db.json.get("user:ada")
'engineer'
{'name': 'Ada', 'role': 'engineer'}
Fork the database
A branch covers every model at once, and forking copies nothing.
db.branches.fork("default", "work")
Reach for fork rather than create, which makes an empty root branch with no
parent. Writing to one of those works, so the mistake stays invisible until a
merge fails and the work has to be redone.
Scope a view to the branch
Where the CLI takes --branch, Python gives you a view. db.at() returns a
lightweight handle over the same database, so you keep both branches in hand at
once.
work = db.at(branch="work")
work.json.set("user:ada", "$.role", "director")
work.json.get("user:ada", "$.role")
db.json.get("user:ada", "$.role")
'director'
'engineer'
Compare, then merge
Results are typed objects, not dictionaries, so you read them with attributes and the capability fields are enums.
preview = db.branches.preview("work", "default")
preview.conflicts
preview.branch_point
[c.value for c in preview.capabilities_covered]
[]
4
['key_value', 'json', 'vector']
That covered list is worth reading. Merging carries key-value, JSON and vector data; events and graph data it does not. Branches covers why.
db.branches.merge("work", "default")
db.branches.delete("work")
db.json.get("user:ada", "$.role")
'director'
Read the past
Every version is still addressable, and history comes back newest first.
db.kv.put("greeting", "goodbye")
for item in db.kv.history("greeting"):
print(item.version, item.value)
15 b'goodbye'
3 b'hello'
as_of takes a position on the commit timeline:
db.kv.get("greeting", as_of=3)
b'hello'
as_of_time takes a wall-clock instant instead, and it is generous about the
form: an aware datetime, a date, a string, or microseconds. Take the instant
from an entry’s committed_at, which to_datetime converts for you.
from stratadb import to_datetime
first = list(db.kv.history("greeting"))[-1]
when = to_datetime(first.committed_at)
db.kv.get("greeting", as_of_time=when)
db.kv.get("greeting", as_of_time=str(when))
b'hello'
b'hello'
Reach back past the first commit and it raises HistoryUnavailableError rather
than returning nothing, so a window that does not exist fails loudly.
These are two different clocks. Passing both raises rather than guessing which you meant. Time travel is the page for choosing.
Errors are exceptions, and they carry the code
Every failure raises a subclass of StrataError, so you can catch a whole
family or one kind, and the exception carries the same code the CLI prints.
from stratadb.errors import NotFoundError
try:
db.branches.merge("nope", "default")
except NotFoundError as err:
print(err.code)
print(err.ref)
not_found.engine.branch
https://stratadb.org/e/not_found.engine.branch
That ref is a page on this site.
Error codes explains the shape of a code and which
failures are safe to retry.
Where to go next
Working with data covers the models this page skipped.
The command reference gives the Python call and signature beside every command.