📚 Bucket DB

Querying

BucketDB provides a fluent QueryBuilder API for reading data. It allows filtering, applying custom JavaScript functions, limiting results, and leveraging native Copy-on-Write (CoW) B+Tree indexes.

Basic Execution

Queries must always specify the tableName.

const results = await db.query('users')
  .where('role', '=', 'admin')
  .limit(50)
  .execute();

Supported Operators

BucketDB supports the following exact operators:

  • where('field', '=', 'value') or .eq()
  • where('field', '!=', 'value') or .ne()
  • where('field', '>', 'value') or .gt()
  • where('field', '>=', 'value') or .gte()
  • where('field', '<', 'value') or .lt()
  • where('field', '<=', 'value') or .lte()
  • where('field', 'IN', ['value1', 'value2']) or .in()

Performance and the Query Optimizer

The Mock Query Executor

As of v0.2.0, BucketDB features a highly dynamic Mock Query Executor. The background Write-Forward Service maintains a strictly bounded _sample system table (e.g., 100 rows per schema version).

When you execute a query, the Query Optimizer does not rely on stale statistics. Instead, it runs the actual JavaScript predicates against this in-memory sample. It calculates a projected physical block read count and assigns a cost score to each potential execution plan. The plan with the lowest cost wins. This ensures optimal index selection with negligible overhead.

Indexes vs. Full Table Scans

1. B+Tree Indexes

If you define indexes: ['role'] in your Schema Definition, the optimizer will detect exact-match equality conditions (.where('role', '=', 'admin')) as well as range conditions (>, >=, <, <=, in).

  • Instead of downloading all data blocks, it traverses the B+Tree blocks under the indexes/ prefix.
  • It returns precise pointers to specific rows in specific blocks, downloading only what is necessary.
  • Complexity: O(log N)

2. Full Table Scans

If you query a field that is unindexed, BucketDB cannot use the B+Tree.

  • It must fetch the Block Index from Block 0.
  • It must download every single block associated with the table into the local cache.
  • It scans the blocks linearly in memory.
  • Complexity: O(N)

Warning: Full table scans are exceptionally slow on multi-gigabyte datasets and can spike your S3 GET request costs. Always define indexes for fields you query frequently.

Batch Overlay

BucketDB is built around eventual consistency and Write-Forward logs. You can pass an active batch to a query to overlay the unflushed data on top of the S3 state.

const b = db.batch();
b.insert('users', { id: 'u1', email: 'test@example.com', role: 'guest' });

// This query returns the newly inserted row, even though it hasn't 
// been flushed to the S3 Write-Forward log yet!
const localGuests = await db.query('users')
  .overlay(b)
  .where('role', '=', 'guest')
  .execute();

Custom Functions

For complex logic that cannot be expressed via simple operators (e.g., regex checks, complex mathematical formulas, or deeply nested JSON iteration), you can execute registered JavaScript functions natively during a query.

See Custom Functions for a complete guide.