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)
    }
}
Tip: Collections are created automatically on the first INSERT. No CREATE TABLE needed.

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

OperatorExample
=, !=, <, >, <=, >=WHERE age >= 18
AND, OR, NOTWHERE age > 18 AND role = "admin"
IS NULL, IS NOT NULLWHERE email IS NOT NULL
LIKE, NOT LIKEWHERE name LIKE "Al%"
IN, NOT INWHERE role IN ("admin", "mod")
BETWEENWHERE 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
Join strategies: NovusDB automatically selects Hash Join O(n+m), Index Lookup O(n·log m), or Nested Loop O(n·m). Use query hints to override.

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
FunctionDescription
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)
Tip: Use 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
OptionDefaultDescription
START WITH n1Initial value
INCREMENT BY n1Step (integer or decimal)
MINVALUE n1Minimum value
MAXVALUE n9999999999Maximum value
CYCLE / NOCYCLENOCYCLEWrap 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
FunctionFormatExample output
SYSDATEYYYY-MM-DD HH:MM:SS2026-02-11 01:30:45
NOW()YYYY-MM-DD HH:MM:SS2026-02-11 01:30:45
CURRENT_DATEYYYY-MM-DD2026-02-11
CURRENT_TIMESTAMPRFC 3339 Nano2026-02-11T01:30:45.123456789+01:00
Note: Dates are stored as strings. Use ISO format (YYYY-MM-DD) to ensure correct sorting and comparison with standard operators (<, >, =).

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
HintEffect
/*+ 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

MethodReturnsDescription
api.Open(path)*DB, errorOpen or create a database file
api.OpenMemory()*DB, errorCreate an in-memory database
db.Close()errorClose the database
db.Exec(sql)*Result, errorExecute any SQL statement
db.InsertJSON(col, json)uint64, errorInsert a raw JSON document
db.InsertDoc(col, doc)uint64, errorInsert a Document object
db.Begin()*Tx, errorStart a transaction
tx.Exec(sql)*Result, errorExecute SQL within a transaction
tx.Commit()errorCommit the transaction
tx.Rollback()errorRollback the transaction
db.Collections()[]stringList collections
db.Schema()[]CollectionSchemaFull schema with types
db.IndexDefs()[]IndexDefList index definitions
db.Views()[]stringList views
db.Sequences()map[string]*SequenceList sequences
db.Dump()stringFull SQL export
db.Vacuum()int, errorCompact deleted records
db.CacheStats()hits, misses, size, capLRU page cache stats
db.CacheHitRate()float64Cache 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);
FunctionDescription
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.

CommandDescription
.tablesList all collections
.schemaShow collection schemas with field types and frequency
.indexesList all indexes
.sequencesList all sequences with current values
.dumpFull SQL export (CREATE INDEX + INSERT INTO)
.helpShow available commands

Built-in Functions

Aggregate Functions

FunctionDescriptionExample
COUNT(*)Count all documentsSELECT COUNT(*) FROM users
COUNT(field)Count non-null valuesSELECT COUNT(email) FROM users
COUNT(DISTINCT field)Count unique valuesSELECT COUNT(DISTINCT role) FROM users
SUM(field)Sum numeric valuesSELECT SUM(total) FROM orders
AVG(field)Average of numeric valuesSELECT AVG(age) FROM users
MIN(field)Minimum valueSELECT MIN(price) FROM products
MAX(field)Maximum valueSELECT MAX(price) FROM products

Date Functions

FunctionDescriptionExample
SYSDATECurrent date and time (YYYY-MM-DD HH:MM:SS)INSERT INTO logs VALUES (ts=SYSDATE)
NOW()Same as SYSDATEINSERT INTO logs VALUES (ts=NOW())
CURRENT_DATECurrent date only (YYYY-MM-DD)INSERT INTO events VALUES (day=CURRENT_DATE)
CURRENT_TIMESTAMPFull precision timestamp (RFC 3339)INSERT INTO audit VALUES (ts=CURRENT_TIMESTAMP)

Sequence Functions

FunctionDescriptionExample
seq.NEXTVALIncrement and return next valueINSERT INTO users VALUES (id=user_seq.NEXTVAL)
seq.CURRVALReturn current value (no increment)SELECT order_seq.CURRVAL FROM orders