Skip to content

🏴‍☠️ Arbitrary JSON-structured data, all-indexing storage backed with lawn

Notifications You must be signed in to change notification settings

mentalblood0/trove

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

203 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Trove

A JSON document database with indexed storage built on top of dream index.

Overview

Trove provides a transactional storage for JSON documents with the following features:

  • Document Storage: Store JSON documents with auto-generated UUID v7 identifiers
  • Path-Based Access: Access nested values using intuitive paths like ["user", "profile", "name"]
  • Indexed Queries: Query objects based on path-value presence/absence conditions
  • ACID Transactions: ACID-compliant transactions for both read and write operations
  • Flat Storage: Efficient flat storage with nested reconstruction

Architecture

Trove is built using:

Component Purpose
dream Inverted indexing for fast queries
bincode Efficient binary encoding
serde_json JSON handling and serialization
xxhash-rust Fast digest computation

Installation

Add trove to your Cargo.toml:

[dependencies]
trove = { git = "https://github.com/mentalblood0/trove" }

Key Concepts

ObjectId

Each document in trove by default has a unique identifier generated using UUID v7 for time-ordered uniqueness:

let id = tx.insert(json!({"data": "value"}))?;
println!("Object ID: {:?}", id.value);

Paths

Access nested values using paths constructed with the path_segments! macro:

// Simple object key
let path1 = path_segments!("name");

// Nested object key  
let path2 = path_segments!("user", "profile");

// Array access
let path3 = path_segments!("items", 0, "id");

// Mixed
let path4 = path_segments!("users", 0, "address", "city");

Transactions

Trove provides two types of transactions:

Write Transactions

For inserting, updating, and removing data:

chest.lock_all_and_write(|tx| {
    // Insert a new object
    let id = tx.insert(json!({"name": "Alice"}))?;
    
    // Update a nested value
    tx.update(id.clone(), path_segments!("age"), json!(30))?;
    
    // Push to array
    tx.push(&id, &path_segments!("tags"), json!("premium"))?;
    
    // Remove a value
    tx.remove(&id, &path_segments!("temp"))?;
    
    Ok(())
})?;

Read Transactions

For querying data without modification:

chest.lock_all_writes_and_read(|tx| {
    // Iterate all objects
    for obj in tx.objects()?.collect::<Vec<_>>()? {
        println!("Object: {:?}", obj.id);
        println!("Value: {:?}", obj.value);
    }
    
    // Get specific value
    let value = tx.get(&id, &path_segments!("name"))?;
    
    // Select by conditions
    let matches = tx.select(
        &vec![(IndexRecordType::Direct, path_segments!("status"), json!("active"))],
        &vec![(IndexRecordType::Direct, path_segments!("deleted"), json!(true))],
        None,
    )?.collect::<Vec<ObjectId>>()?;
    
    Ok(())
})?;

Indexed Queries

Use the inverted index to efficiently query objects:

// Find all objects with status = "active"
let active_users = tx.select(
    &vec![(IndexRecordType::Direct, path_segments!("status"), json!("active"))],
    &vec![],
    None,
)?;

// Find all objects with tag = "premium" (array search)
let premium_users = tx.select(
    &vec![(IndexRecordType::Array, path_segments!("tags"), json!("premium"))],
    &vec![],
    None,
)?;

// Find objects with conditions (AND logic)
let users = tx.select(
    &vec![
        (IndexRecordType::Direct, path_segments!("status"), json!("active")),
        (IndexRecordType::Direct, path_segments!("role"), json!("admin")),
    ],
    &vec![(IndexRecordType::Direct, path_segments!("banned"), json!(true))],
    None,
)?;

Array Operations

Trove supports efficient array operations:

// Get array length
let len = tx.len(&id, &path_segments!("items"))?;

// Get last element
let last = tx.last(&id, &path_segments!("items"))?;

// Check if array contains element
let has_value = tx.contains_element(&id, &path_segments!("items"), &Value::String("test".into()))?;

// Push to array (returns index)
let index = tx.push(&id, &path_segments!("items"), json!("new item"))?;

Data Model

Value Types

Trove stores primitive values:

Type Description
Null JSON null
Integer(i64) 64-bit signed integer
Float(f64) 64-bit floating point
Bool(bool) Boolean
String(String) UTF-8 string

Note: Arrays and objects are not stored directly - they are flattened into path-value pairs.

Index Record Types

  • Direct: For object properties and scalar values. Matches exact path-value pairs.
  • Array: For values inside arrays.

Testing

Run tests with:

cargo test

License

This project is licensed under the MIT License.

About

🏴‍☠️ Arbitrary JSON-structured data, all-indexing storage backed with lawn

Resources

Stars

Watchers

Forks

Contributors

Languages