migraciones para soportar el full text search
This commit is contained in:
@@ -1 +1 @@
|
|||||||
v2.78.1
|
v2.84.2
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
export type Json =
|
export type Json =
|
||||||
| string
|
| string
|
||||||
| number
|
| number
|
||||||
| boolean
|
| boolean
|
||||||
@@ -7,10 +7,30 @@ export type Json =
|
|||||||
| Json[]
|
| Json[]
|
||||||
|
|
||||||
export type Database = {
|
export type Database = {
|
||||||
// Allows to automatically instantiate createClient with right options
|
graphql_public: {
|
||||||
// instead of createClient<Database, { PostgrestVersion: 'XX' }>(URL, KEY)
|
Tables: {
|
||||||
__InternalSupabase: {
|
[_ in never]: never
|
||||||
PostgrestVersion: "14.1"
|
}
|
||||||
|
Views: {
|
||||||
|
[_ in never]: never
|
||||||
|
}
|
||||||
|
Functions: {
|
||||||
|
graphql: {
|
||||||
|
Args: {
|
||||||
|
extensions?: Json
|
||||||
|
operationName?: string
|
||||||
|
query?: string
|
||||||
|
variables?: Json
|
||||||
|
}
|
||||||
|
Returns: Json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Enums: {
|
||||||
|
[_ in never]: never
|
||||||
|
}
|
||||||
|
CompositeTypes: {
|
||||||
|
[_ in never]: never
|
||||||
|
}
|
||||||
}
|
}
|
||||||
public: {
|
public: {
|
||||||
Tables: {
|
Tables: {
|
||||||
@@ -135,6 +155,7 @@ export type Database = {
|
|||||||
orden_celda: number | null
|
orden_celda: number | null
|
||||||
plan_estudio_id: string
|
plan_estudio_id: string
|
||||||
prerrequisito_asignatura_id: string | null
|
prerrequisito_asignatura_id: string | null
|
||||||
|
search_vector: unknown
|
||||||
tipo: Database["public"]["Enums"]["tipo_asignatura"]
|
tipo: Database["public"]["Enums"]["tipo_asignatura"]
|
||||||
tipo_origen: Database["public"]["Enums"]["tipo_origen"] | null
|
tipo_origen: Database["public"]["Enums"]["tipo_origen"] | null
|
||||||
}
|
}
|
||||||
@@ -161,6 +182,7 @@ export type Database = {
|
|||||||
orden_celda?: number | null
|
orden_celda?: number | null
|
||||||
plan_estudio_id: string
|
plan_estudio_id: string
|
||||||
prerrequisito_asignatura_id?: string | null
|
prerrequisito_asignatura_id?: string | null
|
||||||
|
search_vector?: unknown
|
||||||
tipo?: Database["public"]["Enums"]["tipo_asignatura"]
|
tipo?: Database["public"]["Enums"]["tipo_asignatura"]
|
||||||
tipo_origen?: Database["public"]["Enums"]["tipo_origen"] | null
|
tipo_origen?: Database["public"]["Enums"]["tipo_origen"] | null
|
||||||
}
|
}
|
||||||
@@ -187,6 +209,7 @@ export type Database = {
|
|||||||
orden_celda?: number | null
|
orden_celda?: number | null
|
||||||
plan_estudio_id?: string
|
plan_estudio_id?: string
|
||||||
prerrequisito_asignatura_id?: string | null
|
prerrequisito_asignatura_id?: string | null
|
||||||
|
search_vector?: unknown
|
||||||
tipo?: Database["public"]["Enums"]["tipo_asignatura"]
|
tipo?: Database["public"]["Enums"]["tipo_asignatura"]
|
||||||
tipo_origen?: Database["public"]["Enums"]["tipo_origen"] | null
|
tipo_origen?: Database["public"]["Enums"]["tipo_origen"] | null
|
||||||
}
|
}
|
||||||
@@ -1373,6 +1396,35 @@ export type Database = {
|
|||||||
Args: { p_append: Json; p_id: string }
|
Args: { p_append: Json; p_id: string }
|
||||||
Returns: undefined
|
Returns: undefined
|
||||||
}
|
}
|
||||||
|
build_asignaturas_prefix_tsquery: {
|
||||||
|
Args: { p_search: string }
|
||||||
|
Returns: unknown
|
||||||
|
}
|
||||||
|
recalcular_vectores_asignaturas: { Args: never; Returns: undefined }
|
||||||
|
search_asignaturas: {
|
||||||
|
Args: {
|
||||||
|
p_carrera_id?: string
|
||||||
|
p_facultad_id?: string
|
||||||
|
p_limit?: number
|
||||||
|
p_offset?: number
|
||||||
|
p_plan_estudio_id?: string
|
||||||
|
p_search?: string
|
||||||
|
}
|
||||||
|
Returns: {
|
||||||
|
codigo: string
|
||||||
|
contenido_tematico: Json
|
||||||
|
creditos: number
|
||||||
|
datos: Json
|
||||||
|
estado: Database["public"]["Enums"]["estado_asignatura"]
|
||||||
|
id: string
|
||||||
|
nombre: string
|
||||||
|
numero_ciclo: number
|
||||||
|
plan_estudio_id: string
|
||||||
|
rank: number
|
||||||
|
tipo: Database["public"]["Enums"]["tipo_asignatura"]
|
||||||
|
total_count: number
|
||||||
|
}[]
|
||||||
|
}
|
||||||
suma_porcentajes: { Args: { "": Json }; Returns: number }
|
suma_porcentajes: { Args: { "": Json }; Returns: number }
|
||||||
unaccent: { Args: { "": string }; Returns: string }
|
unaccent: { Args: { "": string }; Returns: string }
|
||||||
unaccent_immutable: { Args: { "": string }; Returns: string }
|
unaccent_immutable: { Args: { "": string }; Returns: string }
|
||||||
@@ -1548,6 +1600,9 @@ export type CompositeTypes<
|
|||||||
: never
|
: never
|
||||||
|
|
||||||
export const Constants = {
|
export const Constants = {
|
||||||
|
graphql_public: {
|
||||||
|
Enums: {},
|
||||||
|
},
|
||||||
public: {
|
public: {
|
||||||
Enums: {
|
Enums: {
|
||||||
estado_asignatura: ["borrador", "revisada", "aprobada", "generando"],
|
estado_asignatura: ["borrador", "revisada", "aprobada", "generando"],
|
||||||
@@ -1607,3 +1662,4 @@ export const Constants = {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const
|
} as const
|
||||||
|
|
||||||
|
|||||||
@@ -52,27 +52,18 @@ for each row
|
|||||||
execute function public.fn_asignaturas_update_search_vector();
|
execute function public.fn_asignaturas_update_search_vector();
|
||||||
|
|
||||||
-- Paso 5
|
-- Paso 5
|
||||||
update public.asignaturas
|
create or replace function public.recalcular_vectores_asignaturas()
|
||||||
set search_vector =
|
returns void
|
||||||
setweight(
|
language sql
|
||||||
to_tsvector('public.es_simple_unaccent', coalesce(nombre, '')),
|
as $$
|
||||||
'A'
|
UPDATE public.asignaturas
|
||||||
)
|
SET search_vector =
|
||||||
||
|
setweight(to_tsvector('public.es_simple_unaccent', coalesce(nombre, '')), 'A') ||
|
||||||
setweight(
|
setweight(to_tsvector('public.es_simple_unaccent', coalesce(codigo, '')), 'A') ||
|
||||||
to_tsvector('public.es_simple_unaccent', coalesce(codigo, '')),
|
setweight(to_tsvector('public.es_simple_unaccent', coalesce(datos, '{}'::jsonb)::text), 'B') ||
|
||||||
'A'
|
setweight(to_tsvector('public.es_simple_unaccent', coalesce(contenido_tematico, '[]'::jsonb)::text), 'B')
|
||||||
)
|
WHERE id IS NOT NULL;
|
||||||
||
|
$$;
|
||||||
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
|
-- Paso 6
|
||||||
create index if not exists asignaturas_search_vector_gin_idx
|
create index if not exists asignaturas_search_vector_gin_idx
|
||||||
@@ -117,7 +108,9 @@ $$;
|
|||||||
|
|
||||||
-- Paso 8
|
-- Paso 8
|
||||||
create or replace function public.search_asignaturas(
|
create or replace function public.search_asignaturas(
|
||||||
p_search text,
|
p_search text default '',
|
||||||
|
p_facultad_id uuid default null,
|
||||||
|
p_carrera_id uuid default null,
|
||||||
p_plan_estudio_id uuid default null,
|
p_plan_estudio_id uuid default null,
|
||||||
p_limit integer default 20,
|
p_limit integer default 20,
|
||||||
p_offset integer default 0
|
p_offset integer default 0
|
||||||
@@ -133,14 +126,19 @@ returns table (
|
|||||||
datos jsonb,
|
datos jsonb,
|
||||||
contenido_tematico jsonb,
|
contenido_tematico jsonb,
|
||||||
estado public.estado_asignatura,
|
estado public.estado_asignatura,
|
||||||
rank real
|
rank real,
|
||||||
|
total_count bigint -- 👈 Total para la paginación del frontend
|
||||||
)
|
)
|
||||||
language sql
|
language plpgsql
|
||||||
stable
|
stable
|
||||||
as $$
|
as $$
|
||||||
with q as (
|
declare
|
||||||
select public.build_asignaturas_prefix_tsquery(p_search) as tsq
|
v_tsq tsquery;
|
||||||
)
|
begin
|
||||||
|
-- 1. Construimos el query solo si hay texto
|
||||||
|
v_tsq := public.build_asignaturas_prefix_tsquery(p_search);
|
||||||
|
|
||||||
|
return query
|
||||||
select
|
select
|
||||||
a.id,
|
a.id,
|
||||||
a.plan_estudio_id,
|
a.plan_estudio_id,
|
||||||
@@ -152,16 +150,34 @@ as $$
|
|||||||
a.datos,
|
a.datos,
|
||||||
a.contenido_tematico,
|
a.contenido_tematico,
|
||||||
a.estado,
|
a.estado,
|
||||||
ts_rank(a.search_vector, q.tsq) as rank
|
coalesce(ts_rank(a.search_vector, v_tsq), 0)::real as rank,
|
||||||
|
count(*) OVER() as total_count -- 👈 Cuenta total ignorando el LIMIT
|
||||||
from public.asignaturas a
|
from public.asignaturas a
|
||||||
cross join q
|
-- 2. JOINS para poder filtrar por la jerarquía superior
|
||||||
|
left join public.planes_estudio p on a.plan_estudio_id = p.id
|
||||||
|
left join public.carreras c on p.carrera_id = c.id
|
||||||
where
|
where
|
||||||
q.tsq is not null
|
-- 3. Si no hay búsqueda, trae todo. Si hay búsqueda, usa el FTS
|
||||||
and a.search_vector @@ q.tsq
|
(v_tsq is null or a.search_vector @@ v_tsq)
|
||||||
|
-- 4. Filtros jerárquicos dinámicos
|
||||||
and (p_plan_estudio_id is null or a.plan_estudio_id = p_plan_estudio_id)
|
and (p_plan_estudio_id is null or a.plan_estudio_id = p_plan_estudio_id)
|
||||||
|
and (p_carrera_id is null or p.carrera_id = p_carrera_id)
|
||||||
|
and (p_facultad_id is null or c.facultad_id = p_facultad_id)
|
||||||
order by
|
order by
|
||||||
rank desc,
|
(case when v_tsq is not null then ts_rank(a.search_vector, v_tsq) else 0 end) desc,
|
||||||
a.nombre asc
|
a.nombre asc
|
||||||
limit p_limit
|
limit p_limit
|
||||||
offset p_offset;
|
offset p_offset;
|
||||||
$$;
|
end;
|
||||||
|
$$;
|
||||||
|
|
||||||
|
-- Corrección de bug con triggers
|
||||||
|
-- 1. Borramos la llave foránea estricta actual
|
||||||
|
ALTER TABLE public.cambios_asignatura
|
||||||
|
DROP CONSTRAINT cambios_asignatura_asignatura_id_fkey;
|
||||||
|
|
||||||
|
-- 2. La volvemos a crear, pero le decimos que espere al final de la transacción para validar
|
||||||
|
ALTER TABLE public.cambios_asignatura
|
||||||
|
ADD CONSTRAINT cambios_asignatura_asignatura_id_fkey
|
||||||
|
FOREIGN KEY (asignatura_id) REFERENCES public.asignaturas(id)
|
||||||
|
DEFERRABLE INITIALLY DEFERRED;
|
||||||
Reference in New Issue
Block a user