Files
open-brain/sql/schema.sql
YOUNG e90ea1591b Initial project scaffold: Open Brain self-hosted knowledge infrastructure
Node.js/TypeScript server with MCP endpoint for AI assistant integration,
Discord bot for thought capture and slash commands, PostgreSQL + pgvector
for semantic search, and OpenRouter for embeddings/metadata extraction.
Dockerized for deployment behind Caddy.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-06 14:53:41 -06:00

59 lines
1.7 KiB
PL/PgSQL

CREATE EXTENSION IF NOT EXISTS vector;
CREATE TABLE IF NOT EXISTS thoughts (
id UUID DEFAULT gen_random_uuid() PRIMARY KEY,
user_id TEXT NOT NULL DEFAULT 'default',
content TEXT NOT NULL,
embedding VECTOR(1536),
metadata JSONB DEFAULT '{}'::jsonb,
source TEXT NOT NULL DEFAULT 'mcp',
created_at TIMESTAMPTZ DEFAULT now(),
updated_at TIMESTAMPTZ DEFAULT now()
);
CREATE INDEX IF NOT EXISTS thoughts_embedding_idx ON thoughts USING hnsw (embedding vector_cosine_ops);
CREATE INDEX IF NOT EXISTS thoughts_metadata_idx ON thoughts USING gin (metadata);
CREATE INDEX IF NOT EXISTS thoughts_created_at_idx ON thoughts (created_at DESC);
CREATE INDEX IF NOT EXISTS thoughts_user_id_idx ON thoughts (user_id);
-- Auto-update trigger for updated_at
CREATE OR REPLACE FUNCTION update_updated_at() RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = now();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
DROP TRIGGER IF EXISTS thoughts_updated_at ON thoughts;
CREATE TRIGGER thoughts_updated_at
BEFORE UPDATE ON thoughts
FOR EACH ROW EXECUTE FUNCTION update_updated_at();
-- Semantic search function
CREATE OR REPLACE FUNCTION match_thoughts(
query_embedding VECTOR(1536),
match_threshold FLOAT DEFAULT 0.7,
match_count INT DEFAULT 10,
filter JSONB DEFAULT '{}'::jsonb
) RETURNS TABLE (
id UUID,
content TEXT,
metadata JSONB,
similarity FLOAT,
created_at TIMESTAMPTZ
) LANGUAGE plpgsql AS $$
BEGIN
RETURN QUERY
SELECT
t.id,
t.content,
t.metadata,
(1 - (t.embedding <=> query_embedding))::FLOAT AS similarity,
t.created_at
FROM thoughts t
WHERE 1 - (t.embedding <=> query_embedding) > match_threshold
ORDER BY t.embedding <=> query_embedding
LIMIT match_count;
END;
$$;