-- hybrid_search.sql
-- Hybrid keyword + semantic search using pg_trgm + pgvector RRF
-- 
-- Usage:
--   SELECT * FROM hybrid_search('braid eigensolid convergence', 
--     (SELECT embedding FROM arxiv_papers WHERE paper_id = 'some_id'), 10);

CREATE OR REPLACE FUNCTION hybrid_search(
    query_text TEXT,
    query_embedding vector(1024),
    top_k INT DEFAULT 10
) RETURNS TABLE (
    paper_id TEXT,
    title TEXT,
    trigram_rank BIGINT,
    vector_rank BIGINT,
    rrf_score DOUBLE PRECISION
) AS $$
WITH trigram_candidates AS (
    SELECT p.paper_id, p.title,
           ROW_NUMBER() OVER (ORDER BY similarity(p.title, query_text) DESC) AS trigram_rank
    FROM arxiv_papers p
    WHERE p.title % query_text
    LIMIT 50
),
vector_candidates AS (
    SELECT p.paper_id, p.title,
           ROW_NUMBER() OVER (ORDER BY p.embedding <=> query_embedding) AS vector_rank
    FROM arxiv_papers p
    WHERE p.embedding IS NOT NULL
    ORDER BY p.embedding <=> query_embedding
    LIMIT 50
)
SELECT 
    COALESCE(t.paper_id, v.paper_id)::TEXT AS paper_id,
    COALESCE(t.title, v.title)::TEXT AS title,
    t.trigram_rank,
    v.vector_rank,
    (COALESCE(1.0 / (60 + t.trigram_rank), 0) +
     COALESCE(1.0 / (60 + v.vector_rank), 0))::DOUBLE PRECISION AS rrf_score
FROM trigram_candidates t
FULL OUTER JOIN vector_candidates v ON t.paper_id = v.paper_id
ORDER BY rrf_score DESC
LIMIT top_k;
$$ LANGUAGE sql STABLE;

-- Variant: embed query text inline (for when we don't have a pre-computed embedding)
-- This uses a placeholder — actual embedding must be computed in Python
CREATE OR REPLACE FUNCTION hybrid_search_text(
    query_text TEXT,
    top_k INT DEFAULT 10
) RETURNS TABLE (
    paper_id TEXT,
    title TEXT,
    trigram_rank BIGINT,
    rrf_score DOUBLE PRECISION
) AS $$
SELECT 
    p.paper_id::TEXT,
    p.title::TEXT,
    ROW_NUMBER() OVER (ORDER BY similarity(p.title, query_text) DESC) AS trigram_rank,
    similarity(p.title, query_text)::DOUBLE PRECISION AS rrf_score
FROM arxiv_papers p
WHERE p.title % query_text
ORDER BY similarity(p.title, query_text) DESC
LIMIT top_k;
$$ LANGUAGE sql STABLE;
