Documentation
NovusDB is an embedded document database written in pure Go. It stores JSON documents in a single file and lets you query them with full SQL. No server, no config, no dependencies.
Installation
Go (native)
git clone https://github.com/Felmond13/novusdb.git
cd NovusDB
go build ./...
Then import the api package in your project:
import "github.com/Felmond13/novusdb/api"
CLI binary
Download the latest release from GitHub Releases, or build from source:
git clone https://github.com/Felmond13/novusdb.git
cd NovusDB
go build -o NovusDB ./cmd/NovusDB/
Drivers (Python, Node.js, Java)
All drivers use a single C shared library built from Go. Build it first:
# Windows (requires MinGW)
.\drivers\build.ps1
# Linux / macOS
./drivers/build.sh
This produces novusdb.dll (Windows), libnovusdb.so (Linux), or libnovusdb.dylib (macOS).
Quick Start
package main
import (
"fmt"
"github.com/NovusDB/NovusDB/api"
)
func main() {
db, _ := api.Open("myapp.dlite")
defer db.Close()
// Insert documents
db.Exec(`INSERT INTO users VALUES (name="Alice", age=30, role="admin")`)
db.Exec(`INSERT INTO users VALUES (name="Bob", age=25, role="user")`)
// Query
res, _ := db.Exec(`SELECT name, age FROM users WHERE age > 20 ORDER BY age DESC`)
for _, doc := range res.Docs {
fmt.Println(doc.Doc)
}
}
INSERT
Insert documents with named field assignments. Collections are created automatically.
Basic insert
INSERT INTO users VALUES (name="Alice", age=30, role="admin")
Batch insert
INSERT INTO users VALUES
(name="Alice", age=30),
(name="Bob", age=25),
(name="Charlie", age=35)
Insert with nested documents
INSERT INTO users VALUES (
name="Alice",
address={city="Paris", zip="75001"},
tags=["admin", "vip"]
)
INSERT OR REPLACE
INSERT OR REPLACE INTO config VALUES (key="theme", value="dark")
INSERT ... SELECT
INSERT INTO archive SELECT * FROM logs WHERE created < "2025-01-01"
SELECT
Full SQL SELECT with filtering, sorting, pagination, and aliasing.
SELECT * FROM users
SELECT name, age FROM users WHERE age > 25
SELECT name AS username, age FROM users
WHERE role = "admin"
ORDER BY age DESC
LIMIT 10 OFFSET 5
SELECT DISTINCT role FROM users
Operators
| Operator | Example |
|---|---|
=, !=, <, >, <=, >= | WHERE age >= 18 |
AND, OR, NOT | WHERE age > 18 AND role = "admin" |
IS NULL, IS NOT NULL | WHERE email IS NOT NULL |
LIKE, NOT LIKE | WHERE name LIKE "Al%" |
IN, NOT IN | WHERE role IN ("admin", "mod") |
BETWEEN | WHERE age BETWEEN 18 AND 65 |
UPDATE
UPDATE users SET age = 31 WHERE name = "Alice"
UPDATE users SET role = "moderator", active = true
WHERE age > 30 AND role = "user"
DELETE
DELETE FROM users WHERE role = "guest"
DELETE FROM logs WHERE created < "2024-01-01"
Other table operations:
-- Drop an entire collection
DROP TABLE users
DROP TABLE IF EXISTS temp_data
-- Remove all documents but keep the collection
TRUNCATE TABLE logs
JOIN
NovusDB supports INNER, LEFT, and RIGHT joins. The query planner automatically picks the best strategy: Hash Join, Index Lookup, or Nested Loop.
-- Inner join
SELECT u.name, o.total
FROM users u
INNER JOIN orders o ON u.name = o.customer
WHERE o.total > 100
-- Left join (keep all users even without orders)
SELECT u.name, o.total
FROM users u
LEFT JOIN orders o ON u.name = o.customer
-- Right join
SELECT u.name, o.total
FROM users u
RIGHT JOIN orders o ON u.name = o.customer
-- Multi-table join
SELECT u.name, o.total, p.name AS product
FROM users u
JOIN orders o ON u.name = o.customer
JOIN products p ON o.product_id = p.id
Aggregations
SELECT COUNT(*) FROM users
SELECT role, COUNT(*), AVG(age), MIN(age), MAX(age)
FROM users
GROUP BY role
SELECT role, COUNT(*) AS cnt
FROM users
GROUP BY role
HAVING cnt > 5
ORDER BY cnt DESC
SELECT COUNT(DISTINCT role) FROM users
| Function | Description |
|---|---|
COUNT(*) | Number of documents |
COUNT(field) | Non-null values count |
COUNT(DISTINCT field) | Unique values count |
SUM(field) | Sum of numeric values |
AVG(field) | Average of numeric values |
MIN(field) | Minimum value |
MAX(field) | Maximum value |
Subqueries
-- Subquery in WHERE
SELECT * FROM users
WHERE age > (SELECT AVG(age) FROM users)
-- Subquery with IN
SELECT * FROM users
WHERE name IN (SELECT customer FROM orders WHERE total > 100)
-- Correlated subquery
SELECT * FROM users u
WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer = u.name)
UNION
-- Combine results (removes duplicates)
SELECT name FROM admins
UNION
SELECT name FROM moderators
-- Keep duplicates
SELECT name FROM admins
UNION ALL
SELECT name FROM moderators
CASE / WHEN
SELECT name,
CASE
WHEN age < 18 THEN "minor"
WHEN age < 65 THEN "adult"
ELSE "senior"
END AS category
FROM users
Nested JSON & Dot-Notation
NovusDB stores documents as nested JSON. Use dot-notation to access any depth.
-- Insert nested document
INSERT INTO users VALUES (
name="Alice",
address={city="Paris", country="FR", geo={lat=48.85, lon=2.35}},
tags=["admin", "vip"]
)
-- Query nested fields
SELECT * FROM users WHERE address.city = "Paris"
SELECT * FROM users WHERE address.geo.lat > 48.0
-- Update nested field
UPDATE users SET address.city = "Lyon" WHERE name = "Alice"
Native JSON Insert
The Go API and all drivers support inserting raw JSON strings directly.
// Go
db.InsertJSON("users", `{"name": "Alice", "age": 30, "tags": ["admin"]}`)
// SQL equivalent
INSERT INTO users VALUES (name="Alice", age=30, tags=["admin"])
Indexes
Persistent B+ Tree indexes for O(log n) lookups. The query planner uses them automatically.
-- Create an index
CREATE INDEX ON users (email)
CREATE INDEX IF NOT EXISTS ON users (age)
-- Drop an index
DROP INDEX ON users (email)
DROP INDEX IF EXISTS ON users (age)
EXPLAIN to verify your queries use indexes. Create indexes on fields you filter or join on frequently.
Views
-- Create a view
CREATE VIEW active_admins AS
SELECT name, email FROM users WHERE role = "admin" AND active = true
-- Query the view like a table
SELECT * FROM active_admins
-- Drop a view
DROP VIEW active_admins
DROP VIEW IF EXISTS active_admins
Sequences
Oracle-style sequences for auto-incrementing values. Supports integer and decimal increments.
-- Create a sequence
CREATE SEQUENCE user_seq START WITH 1 INCREMENT BY 1 MAXVALUE 999999
-- With all options
CREATE SEQUENCE order_seq
START WITH 1000
INCREMENT BY 1
MINVALUE 1
MAXVALUE 9999999
NOCYCLE
-- Decimal increment
CREATE SEQUENCE version_seq START WITH 1.0 INCREMENT BY 0.001
-- Use in INSERT
INSERT INTO users VALUES (id=user_seq.NEXTVAL, name="Alice")
-- Read current value (without incrementing)
SELECT order_seq.CURRVAL FROM orders
-- Drop a sequence
DROP SEQUENCE user_seq
DROP SEQUENCE IF EXISTS user_seq
| Option | Default | Description |
|---|---|---|
START WITH n | 1 | Initial value |
INCREMENT BY n | 1 | Step (integer or decimal) |
MINVALUE n | 1 | Minimum value |
MAXVALUE n | 9999999999 | Maximum value |
CYCLE / NOCYCLE | NOCYCLE | Wrap around at MAXVALUE |
Date Functions
Date functions return the current date/time as ISO-formatted strings.
-- Insert with current datetime
INSERT INTO logs VALUES (msg="started", created=SYSDATE)
INSERT INTO logs VALUES (msg="started", created=NOW())
-- Insert with date only
INSERT INTO events VALUES (title="Meeting", day=CURRENT_DATE)
-- Insert with full precision timestamp
INSERT INTO audit VALUES (action="login", ts=CURRENT_TIMESTAMP)
-- Use in WHERE clause
DELETE FROM logs WHERE created < SYSDATE
| Function | Format | Example output |
|---|---|---|
SYSDATE | YYYY-MM-DD HH:MM:SS | 2026-02-11 01:30:45 |
NOW() | YYYY-MM-DD HH:MM:SS | 2026-02-11 01:30:45 |
CURRENT_DATE | YYYY-MM-DD | 2026-02-11 |
CURRENT_TIMESTAMP | RFC 3339 Nano | 2026-02-11T01:30:45.123456789+01:00 |
<, >, =).
Transactions
ACID transactions with undo log. Single-writer isolation, multi-reader concurrency.
-- SQL (CLI)
BEGIN
UPDATE accounts SET balance = balance - 100 WHERE id = 1
UPDATE accounts SET balance = balance + 100 WHERE id = 2
COMMIT
-- Or rollback on error
BEGIN
UPDATE accounts SET balance = balance - 100 WHERE id = 1
ROLLBACK
// Go API
tx, _ := db.Begin()
tx.Exec(`UPDATE accounts SET balance = balance - 100 WHERE id = 1`)
tx.Exec(`UPDATE accounts SET balance = balance + 100 WHERE id = 2`)
err := tx.Commit() // or tx.Rollback()
Query Hints
Oracle-style query hints to control execution. Place them after SELECT, UPDATE, or DELETE.
-- Force full table scan (ignore indexes)
SELECT /*+ FULL_SCAN */ * FROM users WHERE age > 25
-- Force a specific index
SELECT /*+ FORCE_INDEX(age) */ * FROM users WHERE age > 25
-- Force join strategy
SELECT /*+ HASH_JOIN */ u.name, o.total
FROM users u
JOIN orders o ON u.name = o.customer
SELECT /*+ NESTED_LOOP */ u.name, o.total
FROM users u
JOIN orders o ON u.name = o.customer
-- Disable page cache for this query
SELECT /*+ NO_CACHE */ * FROM logs
| Hint | Effect |
|---|---|
/*+ FULL_SCAN */ | Force full collection scan, bypass indexes |
/*+ FORCE_INDEX(field) */ | Force use of a specific index |
/*+ HASH_JOIN */ | Force Hash Join strategy for JOINs |
/*+ NESTED_LOOP */ | Force Nested Loop strategy for JOINs |
/*+ NO_CACHE */ | Bypass page cache for this query |
/*+ PARALLEL(n) */ | Parallelism hint |
EXPLAIN
Inspect the query execution plan: scan strategy, index usage, cardinality, selectivity, and cost.
EXPLAIN SELECT * FROM users WHERE age > 25
EXPLAIN SELECT u.name, o.total
FROM users u
JOIN orders o ON u.name = o.customer
WHERE o.total > 100
Go API
Opening a database
// File-based (persistent)
db, err := api.Open("myapp.dlite")
defer db.Close()
// In-memory (for testing / WASM)
db, err := api.OpenMemory()
Executing queries
// Execute any SQL statement
result, err := db.Exec(`SELECT * FROM users WHERE age > 25`)
// Result struct
result.Docs // []ResultDoc — returned documents
result.RowsAffected // int64 — rows modified (INSERT/UPDATE/DELETE)
result.LastID // uint64 — last inserted record ID
Inserting JSON
id, err := db.InsertJSON("users", `{"name": "Alice", "age": 30, "tags": ["admin"]}`)
Transactions
tx, _ := db.Begin()
tx.Exec(`UPDATE accounts SET balance = balance - 100 WHERE id = 1`)
tx.Exec(`UPDATE accounts SET balance = balance + 100 WHERE id = 2`)
tx.Commit() // or tx.Rollback()
Introspection
db.Collections() // []string — list all collections
db.IndexDefs() // []IndexDef — list all indexes
db.Schema() // []CollectionSchema — full schema with field types
db.Views() // []string — list all views
db.Sequences() // map[string]*Sequence — list all sequences
db.Dump() // string — full SQL export
db.Vacuum() // compact deleted records
Cache & performance
hits, misses, size, capacity := db.CacheStats()
hitRate := db.CacheHitRate() // 0.0 to 1.0
Full method reference
| Method | Returns | Description |
|---|---|---|
api.Open(path) | *DB, error | Open or create a database file |
api.OpenMemory() | *DB, error | Create an in-memory database |
db.Close() | error | Close the database |
db.Exec(sql) | *Result, error | Execute any SQL statement |
db.InsertJSON(col, json) | uint64, error | Insert a raw JSON document |
db.InsertDoc(col, doc) | uint64, error | Insert a Document object |
db.Begin() | *Tx, error | Start a transaction |
tx.Exec(sql) | *Result, error | Execute SQL within a transaction |
tx.Commit() | error | Commit the transaction |
tx.Rollback() | error | Rollback the transaction |
db.Collections() | []string | List collections |
db.Schema() | []CollectionSchema | Full schema with types |
db.IndexDefs() | []IndexDef | List index definitions |
db.Views() | []string | List views |
db.Sequences() | map[string]*Sequence | List sequences |
db.Dump() | string | Full SQL export |
db.Vacuum() | int, error | Compact deleted records |
db.CacheStats() | hits, misses, size, cap | LRU page cache stats |
db.CacheHitRate() | float64 | Cache hit ratio (0.0-1.0) |
db.SetLockPolicy(p) | — | Set lock policy (Wait or Fail) |
Python Driver
Uses ctypes from the standard library. Zero dependencies.
from NovusDB import NovusDB
with NovusDB("myapp.dlite") as db:
# Execute SQL
db.exec('INSERT INTO users VALUES (name="Alice", age=30)')
result = db.exec("SELECT * FROM users WHERE age > 25")
for doc in result["docs"]:
print(doc["name"], doc["age"])
# Insert raw JSON
db.insert_json("users", '{"name": "Bob", "tags": ["vip"]}')
# List collections
print(db.collections())
Node.js Driver
Requires ffi-napi and ref-napi.
const { NovusDB } = require('./NovusDB');
const db = new NovusDB('myapp.dlite');
db.exec('INSERT INTO users VALUES (name="Alice", age=30)');
const result = db.exec('SELECT * FROM users');
console.log(result.docs);
db.insertJSON('users', '{"name": "Bob", "age": 25}');
console.log(db.collections());
db.close();
Java Driver
Uses JNA (Java Native Access). Implements AutoCloseable for try-with-resources.
try (NovusDB db = new NovusDB("myapp.dlite")) {
// Execute SQL
String result = db.exec("SELECT * FROM users");
System.out.println(result);
// Insert JSON
long id = db.insertJSON("users", "{\"name\": \"Alice\", \"age\": 30}");
// List collections
System.out.println(db.collections());
}
C Shared Library
All drivers use this C API under the hood. You can call it directly from any language with FFI support.
#include "NovusDB.h"
long long db = NovusDB_open("myapp.dlite");
char* result = NovusDB_exec(db, "SELECT * FROM users");
printf("%s\n", result);
NovusDB_free(result);
long long id = NovusDB_insert_json(db, "users", "{\"name\": \"Alice\"}");
char* cols = NovusDB_collections(db);
printf("%s\n", cols);
NovusDB_free(cols);
NovusDB_close(db);
| Function | Description |
|---|---|
NovusDB_open(path) | Open a database, returns a handle |
NovusDB_close(handle) | Close the connection |
NovusDB_exec(handle, sql) | Execute SQL, returns JSON string |
NovusDB_insert_json(handle, col, json) | Insert a JSON document |
NovusDB_collections(handle) | List collections (JSON) |
NovusDB_error(handle) | Last error message |
NovusDB_dump(handle) | Full SQL export |
NovusDB_free(ptr) | Free a returned string |
CLI
The CLI is an interactive REPL for querying and managing your database.
# Open or create a database
./NovusDB mydata.dlite
# Execute a query and exit
./NovusDB mydata.dlite -e "SELECT * FROM users"
Dot Commands
Special commands available in the CLI and the online playground.
| Command | Description |
|---|---|
.tables | List all collections |
.schema | Show collection schemas with field types and frequency |
.indexes | List all indexes |
.sequences | List all sequences with current values |
.dump | Full SQL export (CREATE INDEX + INSERT INTO) |
.help | Show available commands |
Built-in Functions
Aggregate Functions
| Function | Description | Example |
|---|---|---|
COUNT(*) | Count all documents | SELECT COUNT(*) FROM users |
COUNT(field) | Count non-null values | SELECT COUNT(email) FROM users |
COUNT(DISTINCT field) | Count unique values | SELECT COUNT(DISTINCT role) FROM users |
SUM(field) | Sum numeric values | SELECT SUM(total) FROM orders |
AVG(field) | Average of numeric values | SELECT AVG(age) FROM users |
MIN(field) | Minimum value | SELECT MIN(price) FROM products |
MAX(field) | Maximum value | SELECT MAX(price) FROM products |
Date Functions
| Function | Description | Example |
|---|---|---|
SYSDATE | Current date and time (YYYY-MM-DD HH:MM:SS) | INSERT INTO logs VALUES (ts=SYSDATE) |
NOW() | Same as SYSDATE | INSERT INTO logs VALUES (ts=NOW()) |
CURRENT_DATE | Current date only (YYYY-MM-DD) | INSERT INTO events VALUES (day=CURRENT_DATE) |
CURRENT_TIMESTAMP | Full precision timestamp (RFC 3339) | INSERT INTO audit VALUES (ts=CURRENT_TIMESTAMP) |
Sequence Functions
| Function | Description | Example |
|---|---|---|
seq.NEXTVAL | Increment and return next value | INSERT INTO users VALUES (id=user_seq.NEXTVAL) |
seq.CURRVAL | Return current value (no increment) | SELECT order_seq.CURRVAL FROM orders |