Skip to content

Documents & PocketValue ​

Documents are the primary data unit in PocketDB. Each document is an ordered map of field names to PocketValue instances, with a unique _id (ObjectId) field.

Installation ​

Add PocketDB to your Cargo.toml:

toml
[dependencies]
pocketdb = "0.1"

Or via the command line:

bash
cargo add pocketdb

Document ​

Creating a Document ​

rust
use pocketdb::{Document, PocketValue};

// Create an empty document
let mut doc = Document::new();

// Set fields
doc.set("name", PocketValue::String("Alice".to_string()));
doc.set("age", PocketValue::Int(30));
doc.set("active", PocketValue::Bool(true));
doc.set("score", PocketValue::Float(95.5));

Using the doc! Macro ​

The doc! macro provides a concise syntax for creating documents:

rust
use pocketdb::doc;

let d = doc! {
    "name" => "Alice",
    "age" => 30,
    "active" => true,
    "score" => 95.5
};

Values are automatically converted to PocketValue via the From trait.

Accessing Fields ​

rust
// Get a reference to a field value
let name = doc.get("name"); // Option<&PocketValue>

// Get with type extraction
if let Some(age) = doc.get("age").and_then(|v| v.as_i64()) {
    println!("Age: {}", age);
}

if let Some(name) = doc.get("name").and_then(|v| v.as_str()) {
    println!("Name: {}", name);
}

if let Some(active) = doc.get("active").and_then(|v| v.as_bool()) {
    println!("Active: {}", active);
}

if let Some(score) = doc.get("score").and_then(|v| v.as_f64()) {
    println!("Score: {}", score);
}

Nested Documents ​

rust
use pocketdb::doc;

let d = doc! {
    "name" => "Alice",
    "address" => {
        "street" => "123 Main St",
        "city" => "NYC",
        "zip" => "10001"
    },
    "scores" => [95, 87, 92]
};

// Access nested fields
if let Some(obj) = d.get("address").and_then(|v| v.as_object()) {
    if let Some(city) = obj.get("city").and_then(|v| v.as_str()) {
        println!("City: {}", city);
    }
}

// Access array elements
if let Some(arr) = d.get("scores").and_then(|v| v.as_array()) {
    println!("First score: {:?}", arr.first());
    println!("Total scores: {}", arr.len());
}

Modifying Documents ​

rust
// Set a field (returns previous value if it existed)
let old = doc.set("name", PocketValue::String("Bob".to_string()));

// Remove a field
let removed = doc.remove("age");

// Check if a field exists
if doc.contains("email") {
    // ...
}

// Merge fields from another document
let mut other = Document::new();
other.set("email", PocketValue::String("bob@example.com".to_string()));
doc.merge(&other);

Document Metadata ​

Every document has built-in timestamps:

rust
// Creation timestamp (milliseconds since epoch)
let created = doc.created_at();

// Last modification timestamp
let updated = doc.updated_at();

// The document's unique ID
let id = doc.id();       // &ObjectId
let hex = doc.id_hex();  // String (24-char hex)

// Touch updates the updated_at timestamp
doc.touch();

JSON Conversion ​

rust
// Convert to serde_json::Value
let json = doc.to_json();

// Create from serde_json::Value
let doc = Document::from_json(json)?;

Display ​

rust
println!("{}", doc);
// Output: {"name": "Alice", "age": 30, "active": true}

ObjectId ​

ObjectId is a 12-byte unique identifier composed of:

  • 4-byte timestamp (seconds since custom epoch)
  • 4-byte random component
  • 4-byte atomic counter
rust
use pocketdb::ObjectId;

// Generate a new ObjectId
let id = ObjectId::new();
assert_eq!(id.to_hex().len(), 24);

// Parse from hex string
let parsed = ObjectId::from_hex("507f1f77bcf86cd799439011")?;

// Extract timestamp
let ts = id.timestamp_ms();

// Convert to/from bytes
let bytes = id.to_bytes();
let id2 = ObjectId::from_bytes(&bytes)?;

// Display
println!("{}", id); // ObjectId("507f1f77bcf86cd799439011")

PocketValue ​

PocketValue is the enum representing all supported value types:

VariantRust TypeDescription
Null()Null/absent value
Bool(bool)boolBoolean
Int(i64)i6464-bit signed integer
Float(f64)f6464-bit float
String(String)StringUTF-8 string
Array(Vec<PocketValue>)Vec<PocketValue>Ordered array
Object(HashMap<String, PocketValue>)HashMapEmbedded object
ObjectId(ObjectId)ObjectIdDocument identifier
Binary(Vec<u8>)Vec<u8>Raw binary data
DateTime(i64)i64Date/time as milliseconds since epoch
Regex(String, String)(String, String)Regular expression (pattern, options)
Decimal128(String)String128-bit decimal (stored as string)

Type Conversions ​

PocketValue implements From for common Rust types:

rust
use pocketdb::PocketValue;

let v1: PocketValue = "hello".into();       // String
let v2: PocketValue = 42i64.into();         // Int
let v3: PocketValue = 42u32.into();         // Int
let v4: PocketValue = 3.14f64.into();       // Float
let v5: PocketValue = true.into();          // Bool
let v6: PocketValue = serde_json::json!([1,2,3]).into(); // Array

JSON Conversions ​

Convert between PocketValue and serde_json::Value:

rust
use pocketdb::PocketValue;

// PocketValue -> serde_json::Value
let pv = PocketValue::String("hello".into());
let json: serde_json::Value = pv.into();

// serde_json::Value -> PocketValue
let pv: PocketValue = serde_json::json!({
    "name": "Alice",
    "age": 30,
    "tags": ["admin", "user"]
}).into();

// Special JSON types are preserved
let pv: PocketValue = serde_json::json!({ "$oid": "507f1f77bcf86cd799439011" }).into();
// -> PocketValue::ObjectId(...)

Type Checking and Extraction ​

rust
let val = PocketValue::Int(42);

val.type_name()     // "int"
val.is_null()       // false
val.is_number()     // true
val.as_i64()        // Some(42)
val.as_f64()        // Some(42.0)

Ordering ​

PocketValues are ordered by type, then by value:

Null < Bool < Int < Float < String < Array < Object < ObjectId < Binary < DateTime < Regex < Decimal128

Within the same type, natural ordering applies (numeric for numbers, lexicographic for strings).

Display ​

rust
println!("{}", PocketValue::Null);                    // null
println!("{}", PocketValue::Bool(true));               // true
println!("{}", PocketValue::Int(42));                  // 42
println!("{}", PocketValue::Float(3.14));              // 3.14
println!("{}", PocketValue::String("hello".into()));   // "hello"
println!("{}", PocketValue::Array(vec![].into()));     // []
println!("{}", PocketValue::DateTime(1700000000000));  // DateTime(1700000000000)

Complete Example ​

rust
use pocketdb::{Database, doc, Document, PocketValue};

fn main() -> pocketdb::Result<()> {
    let db = Database::open("documents_example.pdb")?;
    let users = db.collection("users")?;

    // Create document with doc! macro
    let id = users.insert(doc! {
        "name" => "Alice",
        "age" => 30,
        "email" => "alice@example.com",
        "active" => true,
        "scores" => [95, 87, 92],
        "address" => {
            "city" => "NYC",
            "zip" => "10001"
        }
    })?;

    // Find and access
    if let Some(user) = users.find_one(&pocketdb::query::Filter::eq(
        "_id", PocketValue::ObjectId(id),
    )) {
        if let Some(name) = user.get("name").and_then(|v| v.as_str()) {
            println!("Name: {}", name);
        }
        if let Some(arr) = user.get("scores").and_then(|v| v.as_array()) {
            println!("Scores: {:?}", arr);
        }
    }

    Ok(())
}

Released under the MIT License. Built by Muhammad Fiaz.