anda_db_schema is the type-system layer of the
AndaDB workspace. It defines the field
types, field values, schemas and documents shared by the embedded database,
its derive macros and the higher-level memory components. anda_db
re-exports it as anda_db::schema.
FieldType(aliasFt): the closed set of declarable types —Bool,I64,U64,F64,F32,Bytes,Text,Json,Vector(bf16),Array,Map(typed keys, with wildcard keys for open maps) andOption.FieldValue(aliasFv): the runtime value validated against aFieldType, convertible to and from CBOR (Cbor) and serde-compatible in both JSON and CBOR.FieldEntry(aliasFe): one field's name, type, description, uniqueness flag and stable numeric index, used as the on-disk key.SchemaandSchemaBuilder: an ordered, versioned set of field entries with an implicit unique_id: U64, and forward-compatible upgrades throughSchema::upgrade_with.Document(bound to a schema) andDocumentOwned(standalone).Resource: a predefined structure for external files, blobs and URIs.Vector(Vec<bf16>) withvector_from_f32/vector_from_f64.- The
AndaDBSchemaandFieldTypedderive macros, re-exported fromanda_db_derive.
[dependencies]
anda_db_schema = "0.14"
serde = { version = "1", features = ["derive"] }Derive a schema and round-trip a typed value through a Document:
use anda_db_schema::{AndaDBSchema, Document, Vector, vector_from_f32};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(Debug, PartialEq, Serialize, Deserialize, AndaDBSchema)]
struct Note {
_id: u64,
body: String,
embedding: Vector,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let schema = Arc::new(Note::schema()?);
let note = Note {
_id: 1,
body: "A persistent memory".into(),
embedding: vector_from_f32(vec![0.25, 0.5]),
};
let doc = Document::try_from(schema, ¬e)?;
let restored: Note = doc.try_into()?;
assert_eq!(restored, note);
Ok(())
}Or build one by hand and fill a document field by field:
use anda_db_schema::{Document, FieldEntry, FieldType, Fv, Schema};
use std::sync::Arc;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut builder = Schema::builder();
builder
.with_version(1)
.add_field(FieldEntry::new("title".into(), FieldType::Text)?.with_unique())?
.add_field(FieldEntry::new(
"tags".into(),
FieldType::Option(Box::new(FieldType::Array(vec![FieldType::Text]))),
)?)?;
let schema = Arc::new(builder.build()?);
assert_eq!(schema.version(), 1);
let mut doc = Document::new(schema);
doc.set_id(1);
doc.set_field("title", Fv::Text("Hello".into()))?;
assert_eq!(doc.get_field("title"), Some(&Fv::Text("Hello".into())));
Ok(())
}- Persistence normalizes values to CBOR.
NaNis rejected soFieldValuekeeps a meaningful equality. - JSON serialization rejects non-finite
F32/F64values instead of writingnull; binary CBOR still represents infinities. - Human-readable formats separate text from bytes with explicit
txt:andb64:prefixes. - An untyped round trip keeps the data but normalizes some variants (
F32→F64, non-negativeI64→U64,Vector→Array(U64)); extracting with the declaredFieldTyperestores the declared variant.
Schema::upgrade_with carries persisted field indexes and nested-key
deletion history from the previous schema, so stored documents stay readable.
An upgrade needs a higher version, and new fields must be optional. An older
schema with incomplete history needs a full scan of raw documents and pending
recovery images before new fields can be allocated: the AndaDB collection
performs that scan automatically during an upgrade, and custom storage
integrations use Schema::history_recovery().
cargo test -p anda_db_schema -p anda_db_deriveThe Rust examples in this README are compiled and run as doctests.
- docs/anda_db_schema.md: type system, document model and on-disk format
- docs/anda_db_derive.md
- Schemas and CBOR quick reference
anda_db: the embedded database built on this type systemanda_db_derive:AndaDBSchemaandFieldTyped
MIT. See LICENSE.