August 14, 2025

код для supa

-- ===========================
-- 1) Расширения
-- ===========================
create extension if not exists vector;
create extension if not exists pg_trgm;
create extension if not exists pgcrypto;

-- ===========================
-- 2) Таблица документов
-- ===========================
create table if not exists public.documents (
  id         uuid primary key default gen_random_uuid(),
  content    text not null,
  embedding  vector(1536) not null,
  metadata   jsonb not null default '{}'::jsonb,
  created_at timestamptz not null default now()
);

-- ===========================
-- 3) Индексы
-- (без CONCURRENTLY — Supabase выполняет скрипт в транзакции)
-- ===========================
create index if not exists documents_embedding_ivfflat
  on public.documents using ivfflat (embedding vector_cosine_ops) with (lists = 100);

create index if not exists documents_content_trgm
  on public.documents using gin (content gin_trgm_ops);

create index if not exists documents_metadata_gin
  on public.documents using gin (metadata);

analyze public.documents;

-- Опционально: сколько "корзин" просматривать при ivfflat
-- (можно варьировать 1..lists; больше = медленнее, но точнее)
-- set ivfflat.probes = 10;

-- ===========================
-- 4) Функции поиска (с уникальными именами, чтобы не конфликтовать)
-- ===========================

-- 4.1 Векторный поиск (cosine)
create or replace function public.search_documents_v1(
  query_embedding vector(1536),
  match_count int default 10,
  min_content_length int default 1
)
returns table (
  id uuid,
  content text,
  metadata jsonb,
  similarity float
)
language sql
stable
parallel safe
as $
  select d.id,
         d.content,
         d.metadata,
         1 - (d.embedding <=> query_embedding) as similarity
  from public.documents d
  where length(d.content) >= min_content_length
  order by d.embedding <=> query_embedding
  limit match_count
$;

-- 4.2 Гибридный поиск (лексика + вектор)
create or replace function public.search_documents_hybrid_v1(
  query_text text,
  query_embedding vector(1536),
  match_count int default 10
)
returns table (
  id uuid,
  content text,
  metadata jsonb,
  similarity float
)
language sql
stable
parallel safe
as $
  select d.id,
         d.content,
         d.metadata,
         1 - (d.embedding <=> query_embedding) as similarity
  from public.documents d
  where
    -- триграммы
    d.content ilike '%' || query_text || '%'
    -- и/или полнотекст (русский словарь можно заменить на 'simple', если надо)
    or to_tsvector('russian', d.content) @@ plainto_tsquery('russian', query_text)
  order by d.embedding <=> query_embedding
  limit match_count
$;

-- ===========================
-- Готово.
-- Вставлять данные: INSERT INTO public.documents(content, metadata, embedding) VALUES (...)
-- И вызывать поиск:
--   select * from public.search_documents_v1($1::vector(1536), 10);
--   select * from public.search_documents_hybrid_v1('дурак', $2::vector(1536), 10);
-- ===========================

[