db/radixtree/README.md

3.8 KiB

RadixTree

A persistent radix tree implementation in Rust using OurDB for storage.

Overview

RadixTree is a space-optimized tree data structure that enables efficient string key operations with persistent storage. This implementation provides a persistent radix tree that can be used for efficient prefix-based key operations, such as auto-complete, routing tables, and more.

A radix tree (also known as a patricia trie or radix trie) is a space-optimized tree data structure that enables efficient string key operations. Unlike a standard trie where each node represents a single character, a radix tree compresses paths by allowing nodes to represent multiple characters (key segments).

Key characteristics:

  • Each node stores a segment of a key (not just a single character)
  • Nodes can have multiple children, each representing a different branch
  • Leaf nodes contain the actual values
  • Optimizes storage by compressing common prefixes

Features

  • Efficient prefix-based key operations
  • Persistent storage using OurDB backend
  • Memory-efficient storage of strings with common prefixes
  • Support for binary values
  • Thread-safe operations through OurDB

Usage

Add the dependency to your Cargo.toml:

[dependencies]
radixtree = { path = "../radixtree" }

Basic Example

use radixtree::RadixTree;

fn main() -> Result<(), radixtree::Error> {
    // Create a new radix tree
    let mut tree = RadixTree::new("/path/to/storage", false)?;
    
    // Set key-value pairs
    tree.set("hello", b"world".to_vec())?;
    tree.set("help", b"me".to_vec())?;
    
    // Get values by key
    let value = tree.get("hello")?;
    println!("hello: {}", String::from_utf8_lossy(&value)); // Prints: world
    
    // List keys by prefix
    let keys = tree.list("hel")?; // Returns ["hello", "help"]
    println!("Keys with prefix 'hel': {:?}", keys);
    
    // Get all values by prefix
    let values = tree.getall("hel")?; // Returns [b"world", b"me"]
    
    // Delete keys
    tree.delete("help")?;
    
    Ok(())
}

API

Creating a RadixTree

// Create a new radix tree
let mut tree = RadixTree::new("/path/to/storage", false)?;

// Create a new radix tree and reset if it exists
let mut tree = RadixTree::new("/path/to/storage", true)?;

Setting Values

// Set a key-value pair
tree.set("key", b"value".to_vec())?;

Getting Values

// Get a value by key
let value = tree.get("key")?;

Updating Values

// Update a value at a given prefix
tree.update("prefix", b"new_value".to_vec())?;

Deleting Keys

// Delete a key
tree.delete("key")?;

Listing Keys by Prefix

// List all keys with a given prefix
let keys = tree.list("prefix")?;

Getting All Values by Prefix

// Get all values for keys with a given prefix
let values = tree.getall("prefix")?;

Performance Characteristics

  • Search: O(k) where k is the key length
  • Insert: O(k) for new keys, may require node splitting
  • Delete: O(k) plus potential node cleanup
  • Space: O(n) where n is the total length of all keys

Use Cases

RadixTree is particularly useful for:

  • Prefix-based searching
  • IP routing tables
  • Dictionary implementations
  • Auto-complete systems
  • File system paths
  • Any application requiring efficient string key operations with persistence

Implementation Details

The RadixTree implementation uses OurDB for persistent storage:

  • Each node is serialized and stored as a record in OurDB
  • Node references use OurDB record IDs
  • The tree maintains a root node ID for traversal
  • Node serialization includes version tracking for format evolution

For more detailed information about the implementation, see the ARCHITECTURE.md file.

License

This project is licensed under the same license as the HeroCode project.