Expand description
Docs<T>, the typed document handle, and the traits #[derive(Yo)] writes
(15 sections 2 and 4).
A document collection is your own struct, stored as your own struct. There is no schema to declare, no JSON text to parse on either side, and no query language: a struct goes in, the same struct comes out, and the fields worth looking documents up by say so with an attribute.
use yo::Yo;
#[derive(Yo, Debug, PartialEq)]
struct Order {
#[yo(id)]
id: u64,
#[yo(index)]
status: String,
#[yo(ordered)]
total: f64,
}
let db = yo::open(yo::MEMORY)?;
let orders = db.docs::<Order>("orders")?;
orders.put(&Order { id: 1, status: "open".to_owned(), total: 12.5 })?;
orders.put(&Order { id: 2, status: "shipped".to_owned(), total: 99.0 })?;
assert_eq!(orders.get(&1)?.unwrap().total, 12.5);
assert_eq!(orders.find(Order::STATUS, "open")?.len(), 1);
assert_eq!(orders.count(Order::STATUS, "shipped")?, 1);§The query is a constant, not a string
Order::STATUS is a Path the derive wrote, and Order::TOTAL is an
Ordered, which is what a #[yo(ordered)] field gets. A field that is not
indexed has no constant at all, so asking for one is a name that does not
exist rather than a query that quietly turns into a scan. Docs::range
takes an Ordered and nothing else, so asking an equality index for a range
is a type error at the call site.
// The index on status answers equality, so there is no range to walk.
orders.range(Order::STATUS, "a".."z").unwrap();The value side is typed too, so comparing a number field against a string is the same kind of mistake and gets the same answer.
orders.find(Order::TOTAL, "twelve").unwrap();§A range over a string field takes a pair of bounds
orders.range(Order::NAME, "a".."m") does not compile, and the reason is not
this crate. Range<&str> only implements RangeBounds<str> when str is
sized, which it is not, so the standard library’s own
BTreeMap<String, u8>::range("a".."m") is rejected the same way. Writing the
two ends out is what works there and it is what works here.
let early = orders.range(Order::NAME, (Bound::Included("a"), Bound::Excluded("m")))?;
assert_eq!(early.len(), 1);A range over a number field is written the way anyone would write it, because
the numbers are sized and 0.0..50.0 is a RangeBounds<f64> already.
§What a field can be
Field is the list, and it is the JSON types rather than the Rust ones,
because a document is JSON shaped whatever it was written from. The integers
and floats, bool, String, Option<T> for a field that may be absent,
Vec<T> for a list, and any other type that derives Yo, which nests.
An integer is stored as an i64, which is the one number type JSON has, so
a u64 above i64::MAX is refused on the way in rather than silently
rounded through a float.
Structs§
- Builder
- A value under construction.
- Doc
- A value with the key table its keys are interned against.
- Docs
- A collection of
T. - Key
- A value as an index looks it up.
- Ordered
- A path whose index keeps its keys in order, so it answers ranges as well as equality.
- Path
- A path into a document, what its index can be asked, and the type of the value that lives there.
Enums§
- Index
Kind - A float as bytes that sort the way the float does. What an index can be asked, and how many keys a document gets at its path.
Traits§
- Asked
- How a field’s type is written in a query.
- Document
- A type that is a whole document: a
Fieldwith an id and its indexes. - Field
- A type that can be a field of a document.
- Indexed
- The indexes a type declares.
- Query
- A value that can be an index key.
Functions§
- at
- Read one field out of a document, which is what the derive calls per field.
- expect_
object - Check that what is stored under this collection is an object at all, which is what the derive calls before it reads the fields.