SQLite Storage

May 8, 2025 ยท View on GitHub

SQLite storage provides a more efficient database backend for Memory Graph MCP, using a single SQLite file to store all memory data.

Overview

SQLite storage offers several advantages over JSON storage:

  • Better performance for large memory sets
  • Full-text search capabilities
  • More efficient memory usage
  • Single file for all domains
  • Supports complex queries and filters

Database Structure

When using SQLite storage, the following database tables are created:

DOMAINS           # List of all domains and metadata
PERSISTENCE       # Session state tracking
MEMORY_NODES      # Memory node data
MEMORY_TAGS       # Many-to-many relationship for tags
MEMORY_EDGES      # Relationships between memories
DOMAIN_REFS       # Cross-domain references
memory_content_fts  # Full-text search virtual table

Table Schemas

DOMAINS

CREATE TABLE DOMAINS (
    id TEXT PRIMARY KEY,              -- Unique domain identifier
    name TEXT NOT NULL,               -- Human-readable name
    description TEXT,                 -- Purpose/scope of the domain
    created TEXT NOT NULL,            -- ISO timestamp
    lastAccess TEXT NOT NULL          -- ISO timestamp
);

PERSISTENCE

CREATE TABLE PERSISTENCE (
    id INTEGER PRIMARY KEY CHECK (id = 1), -- Enforces single row
    currentDomain TEXT NOT NULL,           -- Currently active domain
    lastAccess TEXT NOT NULL,              -- ISO timestamp
    lastMemoryId TEXT,                     -- Most recently created memory (optional)
    FOREIGN KEY (currentDomain) REFERENCES DOMAINS(id)
);

MEMORY_NODES

CREATE TABLE MEMORY_NODES (
    id TEXT PRIMARY KEY,          -- Unique memory identifier
    domain TEXT NOT NULL,         -- Domain this memory belongs to
    content TEXT NOT NULL,        -- Main memory content
    timestamp TEXT NOT NULL,      -- ISO timestamp of creation
    path TEXT DEFAULT '/',        -- Organizational path
    content_summary TEXT,         -- Optional summary of the content
    summary_timestamp TEXT,       -- When the summary was last updated
    FOREIGN KEY (domain) REFERENCES DOMAINS(id)
);

MEMORY_TAGS

CREATE TABLE MEMORY_TAGS (
    nodeId TEXT NOT NULL,         -- Memory node ID
    tag TEXT NOT NULL,            -- Tag value
    PRIMARY KEY (nodeId, tag),    -- Prevents duplicate tags
    FOREIGN KEY (nodeId) REFERENCES MEMORY_NODES(id) ON DELETE CASCADE
);

MEMORY_EDGES

CREATE TABLE MEMORY_EDGES (
    id TEXT PRIMARY KEY,          -- Composite edge ID (source-target-type)
    source TEXT NOT NULL,         -- Source memory node ID
    target TEXT NOT NULL,         -- Target memory node ID
    type TEXT NOT NULL,           -- Relationship type
    strength REAL NOT NULL CHECK (strength >= 0 AND strength <= 1), -- Relationship strength (0-1)
    timestamp TEXT NOT NULL,      -- ISO timestamp
    domain TEXT NOT NULL,         -- Domain this edge belongs to
    FOREIGN KEY (source) REFERENCES MEMORY_NODES(id) ON DELETE CASCADE,
    FOREIGN KEY (target) REFERENCES MEMORY_NODES(id) ON DELETE CASCADE,
    FOREIGN KEY (domain) REFERENCES DOMAINS(id)
);

DOMAIN_REFS

CREATE TABLE DOMAIN_REFS (
    nodeId TEXT NOT NULL,         -- Source memory node ID
    domain TEXT NOT NULL,         -- Source domain
    targetDomain TEXT NOT NULL,   -- Target domain
    targetNodeId TEXT NOT NULL,   -- Target memory node ID
    description TEXT,             -- Optional reference description
    bidirectional INTEGER NOT NULL DEFAULT 0, -- 0=one-way, 1=bidirectional
    PRIMARY KEY (nodeId, targetDomain, targetNodeId), -- Prevents duplicates
    FOREIGN KEY (nodeId) REFERENCES MEMORY_NODES(id) ON DELETE CASCADE,
    FOREIGN KEY (domain) REFERENCES DOMAINS(id),
    FOREIGN KEY (targetDomain) REFERENCES DOMAINS(id)
);

SQLite storage uses the FTS5 extension for full-text search capabilities:

-- Full-text search virtual table
CREATE VIRTUAL TABLE memory_content_fts USING fts5(
    id,              -- Memory ID
    content,         -- Memory content
    content_summary, -- Memory summary
    path,            -- Organization path
    tags,            -- Concatenated tags for searching
    domain,          -- Domain ID
    tokenize="porter unicode61"  -- Use Porter stemming algorithm
);

Triggers keep the FTS index in sync with changes to the main tables:

-- Insert trigger for memory nodes
CREATE TRIGGER memory_nodes_ai AFTER INSERT ON MEMORY_NODES BEGIN
    INSERT INTO memory_content_fts(id, content, content_summary, path, domain)
    VALUES (new.id, new.content, new.content_summary, new.path, new.domain);
END;

-- Update trigger for memory nodes
CREATE TRIGGER memory_nodes_au AFTER UPDATE ON MEMORY_NODES BEGIN
    DELETE FROM memory_content_fts WHERE id = old.id;
    INSERT INTO memory_content_fts(id, content, content_summary, path, domain)
    VALUES (new.id, new.content, new.content_summary, new.path, new.domain);
END;

-- Delete trigger for memory nodes
CREATE TRIGGER memory_nodes_ad AFTER DELETE ON MEMORY_NODES BEGIN
    DELETE FROM memory_content_fts WHERE id = old.id;
END;

-- Insert trigger for memory tags
CREATE TRIGGER memory_tags_ai AFTER INSERT ON MEMORY_TAGS BEGIN
    UPDATE memory_content_fts 
    SET tags = (SELECT group_concat(tag, ' ') FROM MEMORY_TAGS WHERE nodeId = new.nodeId)
    WHERE id = new.nodeId;
END;

-- Delete trigger for memory tags
CREATE TRIGGER memory_tags_ad AFTER DELETE ON MEMORY_TAGS BEGIN
    UPDATE memory_content_fts 
    SET tags = (SELECT group_concat(tag, ' ') FROM MEMORY_TAGS WHERE nodeId = old.nodeId)
    WHERE id = old.nodeId;
END;

Configuration

To use SQLite storage, set the STORAGE_TYPE environment variable:

STORAGE_TYPE=sqlite

Additional configuration options:

# Directory where the SQLite database will be stored
MEMORY_DIR=/path/to/memory/directory

The database file will be created at:

/path/to/memory/directory/memory-graph.db

Advantages

  • Performance: Better query performance than JSON storage
  • Full-Text Search: Advanced search capabilities using FTS5
  • Memory Efficiency: Loads only necessary data, not entire domains
  • Single File: All domains in one database file
  • Transactions: ACID compliance for data integrity
  • Indexes: Optimized data access with database indexes

Limitations

  • Inspection: Requires SQL tools to inspect data
  • Concurrency: Limited concurrent write operations
  • Deployment: Not ideal for high-load production environments
  • Dependencies: Requires SQLite compilation with FTS5 support

When to Use SQLite Storage

SQLite storage is ideal for:

  • Medium-sized deployments
  • Personal or single-user applications
  • Systems with lots of memory nodes
  • Use cases requiring full-text search
  • Local applications without database server

Implementation Details

The SQLite storage implementation is handled by the SqliteMemoryStorage class. Key operations:

  • Database Initialization: Tables, indexes, and triggers are created automatically
  • Transactions: Used for multi-step operations for data integrity
  • Query Building: Parameterized queries prevent SQL injection
  • Connection Management: Single connection with proper closing

Full-Text Search Features

The SQLite FTS5 implementation provides:

  • Word Stemming: "running" matches "run", etc.
  • Unicode Support: Proper handling of international text
  • Phrase Searches: Match exact phrases in content
  • Relevance Ranking: Results ordered by relevance
  • Content + Tags: Search both content and tags

Example search query:

SELECT m.* FROM MEMORY_NODES m
JOIN memory_content_fts fts ON m.id = fts.id
WHERE memory_content_fts MATCH ?
AND m.domain = ?
ORDER BY rank
LIMIT ?

Testing with SQLite Storage

For testing with SQLite storage:

// In your test setup
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'memory-test-'));
const graph = new MemoryGraph({ 
  storageDir: tempDir, 
  storageType: 'sqlite' 
});
await graph.initialize();

// Run your tests...

// Cleanup
await fs.rm(tempDir, { recursive: true, force: true });

Converting To/From SQLite Storage

To convert between storage types:

# Convert from JSON to SQLite
npx ts-node scripts/convert-storage.ts json2sqlite /path/to/json/data /path/to/sqlite/file.db

# Convert from SQLite to JSON
npx ts-node scripts/convert-storage.ts sqlite2json /path/to/sqlite/file.db /path/to/json/data

# Convert from SQLite to MariaDB
npx ts-node scripts/convert-storage.ts sqlite2mariadb /path/to/sqlite/file.db "mariadb://user:password@localhost:3306/memory_graph"

For more details on conversion, see Converting Between Storage Types.