Qubit Metadata
A general-purpose, type-safe metadata model for Rust.
Overview
qubit-metadata provides a Metadata type for attaching explicitly typed
extension fields to data objects without hard-coding every auxiliary field into
the core model. Typical uses include:
- Document ingestion: keep
file_id,chunk_index,language,source, andconfidencewith each document chunk. - Vector search: store fields such as
tenant_id,doc_type,created_at,score, oracl_groupthat later become vector-database metadata columns or payload filters. - Event and message pipelines: carry
trace_id,request_id,tenant_id,route, and retry metadata through services. - External service integration: retain compact service result fields such as model version, latency, billing tag, and request ID for diagnostics and analytics.
Metadata stores values as qubit_value::Value, so scalar types remain clear:
i64 is different from u32, and f64 is different from String. If a caller
really needs nested data, it can store Value::Json explicitly. Common document
metadata, vector-database metadata, and request context are usually flat typed
field sets, which keeps schema validation and filter construction predictable.
Design Goals
- Typed values: preserve concrete runtime types with
qubit_value::Value. - Convenient construction: support both mutable
set()and fluentwith(). - Optional schema: validate field names, required fields, concrete
DataTypes, and filter compatibility. - Filter builder: build immutable
MetadataFiltervalues with a chainable builder. - Serde support: serialize metadata, schemas, and filters for config, storage, and service boundaries.
Features
1) Typed metadata storage
Metadata is an ordered String -> Value map. It supports typed get, set,
try_get, try_set, with, iteration, merge, retain, and conversion to/from
BTreeMap<String, Value>.
use Metadata;
let meta = new
.with
.with
.with;
assert_eq!;
assert_eq!;
2) Schema for validation and storage planning
MetadataSchema uses qubit_common::DataType. This is useful when a storage
backend requires metadata fields to be declared in advance, and it also lets the
filter builder validate field/operator compatibility early.
use DataType;
use ;
let schema = builder
.required
.required
.optional
.build;
let meta = new
.with
.with;
schema.validate.unwrap;
3) Immutable filters built by builder
MetadataFilter::builder() creates a builder. Calling build() returns an
immutable filter. build_checked(&schema) also validates referenced fields,
operator compatibility, and filter value types.
use DataType;
use ;
let schema = builder
.required
.required
.build;
let filter = builder
.eq
.and_ge
.build_checked
.unwrap;
let meta = new
.with
.with;
assert!;
4) Filter DSL
| Method | Semantics |
|---|---|
eq, ne |
Equality / inequality |
gt, ge, lt, le |
Numeric or string range comparison |
exists, not_exists |
Key presence / absence |
in_set, not_in_set |
Membership / exclusion |
and_*, or_* |
Append one predicate with explicit connector |
and, or, and_not, or_not |
Append grouped subexpressions |
not |
Negate the current expression |
Grouped subexpressions are built with closures. The closure receives a fresh builder, and the resulting expression is appended as one grouped child:
use ;
let filter = builder
.eq
.and
.build;
let meta = new
.with
.with
.with;
assert!;
The expression above means:
status == "active" AND (score >= 80 OR tag == "rust")
Use and_not or or_not when the whole group should be negated:
let filter = builder
.eq
.and_not
.build;
Missing-key behavior for negative predicates is controlled by
MissingKeyPolicy. Mixed numeric comparison behavior is controlled by
NumberComparisonPolicy.
Error Handling
Use try_get and schema validation when the caller needs diagnostics instead of
Option:
use DataType;
use ;
let meta = new.with;
match meta.
Installation
Add to your Cargo.toml:
[]
= "0.3.0"
License
Licensed under the Apache License, Version 2.0.