From 220b830aa87c1b1cfa99fed4220e7f266a1b2006 Mon Sep 17 00:00:00 2001 From: Guillermo Arrieta Medina Date: Tue, 24 Mar 2026 20:46:35 -0600 Subject: [PATCH] =?UTF-8?q?Full=20text=20search=20sobre=20asignaturas=20pa?= =?UTF-8?q?ra=20clonaci=C3=B3n=20de=20asignaturas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ...606_full_text_search_sobre_asignaturas.sql | 167 ++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 supabase/migrations/20260325024606_full_text_search_sobre_asignaturas.sql diff --git a/supabase/migrations/20260325024606_full_text_search_sobre_asignaturas.sql b/supabase/migrations/20260325024606_full_text_search_sobre_asignaturas.sql new file mode 100644 index 0000000..12638b9 --- /dev/null +++ b/supabase/migrations/20260325024606_full_text_search_sobre_asignaturas.sql @@ -0,0 +1,167 @@ +-- Paso 1 +drop text search configuration if exists public.es_simple_unaccent cascade; + +create text search configuration public.es_simple_unaccent (copy = pg_catalog.simple); + +alter text search configuration public.es_simple_unaccent + alter mapping for hword, hword_part, word + with unaccent, simple; + +-- Paso 2 +alter table public.asignaturas +add column if not exists search_vector tsvector; + +-- Paso 3 +create or replace function public.fn_asignaturas_update_search_vector() +returns trigger +language plpgsql +as $$ +begin + new.search_vector := + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(new.nombre, '')), + 'A' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(new.codigo, '')), + 'A' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(new.datos, '{}'::jsonb)::text), + 'B' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(new.contenido_tematico, '[]'::jsonb)::text), + 'B' + ); + + return new; +end; +$$; + +-- Paso 4 +drop trigger if exists trg_asignaturas_search_vector on public.asignaturas; + +create trigger trg_asignaturas_search_vector +before insert or update of nombre, codigo, datos, contenido_tematico +on public.asignaturas +for each row +execute function public.fn_asignaturas_update_search_vector(); + +-- Paso 5 +update public.asignaturas +set search_vector = + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(nombre, '')), + 'A' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(codigo, '')), + 'A' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(datos, '{}'::jsonb)::text), + 'B' + ) + || + setweight( + to_tsvector('public.es_simple_unaccent', coalesce(contenido_tematico, '[]'::jsonb)::text), + 'B' + ); + +-- Paso 6 +create index if not exists asignaturas_search_vector_gin_idx +on public.asignaturas +using gin (search_vector); + +-- Paso 7 +create or replace function public.build_asignaturas_prefix_tsquery(p_search text) +returns tsquery +language plpgsql +stable +as $$ +declare + cleaned text; + tokens text[]; + query_text text; +begin + cleaned := trim(coalesce(p_search, '')); + + if cleaned = '' then + return null; + end if; + + cleaned := lower(public.unaccent(cleaned)); + cleaned := regexp_replace(cleaned, '[^[:alnum:]\s]+', ' ', 'g'); + cleaned := regexp_replace(cleaned, '\s+', ' ', 'g'); + + tokens := regexp_split_to_array(cleaned, '\s+'); + + select string_agg(token || ':*', ' & ') + into query_text + from unnest(tokens) as token + where token <> ''; + + if query_text is null or query_text = '' then + return null; + end if; + + return to_tsquery('public.es_simple_unaccent', query_text); +end; +$$; + +-- Paso 8 +create or replace function public.search_asignaturas( + p_search text, + p_plan_estudio_id uuid default null, + p_limit integer default 20, + p_offset integer default 0 +) +returns table ( + id uuid, + plan_estudio_id uuid, + codigo text, + nombre text, + tipo public.tipo_asignatura, + creditos numeric, + numero_ciclo integer, + datos jsonb, + contenido_tematico jsonb, + estado public.estado_asignatura, + rank real +) +language sql +stable +as $$ + with q as ( + select public.build_asignaturas_prefix_tsquery(p_search) as tsq + ) + select + a.id, + a.plan_estudio_id, + a.codigo, + a.nombre, + a.tipo, + a.creditos, + a.numero_ciclo, + a.datos, + a.contenido_tematico, + a.estado, + ts_rank(a.search_vector, q.tsq) as rank + from public.asignaturas a + cross join q + where + q.tsq is not null + and a.search_vector @@ q.tsq + and (p_plan_estudio_id is null or a.plan_estudio_id = p_plan_estudio_id) + order by + rank desc, + a.nombre asc + limit p_limit + offset p_offset; +$$; \ No newline at end of file