pub struct Db { /* private fields */ }Expand description
An open database.
Cheap to clone, and every clone is the same database. A handle taken out of it stays valid for as long as any clone lives.
This build runs in inline mode (15 section 7): the calling thread is the
shard, which is what makes a point read a function call rather than a
message. That is also why a handle does not cross threads yet. The owned
and served modes put the same API on top of yo-shard’s runtime, and they
arrive with it.
Implementations§
Source§impl Db
impl Db
Sourcepub fn map<K: Decode, V: Decode>(&self, name: &str) -> Result<Map<K, V>>
pub fn map<K: Decode, V: Decode>(&self, name: &str) -> Result<Map<K, V>>
Open a map, creating it if this is the first time.
The type is the collection’s shape (15 section 3), so opening the
same name a second time with a different type is an error and not a
surprise later: the shapes are compared, and a mismatch says which
field moved and whether the change is additive or breaking.
§Errors
Code::ShapeMismatch when the name is already a collection of
another shape.
Sourcepub fn docs<T: Document>(&self, name: &str) -> Result<Docs<T>>
pub fn docs<T: Document>(&self, name: &str) -> Result<Docs<T>>
Open a collection of documents, creating it if this is the first time.
T is the collection’s shape, exactly as it is for Db::map, and it
also carries the indexes: every field the type marked with #[yo(index)]
or one of its friends is declared here, so a collection cannot be opened
without the indexes its queries need.
use yo::Yo;
#[derive(Yo)]
struct Order {
#[yo(id)]
id: u64,
#[yo(index)]
status: String,
}
let db = yo::open(yo::MEMORY)?;
let orders = db.docs::<Order>("orders")?;
orders.put(&Order { id: 7, status: "open".to_owned() })?;
assert_eq!(orders.find(Order::STATUS, "open")?.len(), 1);§Errors
Code::ShapeMismatch when the name is already a collection of another
shape, and Code::Invalid for a declared index whose path is not one.
Sourcepub fn graph(&self, name: &str) -> Result<Graph>
pub fn graph(&self, name: &str) -> Result<Graph>
Open a graph, creating it if this is the first time.
The name has one shape like every other collection, and it is the same shape for every graph, because a graph’s types are not fixed when it is opened. A node type or an edge type registers itself under its label the first time it is used, and the shape it registered is checked on every later use, so the check is per label rather than one tuple named up front. That way adding a node type to a program is not a schema change for the types already in the graph.
use yo::{Edge, Node, Yo};
#[derive(Yo)]
struct Person { #[yo(id)] id: u64, name: String }
#[derive(Yo)]
struct Follows { since: i64 }
impl Node for Person { const LABEL: &'static str = "Person"; }
impl Edge for Follows {
type From = Person;
type To = Person;
const LABEL: &'static str = "FOLLOWS";
}
let db = yo::open(yo::MEMORY)?;
let g = db.graph("social")?;
let ada = g.add(&Person { id: 1, name: "ada".to_owned() })?;
let grace = g.add(&Person { id: 2, name: "grace".to_owned() })?;
g.link(ada, grace, &Follows { since: 2026 })?;
assert_eq!(g.out::<Follows>(ada)?, vec![grace]);§Errors
Code::ShapeMismatch when the name is already a collection of another
shape, which includes a name that is already a map or a document
collection.
Sourcepub fn vectors(&self, name: &str, dim: usize) -> Result<Vectors>
pub fn vectors(&self, name: &str, dim: usize) -> Result<Vectors>
Open a collection of vectors, creating it if this is the first time.
Nearness is euclidean distance. Db::vectors_with is the same call
with the metric spelled out, and cosine is the other one worth having.
The dimension is part of the collection’s shape exactly as a map’s value type is, so a collection opened at 768 dimensions cannot later be opened at 1536 and quietly read the old vectors as half of a new one.
let db = yo::open(yo::MEMORY)?;
let v = db.vectors("passages", 3)?;
v.put("a", &[1.0, 0.0, 0.0])?;
v.put("b", &[0.0, 1.0, 0.0])?;
assert_eq!(v.search(&[0.9, 0.1, 0.0], 1)?[0].key, b"a".to_vec());§Errors
Code::ShapeMismatch when the name is already a collection of another
shape, which includes the same name at another dimension or metric, and
Code::Invalid for a dimension no collection can hold.
Sourcepub fn vectors_with(
&self,
name: &str,
dim: usize,
metric: Metric,
) -> Result<Vectors>
pub fn vectors_with( &self, name: &str, dim: usize, metric: Metric, ) -> Result<Vectors>
The same, saying what nearness means.
Metric::Cosine stores the unit vector and
reports one minus the cosine similarity, which is what a collection of
text embeddings wants. See the vector module for what
each metric does and why two of the four are refused.
§Errors
As Db::vectors, and Code::Unsupported for a metric this build
does not measure.
Sourcepub fn strings(&self) -> Strings
pub fn strings(&self) -> Strings
The Redis string keyspace.
The same store a client reaches over RESP, reached without the socket, the parser or the reply (Y23). Not a named collection, because in Redis a string is not one: it is the keyspace itself.
let db = yo::open(yo::MEMORY)?;
assert_eq!(db.strings().incr("hits")?, 1);Sourcepub fn counter(&self, key: impl Into<Vec<u8>>) -> Counter
pub fn counter(&self, key: impl Into<Vec<u8>>) -> Counter
A counter at one key, which is 15 section 2’s db.counter("hits").
Sugar over Db::strings and worth having: a counter is the commonest
thing a string key is, and a handle that holds the key means the key is
spelled once rather than at every call site.
let db = yo::open(yo::MEMORY)?;
let hits = db.counter("hits");
hits.incr()?;
hits.add(9)?;
assert_eq!(hits.get()?, 10);Sourcepub fn sets(&self) -> Sets
pub fn sets(&self) -> Sets
Every Redis set command, with the key as the first argument.
The same store SADD off a socket reaches. Like Db::strings this is
not a named collection, because in Redis a set is not one: it is a key in
the keyspace that happens to hold a set.
let db = yo::open(yo::MEMORY)?;
db.sets().add_many("online", &["alice", "bob"])?;
assert_eq!(db.sets().len_of("online")?, 2);Sourcepub fn set(&self, key: impl Into<Vec<u8>>) -> Set
pub fn set(&self, key: impl Into<Vec<u8>>) -> Set
A set at one key, which is the same sugar Db::counter is.
let db = yo::open(yo::MEMORY)?;
let online = db.set("online");
online.add("alice")?;
assert!(online.contains("alice")?);Sourcepub fn keys(&self) -> Keys
pub fn keys(&self) -> Keys
Every command that works on a key whatever the key holds.
DEL, EXISTS and TYPE, and the whole expiry family. These are the
ones that belong to the keyspace rather than to a type, which is why
they are not on Db::strings or Db::sets: a deadline sits in the
key’s record and does not care what the record points at.
use std::time::Duration;
let db = yo::open(yo::MEMORY)?;
db.set("online").add("alice")?;
db.keys().expire_in("online", Duration::from_secs(60))?;Sourcepub fn collections(&self) -> Result<Vec<String>>
pub fn collections(&self) -> Result<Vec<String>>
The names of the typed collections in this database, in the order they were first opened.
§Errors
Code::Invalid if called from inside a callback that is already
holding this database.
Sourcepub fn shape(&self, name: &str) -> Result<Option<Tag>>
pub fn shape(&self, name: &str) -> Result<Option<Tag>>
The shape of a collection, if it exists.
§Errors
Code::Invalid if called from inside a callback that is already
holding this database.
Sourcepub fn memory_bytes(&self) -> Result<usize>
pub fn memory_bytes(&self) -> Result<usize>
What this database is holding, index and arena together, across the keyspace and every typed collection.
§Errors
Code::Invalid if called from inside a callback that is already
holding this database.
Sourcepub fn reads_the_clock(&self) -> bool
pub fn reads_the_clock(&self) -> bool
Whether this database reads the clock on the data path.
False until something is given a deadline, because until then the
clock’s answer cannot change any reply. 04 section 5 is the reason
this is worth a method: a clock read is tens of nanoseconds against a
budget of a hundred and fifty.