Skip to main content

Store

Struct Store 

Source
pub struct Store { /* private fields */ }
Expand description

A simple, ergonomic hierarchical data store backed by Hyperbolic Tree Tensors.

All keys are path-like strings (e.g. "/users/alice"). Parent directories are created automatically. The root node "/" always exists.

§Examples

use horon_engine::Store;
use g_math::fixed_point::FixedPoint;

let store = Store::new();

// Store and retrieve data
store.put("/config/db", b"postgres://localhost").unwrap();
assert_eq!(store.get("/config/db").unwrap(), b"postgres://localhost");

// Coordinates and distances are Q64.64 fixed point, never floats: the
// same query returns bit-identical results on any platform. Converting
// from a decimal literal is explicit, so the lossy step is visible at
// the call site rather than hidden inside the API.
let origin: Vec<FixedPoint> = (0..4).map(|_| FixedPoint::from_int(0)).collect();
let (path, distance) = store.nearest(&origin).unwrap();

Implementations§

Source§

impl Store

Source

pub fn new() -> Self

Create a new Store with default settings (capacity: 10,000).

Source

pub fn with_config(config: StoreConfig) -> Self

Create a new Store with custom configuration.

Source

pub fn put(&self, key: &str, data: &[u8]) -> Result<(), StoreError>

Store data at a key (upsert — inserts or updates).

Parent directories are created automatically.

Source

pub fn put_data_only(&self, key: &str, data: &[u8]) -> Result<(), StoreError>

Store data without geometric embedding (data + semantic only).

Much faster than put() for bulk loading — skips Sarkar embedding, VP-tree, and power diagram construction. Semantic queries (nearest_semantic, neighbors_semantic, get_semantic) work normally. Spatial queries (nearest, neighbors) will not find nodes loaded this way.

Source

pub fn put_positioned( &self, key: &str, data: &[u8], child_index: u32, ) -> Result<(), StoreError>

Store data with an explicit child_index for deterministic Sarkar reconstruction.

Used during snapshot replay: the stored child_index ensures the node gets the same geometric position regardless of replay order.

Source

pub fn get(&self, key: &str) -> Result<Vec<u8>, StoreError>

Retrieve data by key.

Source

pub fn remove(&self, key: &str) -> Result<(), StoreError>

Remove a key and its data.

Source

pub fn exists(&self, key: &str) -> bool

Check if a key exists.

Source

pub fn children(&self, path: &str) -> Result<Vec<String>, StoreError>

List immediate children of a path.

Returns only direct children, not the full subtree.

Source

pub fn list(&self, prefix: &str) -> Result<Vec<String>, StoreError>

List all keys under a prefix (full subtree).

Source

pub fn set_meta( &self, key: &str, name: &str, value: &str, ) -> Result<(), StoreError>

Set a metadata field on a key.

Source

pub fn get_meta(&self, key: &str) -> Result<HashMap<String, String>, StoreError>

Get all metadata for a key.

Source

pub fn set_semantic(&self, key: &str, coords: Vec<u8>) -> Result<(), StoreError>

Set semantic coordinates on a key (raw Q64.64 bytes, 16 bytes per dimension).

Each coordinate is 16 bytes (i128 LE). For example, 2 semantic dimensions requires 32 bytes. Use FixedPoint::from_f64(value).raw().to_le_bytes() to encode each coordinate.

Returns InvalidOperation if coords is not a multiple of 16 bytes — a misaligned vector would otherwise have its trailing partial dimension silently ignored by distance computations.

Source

pub fn get_semantic(&self, key: &str) -> Result<Vec<u8>, StoreError>

Get semantic coordinates for a key (raw Q64.64 bytes).

Returns empty Vec if no semantic coordinates have been set.

Source

pub fn nearest( &self, coords: &[FixedPoint], ) -> Result<(String, FixedPoint), StoreError>

Find the nearest stored node to an arbitrary point in hyperbolic space.

Coordinates are in the Poincare disk model (each component in (-1, 1)). Uses the Nielsen power diagram for O(1) point location.

Returns (key, hyperbolic_distance).

Source

pub fn nearest_k( &self, coords: &[FixedPoint], k: usize, ) -> Result<Vec<(String, FixedPoint)>, StoreError>

Find the k nearest stored nodes to an arbitrary point in hyperbolic space.

Like nearest() but returns multiple candidates, enabling the caller to post-filter and still get results.

Complexity: unlike nearest(), this always takes the bucketed VP-tree path — O(B + log(n/B)) with B ≈ 61 fixed buckets — not the O(1) power-diagram fast path.

Returns (key, hyperbolic_distance) sorted by ascending distance.

Source

pub fn neighbors(&self, path: &str, k: usize) -> Result<Vec<String>, StoreError>

Find the k nearest neighbors of an existing node.

Returns keys sorted by ascending hyperbolic distance. The queried key itself is excluded from results.

Complexity: bucketed VP-tree search — O(B + log(n/B)) with B ≈ 61 fixed buckets — not the O(1) power-diagram fast path.

Source

pub fn nearest_semantic( &self, query_coords: &[u8], k: usize, dim_range: Range<usize>, ) -> Result<Vec<(String, FixedPoint)>, StoreError>

Find the k nearest nodes by Euclidean distance across a dimensional slice.

A dimensional slice selects which semantic dimensions to compare. For example, 16..33 compares only category preference axes, ignoring operational dimensions. Different slices answer different questions from the same data.

query_coords: raw Q64.64 bytes (16 bytes per dimension). k: number of nearest neighbors to return. dim_range: which dimensions to include in the distance calculation.

Returns (key, distance) sorted ascending by (distance, key) — ties break deterministically. Returns InvalidOperation if query_coords is not a multiple of 16 bytes.

Complexity (docs/SEMANTIC_INDEX.md): stores below SEMANTIC_INDEX_MIN_NODES use a brute-force O(n × d) scan. Larger stores use a lazily built per-dim_range VP-tree: O(log n) expected per warm query on low-dimensional slices; the first query for a slice after any semantic write pays an O(n log n) rebuild. Results are identical on both paths.

Source

pub fn neighbors_semantic( &self, path: &str, k: usize, dim_range: Range<usize>, ) -> Result<Vec<(String, FixedPoint)>, StoreError>

Find the k nearest nodes to an existing node by semantic dimensional distance.

Reads the node’s semantic coordinates and finds the closest other nodes in the specified dimensional slice. The queried node is excluded.

Complexity: same routing as Store::nearest_semantic (indexed above the node floor, brute-force below).

Returns keys sorted by ascending semantic distance.

Source

pub fn find_similar( &self, key: &str, k: usize, dim_range: Range<usize>, ) -> Result<Vec<(String, FixedPoint)>, StoreError>

Find the k stored nodes most similar to an existing node across a dimensional slice — “what’s like this one?”.

This is Store::neighbors_semantic under a task-shaped name: it reads the node’s semantic coordinates and returns the k nearest other nodes by Euclidean distance over dim_range, sorted ascending by (distance, key). Same routing and cost as nearest_semantic.

Source

pub fn find_outliers( &self, prefix: &str, z_threshold: FixedPoint, dim_range: Range<usize>, ) -> Result<Vec<SemanticOutlier>, StoreError>

Find semantic outliers among the nodes under a key prefix: nodes whose average distance to their nearest peers is anomalously large relative to the population.

For each node under prefix that has semantic coordinates, computes the average distance to its OUTLIER_KNN (10, capped at population−1) nearest peers within the same population over dim_range, then flags nodes whose average exceeds the population mean by more than z_threshold standard deviations. Returns outliers sorted by descending z-score (ties by key). This is the “room 403 rates unlike its floor-mates” / “course far from every peer” query as one call.

Statistics are computed strictly within the prefix population — nodes outside prefix (or without coordinates) neither appear nor skew the baseline. Populations below OUTLIER_MIN_POPULATION (5) return no outliers: z-scores over a handful of nodes are noise, not findings.

Complexity: builds a dedicated VP-tree over the population (O(m log m) distance evaluations) plus one k-NN query per node — ~seconds at 10k nodes, versus the O(m²) pairwise scan this replaces. Deterministic: identical stores produce identical results.

Returns InvalidOperation if z_threshold is not a finite positive number.

Source

pub fn embed_existing(&self, key: &str) -> Result<bool, StoreError>

Upgrade a data-only key (inserted via Store::put_data_only) to a full geometric embedding, in place.

Missing ancestors are embedded first; the node’s key, value, metadata, and semantic coordinates are preserved. After this call the key participates in spatial queries (nearest, neighbors, find_within) and has a Store::position.

Returns whether this call performed the upgrade (false = the key was already embedded; idempotent). Positions are derived state, not persisted: deterministic for a fixed operation sequence, but a lazily-loaded store must re-embed after reopening.

Source

pub fn embed_all(&self, prefix: &str) -> Result<usize, StoreError>

Embed the prefix node (when it exists) and every data-only key under it (convenience). Parents embed before children (sorted order + ancestor recursion). Returns how many keys this call upgraded.

Source

pub fn position(&self, key: &str) -> Result<Vec<FixedPoint>, StoreError>

The hyperbolic (Poincaré) position of a stored key.

Errors for unknown keys and for data-only nodes (no embedding).

Source

pub fn semantic_epoch(&self) -> u64

Monotone counter of semantic-relevant mutations (coordinate writes, inserts, deletes). External caches over semantic state — e.g. the semantic disk crate::semantic_disk::SemanticDisk — tag their builds with it and rebuild when it has advanced, exactly like the internal index cache.

Source

pub fn semantic_distance( coords_a: &[u8], coords_b: &[u8], dim_range: Range<usize>, ) -> FixedPoint

Compute the Euclidean distance between two raw semantic coordinate vectors across a dimensional slice.

Utility method for computing distances without querying the store.

Source

pub fn find_within( &self, path: &str, radius: FixedPoint, ) -> Result<Vec<String>, StoreError>

Find all nodes within a hyperbolic distance of an existing node.

Complexity: bucketed VP-tree range search; cost grows with the number of buckets intersecting the radius and the result size.

Source

pub fn query( &self, adapter: &dyn QueryAdapter, query: &str, ) -> Result<Vec<QueryResult>, StoreError>

Execute a query using a pluggable adapter.

The adapter receives a reference to this store and the query string, and returns results by calling get, list, neighbors, etc.

Source

pub fn len(&self) -> usize

Number of stored entries (excludes the root node).

Source

pub fn is_empty(&self) -> bool

Returns true if the store contains no user data.

Source

pub fn inner(&self) -> &HTTStorage

Access the underlying HTTStorage for advanced operations.

Source

pub fn inner_mut(&mut self) -> &mut HTTStorage

👎Deprecated:

All HTTStorage methods now take &self; use inner() instead

Mutably access the underlying HTTStorage for advanced operations.

Trait Implementations§

Source§

impl Default for Store

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl !RefUnwindSafe for Store

§

impl !UnwindSafe for Store

§

impl Freeze for Store

§

impl Send for Store

§

impl Sync for Store

§

impl Unpin for Store

§

impl UnsafeUnpin for Store

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.