Datalog

Nucleus includes a built-in Datalog engine for recursive queries and logic programming. Define facts and rules, then query with automatic fixed-point evaluation — integrated directly with your relational and graph data.

Facts

Assert ground truth:

SELECT DATALOG_ASSERT('parent(alice, bob).');
SELECT DATALOG_ASSERT('parent(bob, charlie).');
SELECT DATALOG_ASSERT('parent(charlie, diana).');

Rules

Define derived relations:

-- Direct parent relationship
SELECT DATALOG_RULE('ancestor(X, Y) :- parent(X, Y).');

-- Transitive closure (recursive)
SELECT DATALOG_RULE('ancestor(X, Z) :- ancestor(X, Y), parent(Y, Z).');

Queries

-- Who are Alice's descendants?
SELECT DATALOG_QUERY('?- ancestor(alice, Who).');
-- → [{"Who":"bob"}, {"Who":"charlie"}, {"Who":"diana"}]

-- Is Alice an ancestor of Diana?
SELECT DATALOG_QUERY('?- ancestor(alice, diana).');
-- → [{}]  (empty binding = true)

Retracting Facts

-- Remove a specific fact
SELECT DATALOG_RETRACT('parent(bob, charlie).');

-- Clear all facts for a predicate
SELECT DATALOG_CLEAR('parent');

Cross-Model Integration

Import data from relational tables and the graph engine:

-- Import rows from a SQL table as facts
-- Each row becomes: predicate(col1, col2, ...)
SELECT DATALOG_IMPORT('employees', 'employee');

-- Import graph edges as facts
-- Each edge becomes: predicate(from_id, edge_type, to_id)
SELECT DATALOG_IMPORT_GRAPH('edge');

-- Import graph nodes
-- Each node becomes: predicate(node_id, label)
SELECT DATALOG_IMPORT_NODES('node');

Then query across models:

-- Find all employees reachable through the management graph
SELECT DATALOG_RULE('manages(X, Y) :- edge(X, "MANAGES", Y).');
SELECT DATALOG_RULE('manages(X, Z) :- manages(X, Y), edge(Y, "MANAGES", Z).');
SELECT DATALOG_QUERY('?- manages(1, Who).');

Negation

Stratified negation using \+:

SELECT DATALOG_RULE('orphan(X) :- person(X), \\+ parent(_, X).');
SELECT DATALOG_QUERY('?- orphan(Who).');

Aggregates

Built-in aggregate functions in rule heads:

-- Count children per parent
SELECT DATALOG_RULE('child_count(X, count<Y>) :- parent(X, Y).');
SELECT DATALOG_QUERY('?- child_count(Who, Count).');

Supported aggregates: count, sum, min, max.

How It Works

  • Semi-naive evaluation — Only new facts from the previous iteration are used to derive new facts, avoiding redundant computation
  • Indexed EDB — O(1) fact lookups for efficient joins
  • Stratified negation — Safe handling of negated predicates
  • WAL-backed — Crash-safe fact persistence

Use Cases

  • Access control — Recursive permission inheritance
  • Graph analytics — Transitive closure, reachability
  • Data lineage — Track data provenance and dependencies
  • Rule engines — Business rules and compliance checks
  • Ontologies — Taxonomic reasoning and classification