From 2f80dcf9b7947ed9ce162f32b2c10b361699b0e9 Mon Sep 17 00:00:00 2001 From: mask Date: Fri, 6 Feb 2026 16:32:58 -0600 Subject: [PATCH 01/58] docs(ray-docs): align API docs and examples with current code --- ray-docs/src/lib/docs.ts | 2 +- ray-docs/src/routes/docs/$.tsx | 2 +- ray-docs/src/routes/docs/api/$.tsx | 76 ++++-- .../src/routes/docs/getting-started/$.tsx | 8 +- .../docs/getting-started/installation.tsx | 6 +- ray-docs/src/routes/docs/guides/$.tsx | 252 +++++++----------- .../routes/docs/internals/-performance.tsx | 64 +++-- ray-docs/src/routes/docs/internals/-wal.tsx | 12 +- ray-docs/src/routes/index.tsx | 26 +- 9 files changed, 208 insertions(+), 240 deletions(-) diff --git a/ray-docs/src/lib/docs.ts b/ray-docs/src/lib/docs.ts index 6f29200..f67f94d 100644 --- a/ray-docs/src/lib/docs.ts +++ b/ray-docs/src/lib/docs.ts @@ -85,7 +85,7 @@ export const docsStructure: DocSection[] = [ }, { title: "Low-Level API", - description: "Direct storage access", + description: "Direct database primitives", slug: "api/low-level", }, { diff --git a/ray-docs/src/routes/docs/$.tsx b/ray-docs/src/routes/docs/$.tsx index 08ffcc4..ec68050 100644 --- a/ray-docs/src/routes/docs/$.tsx +++ b/ray-docs/src/routes/docs/$.tsx @@ -96,7 +96,7 @@ function DocPageContent(props: { slug: string }) { traversals
  • - Vector search – HNSW-indexed similarity queries + Vector search – IVF-based similarity queries
  • Embedded – Runs in your process, no server needed diff --git a/ray-docs/src/routes/docs/api/$.tsx b/ray-docs/src/routes/docs/api/$.tsx index 5b8f41e..986780e 100644 --- a/ray-docs/src/routes/docs/api/$.tsx +++ b/ray-docs/src/routes/docs/api/$.tsx @@ -117,7 +117,7 @@ db.countEdges(follows)`}

    Next Steps

    @@ -128,46 +128,64 @@ db.countEdges(follows)`} return (

    - The low-level API provides direct access to the underlying storage - engine for advanced use cases. + The low-level API uses the Database class for direct + graph operations, transaction control, and batched writes.

    -

    Storage Access

    +

    Open and Write

    Batch Operations

    +db.addEdgesBatch(edges); // Array<{ src, etype, dst }> +db.addEdgesWithPropsBatch(edgesWithProps); +db.commit(); + +// Optional maintenance checkpoint after ingest +db.checkpoint();`} language="typescript" /> -

    Iterators

    +

    Streaming and Pagination

    diff --git a/ray-docs/src/routes/docs/getting-started/$.tsx b/ray-docs/src/routes/docs/getting-started/$.tsx index a3dae51..61c6082 100644 --- a/ray-docs/src/routes/docs/getting-started/$.tsx +++ b/ray-docs/src/routes/docs/getting-started/$.tsx @@ -68,7 +68,7 @@ function DocPageContent(props: { slug: string }) { typescript={`import { kite } from '@kitedb/core'; // Define schema inline when opening the database -const db = kite('./social.kitedb', { +const db = await kite('./social.kitedb', { nodes: [ { name: 'user', @@ -151,7 +151,7 @@ let bob = db.insert("user") .returning()?; // Create a follow relationship -db.link(alice.id, "follows", bob.id, Some(json!({ +db.link(alice.id(), "follows", bob.id(), Some(json!({ "followedAt": std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH)? .as_secs() @@ -185,14 +185,14 @@ const followsBob = db.hasEdge(alice.id, 'follows', bob.id); console.log('Alice follows Bob:', followsBob);`} rust={`// Find all users Alice follows let following = db - .from(alice.id) + .from(alice.id()) .out(Some("follows")) .nodes()?; println!("Alice follows: {} users", following.len()); // Check if Alice follows Bob -let follows_bob = db.has_edge(alice.id, "follows", bob.id)?; +let follows_bob = db.has_edge(alice.id(), "follows", bob.id())?; println!("Alice follows Bob: {}", follows_bob);`} python={`# Find all users Alice follows following = (db diff --git a/ray-docs/src/routes/docs/getting-started/installation.tsx b/ray-docs/src/routes/docs/getting-started/installation.tsx index 2eef324..d10105f 100644 --- a/ray-docs/src/routes/docs/getting-started/installation.tsx +++ b/ray-docs/src/routes/docs/getting-started/installation.tsx @@ -20,8 +20,8 @@ function InstallationPage() {

    Requirements

    @@ -31,7 +31,7 @@ function InstallationPage() { typescript={`import { kite } from '@kitedb/core'; // Open database with a simple schema -const db = kite('./test.kitedb', { +const db = await kite('./test.kitedb', { nodes: [ { name: 'user', diff --git a/ray-docs/src/routes/docs/guides/$.tsx b/ray-docs/src/routes/docs/guides/$.tsx index ae9dd14..5714007 100644 --- a/ray-docs/src/routes/docs/guides/$.tsx +++ b/ray-docs/src/routes/docs/guides/$.tsx @@ -67,7 +67,7 @@ function DocPageContent(props: { slug: string }) { = db.all("user")?.collect(); // Count nodes -let user_count = db.count_nodes(Some("user"))?;`} +let user_count = db.count_nodes_by_type("user")?;`} python={`# Get by key user = db.get(user, "alice") -# Get by node ID -user_by_id = db.get_by_id(alice.id) +# Get lightweight ref by key +user_ref = db.get_ref(user, "alice") # Check if exists -exists = db.exists(alice.id) +exists = alice is not None and db.exists(alice) # List all nodes of a type -all_users = db.all(user) +all_users = list(db.all(user)) # Count nodes -user_count = db.count_nodes("user")`} +user_count = db.count(user)`} />

    Updating Data

    @@ -299,35 +301,33 @@ db.update(user, 'alice') .unset('email') .execute();`} rust={`// Update by node ID -db.update_by_id(alice.id) - .set("name", "Alice C.") +db.update_by_id(alice.id()) + .set("name", PropValue::String("Alice C.".into())) .execute()?; // Update multiple properties -db.update_by_id(alice.id) - .set_all(json!({ - "name": "Alice Chen", - "email": "newemail@example.com" - })) +db.update_by_id(alice.id()) + .set("name", PropValue::String("Alice Chen".into())) + .set("email", PropValue::String("newemail@example.com".into())) .execute()?; // Remove a property -db.update_by_id(alice.id) +db.update_by_id(alice.id()) .unset("email") .execute()?;`} - python={`# Update by node ID -(db.update_by_id(alice.id) - .set("name", "Alice C.") + python={`# Update by node reference +(db.update(alice) + .set(name="Alice C.") .execute()) # Update multiple properties -(db.update_by_id(alice.id) - .set_all({"name": "Alice Chen", "email": "newemail@example.com"}) +(db.update(alice) + .set({"name": "Alice Chen", "email": "newemail@example.com"}) .execute()) -# Remove a property -(db.update_by_id(alice.id) - .unset("email") +# Update another property +(db.update(alice) + .set(email="newemail@example.com") .execute())`} /> @@ -339,15 +339,19 @@ db.deleteById(alice.id); // Delete by key db.deleteByKey('user', 'alice');`} rust={`// Delete by node ID -db.delete_by_id(alice.id)?; +db.delete_node(alice.id())?; -// Delete by key -db.delete_by_key("user", "alice")?;`} - python={`# Delete by node ID -db.delete_by_id(alice.id) +// Delete by key (lookup then delete) +if let Some(node) = db.get("user", "alice")? { + db.delete_node(node.id())?; +}`} + python={`# Delete by node reference +db.delete(alice) -# Delete by key -db.delete_by_key(user, "alice")`} +# Delete by key (lookup then delete) +node = db.get(user, "alice") +if node is not None: + db.delete(node)`} />

    Next Steps

    @@ -388,19 +392,19 @@ const connections = db .nodes();`} rust={`// Find all users that Alice follows (outgoing edges) let following = db - .from(alice.id) + .from(alice.id()) .out(Some("follows")) .nodes()?; // Find all followers of Alice (incoming edges) let followers = db - .from(alice.id) + .from(alice.id()) .in_(Some("follows")) .nodes()?; // Follow edges in both directions let connections = db - .from(alice.id) + .from(alice.id()) .both(Some("knows")) .nodes()?;`} python={`# Find all users that Alice follows (outgoing edges) @@ -442,14 +446,14 @@ const authorsOfLikedArticles = db .nodes();`} rust={`// Find friends of friends (2-hop) let friends_of_friends = db - .from(alice.id) + .from(alice.id()) .out(Some("follows")) .out(Some("follows")) .nodes()?; // Chain different edge types let authors_of_liked = db - .from(alice.id) + .from(alice.id()) .out(Some("likes")) // Alice -> Articles .in_(Some("authored")) // Articles <- Users .nodes()?;`} @@ -486,7 +490,7 @@ const topConnections = db .nodes();`} rust={`// Traverse 1-3 hops let network = db - .from(alice.id) + .from(alice.id()) .traverse(Some("follows"), TraverseOptions { min_depth: Some(1), max_depth: 3, @@ -496,7 +500,7 @@ let network = db // Limit results let top_connections = db - .from(alice.id) + .from(alice.id()) .out(Some("follows")) .take(10) .nodes()?;`} @@ -574,7 +578,7 @@ index.set(doc.id, embedding);`} let embedding: Vec = get_embedding("Your document content")?; // Store the vector, associated with a node ID -index.set(doc.id, &embedding)?;`} +index.set(doc.id(), &embedding)?;`} python={`# Generate embedding with your preferred provider response = openai.embeddings.create( model="text-embedding-ada-002", @@ -641,13 +645,13 @@ index.buildIndex(); const stats = index.stats(); console.log(\`Total vectors: \${stats.totalVectors}\`);`} rust={`// Check if a node has a vector -let has_vector = index.has(doc.id)?; +let has_vector = index.has(doc.id())?; // Get a stored vector -let vector = index.get(doc.id)?; +let vector = index.get(doc.id())?; // Delete a vector -index.delete(doc.id)?; +index.delete(doc.id())?; // Build/rebuild the IVF index for faster search index.build_index()?; @@ -713,7 +717,7 @@ let mut db = Kite::open("./my.kitedb", options)?; db.transaction(|ctx| { let alice = ctx.create_node("user", "alice", HashMap::new())?; let bob = ctx.create_node("user", "bob", HashMap::new())?; - ctx.link(alice.id, "follows", bob.id)?; + ctx.link(alice.id(), "follows", bob.id())?; Ok(()) })?;`} python={`from kitedb import kite @@ -793,7 +797,7 @@ db.commit()`} Max throughput, single writer - begin_bulk() + batch APIs + beginBulk() + batch APIs Atomic ingest w/ MVCC @@ -801,7 +805,7 @@ db.commit()`} Multi-writer throughput - sync_mode=Normal + group commit + chunked batches + syncMode: 'Normal' + group commit + chunked batches @@ -955,7 +959,7 @@ if db.has_transaction(): Max ingest throughput, single writer - begin_bulk() + batch APIs + beginBulk() + batch APIs Atomic ingest with MVCC @@ -963,15 +967,15 @@ if db.has_transaction(): Multi-writer throughput - sync_mode=Normal + group commit (1-2ms) + syncMode: 'Normal' + group commit (1-2ms) Strong durability per commit - sync_mode=Full + syncMode: 'Full' Throwaway or test data - sync_mode=Off + syncMode: 'Off' @@ -1020,32 +1024,32 @@ db.commit()`} Single-writer ingest - sync_mode=Normal, group_commit=false, - WAL ≥ 256MB, auto_checkpoint=false + syncMode: 'Normal', groupCommitEnabled: false, + WAL ≥ 256MB, autoCheckpoint: false Multi-writer throughput - sync_mode=Normal, group_commit=true + syncMode: 'Normal', groupCommitEnabled: true (1-2ms window), chunked batches Max durability - sync_mode=Full, smaller batches + syncMode: 'Full', smaller batches Max speed (test) - sync_mode=Off + syncMode: 'Off'

    Checklist

      -
    • Use batch APIs: create_nodes_batch, add_edges_batch, add_edges_with_props_batch
    • -
    • Prefer begin_bulk() for ingest; commit in chunks
    • +
    • Use batch APIs: createNodesBatch, addEdgesBatch, addEdgesWithPropsBatch
    • +
    • Prefer beginBulk() for ingest; commit in chunks
    • Increase WAL size for large ingest (256MB+)
    • Disable auto-checkpoint during ingest; checkpoint once at the end
    • Use low-level API for hot paths in JS/TS
    • @@ -1101,15 +1105,17 @@ const results = await Promise.all([ // Workers can read concurrently from the same database file`} rust={`use std::sync::{Arc, RwLock}; use std::thread; +use kitedb::api::kite::Kite; -let db = Arc::new(RwLock::new(Kite::open("./data.kitedb")?)); +let db = Arc::new(RwLock::new(Kite::open("./data.kitedb", options)?)); let handles: Vec<_> = (0..4).map(|i| { let db = Arc::clone(&db); thread::spawn(move || { // Multiple threads can acquire read locks simultaneously + let key = format!("user{}", i); let guard = db.read().unwrap(); - guard.get_node(format!("user:{}", i)) + guard.get("user", &key).ok().flatten() }) }).collect(); @@ -1141,47 +1147,12 @@ for t in threads: print(results)`} /> -

      Performance Scaling

      +

      Performance Notes

      - Benchmarks show ~1.5-1.8x throughput improvement with 4-8 reader - threads: + Read throughput typically improves with parallel readers, while write + throughput is constrained by serialized commit ordering. Measure with + your workload and tune batch sizes and sync mode accordingly.

      - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
      ThreadsRelative ThroughputNotes
      11.0x (baseline)Single-threaded
      2~1.3xGood scaling
      4~1.5-1.6xSweet spot for most workloads
      8~1.6-1.8xDiminishing returns
      16~1.7-1.9xLock contention increases

      Best Practices

        @@ -1203,63 +1174,44 @@ print(results)`}
      -

      MVCC and Snapshot Isolation

      +

      MVCC and Transaction Semantics

      - KiteDB uses Multi-Version Concurrency Control (MVCC) to provide - snapshot isolation: + KiteDB uses Multi-Version Concurrency Control (MVCC) with serialized + writes:

        -
      • Readers never block writers
      • -
      • Writers never block readers
      • +
      • Multiple readers can run concurrently
      • - Each transaction sees a consistent snapshot from its start time + A write waits for in-flight reads, then blocks new reads while it + commits
      • -
      • Write conflicts are detected and one transaction is aborted
      • +
      • Each committed transaction is atomic
      • +
      • Write conflicts are detected at commit time
      { + const alice = ctx.get(user, 'alice'); + if (alice) { + ctx.update(user, 'alice') + .set('name', 'Alice Updated') + .execute(); + } +});`} + rust={`// Atomic transaction with TxContext +db.transaction(|ctx| { + let alice = ctx.get("user", "alice")?; + if let Some(node) = alice { + ctx.set_prop(node.id(), "name", PropValue::String("Alice Updated".into()))?; + } + Ok(()) +})?;`} + python={`# Atomic transaction (context manager handles commit/rollback) +with db.transaction(): + alice = db.get(user, "alice") + if alice is not None: + db.update(user, "alice").set(name="Alice Updated").execute()`} />

      Limitations

      diff --git a/ray-docs/src/routes/docs/internals/-performance.tsx b/ray-docs/src/routes/docs/internals/-performance.tsx index 4e583be..dbcf199 100644 --- a/ray-docs/src/routes/docs/internals/-performance.tsx +++ b/ray-docs/src/routes/docs/internals/-performance.tsx @@ -320,7 +320,7 @@ export function PerformancePage() {

      Latest snapshot (single-file raw, Rust core, 10k nodes / 50k edges, - edge types=3, edge props=10, sync_mode=Normal, group_commit=false, + edge types=3, edge props=10, syncMode=Normal, groupCommitEnabled=false, February 4, 2026):

      @@ -379,16 +379,16 @@ export function PerformancePage() {

      Write Durability vs Throughput

      • - Defaults stay safe: sync_mode=Full,{" "} - group_commit=false. + Defaults stay safe: syncMode=Full,{" "} + groupCommitEnabled=false.
      • Single-writer, low latency:{" "} - sync_mode=Normal + group_commit=false. + syncMode=Normal + groupCommitEnabled=false.
      • Multi-writer throughput:{" "} - sync_mode=Normal + group_commit=true (1-2ms). + syncMode=Normal + groupCommitEnabled=true (1-2ms). {" "} Scaling saturates quickly; prefer prep-parallel + single writer for max ingest. See{" "} @@ -397,7 +397,7 @@ export function PerformancePage() {
      • Highest speed, weakest durability:{" "} - sync_mode=Off (testing/throwaway only). + syncMode=Off (testing/throwaway only).

      @@ -410,8 +410,8 @@ export function PerformancePage() { Workload - sync_mode - group_commit + syncMode + groupCommitEnabled Why @@ -447,14 +447,14 @@ export function PerformancePage() {

      • Fastest ingest (single writer):{" "} - begin_bulk() + create_nodes_batch() +{" "} - add_edges_batch() / add_edges_with_props_batch(),{" "} - sync_mode=Normal, group_commit=false, WAL ≥ 256MB, + beginBulk() + createNodesBatch() +{" "} + addEdgesBatch() / addEdgesWithPropsBatch(),{" "} + syncMode=Normal, groupCommitEnabled=false, WAL ≥ 256MB, auto-checkpoint off during ingest, then checkpoint.
      • Multi-writer throughput:{" "} - sync_mode=Normal + group_commit=true (1-2ms window), + syncMode=Normal + groupCommitEnabled=true (1-2ms window), batched ops per transaction.
      • @@ -463,7 +463,7 @@ export function PerformancePage() {
      • Max speed, lowest durability:{" "} - sync_mode=Off for testing only. + syncMode=Off for testing only.

      @@ -473,10 +473,10 @@ export function PerformancePage() {

      Bulk Ingest Example (Low-Level)

      @@ -553,21 +553,19 @@ const stats = await db.stats();`} diff --git a/ray-docs/src/routes/docs/internals/-wal.tsx b/ray-docs/src/routes/docs/internals/-wal.tsx index d4af9b8..3f9b5e8 100644 --- a/ray-docs/src/routes/docs/internals/-wal.tsx +++ b/ray-docs/src/routes/docs/internals/-wal.tsx @@ -524,19 +524,19 @@ export function WALPage() {
      • - sync_mode = Normal + syncMode = Normal
      • - group_commit_enabled = true + groupCommitEnabled = true
      • - group_commit_window_ms = 2 + groupCommitWindowMs = 2
      • - begin_bulk() + batch APIs for ingest (MVCC disabled) + beginBulk() + batch APIs for ingest (MVCC disabled)
      • - Optional: increase wal_size (e.g., 64MB) for heavy ingest to + Optional: increase walSizeMb (e.g., 64MB) for heavy ingest to reduce checkpoints
      @@ -564,7 +564,7 @@ export function WALPage() { use resizeWal (offline) to grow it, or rebuild into a new file. To prevent single transactions from overfilling the active WAL region, split work into smaller commits (see bulkWrite or - chunked begin_bulk() sessions) and consider disabling + chunked beginBulk() sessions) and consider disabling background checkpoints during ingest.

      diff --git a/ray-docs/src/routes/index.tsx b/ray-docs/src/routes/index.tsx index 46f4904..a7c058b 100644 --- a/ray-docs/src/routes/index.tsx +++ b/ray-docs/src/routes/index.tsx @@ -107,7 +107,7 @@ function HomePage() { typescript: `import { kite } from '@kitedb/core'; // Open database with schema -const db = kite('./knowledge.kitedb', { +const db = await kite('./knowledge.kitedb', { nodes: [ { name: 'document', @@ -187,14 +187,14 @@ const results = db .nodes();`, rust: `// Find all topics discussed by Alice's documents let topics = db - .from(alice.id) + .from(alice.id()) .out(Some("wrote")) // Alice -> Document .out(Some("discusses")) // Document -> Topic .nodes()?; // Multi-hop traversal let results = db - .from(start_node.id) + .from(start_node.id()) .out(Some("knows")) .out(Some("worksAt")) .take(10) @@ -245,7 +245,7 @@ let mut index = VectorIndex::new(VectorIndexOptions { })?; // Add vectors for nodes -index.set(doc.id, &embedding)?; +index.set(doc.id(), &embedding)?; // Find similar documents let similar = index.search(&query_embedding, SimilarOptions { @@ -300,12 +300,12 @@ let doc = db.insert("document") .returning()?; // Create relationships -db.link(doc.id, "discusses", topic.id, Some(json!({ +db.link(doc.id(), "discusses", topic.id(), Some(json!({ "relevance": 0.95 })))?; // Update properties -db.update_by_id(doc.id) +db.update_by_id(doc.id()) .set("title", "Updated Title") .execute()?;`, python: `# Insert with returning @@ -317,8 +317,8 @@ doc = (db.insert(document) db.link(doc, discusses, topic, relevance=0.95) # Update properties -(db.update_by_id(doc.id) - .set("title", "Updated Title") +(db.update(doc) + .set(title="Updated Title") .execute())`, }; @@ -614,8 +614,8 @@ db.link(doc, discusses, topic, relevance=0.95) icon={