282 lines
7.3 KiB
PL/PgSQL
282 lines
7.3 KiB
PL/PgSQL
-- 说明:为自动入库(Supabase -> LightRAG)提供队列表与触发器/函数。
|
|
-- 编码:UTF-8
|
|
|
|
create table public.rag_index_sources (
|
|
id uuid primary key default gen_random_uuid(),
|
|
workspace_id uuid not null references public.workspaces(id) on delete cascade,
|
|
source_type text not null,
|
|
source_id uuid not null,
|
|
document_id uuid references public.documents(id) on delete set null,
|
|
user_id uuid references public.profiles(id) on delete set null,
|
|
status text not null default 'pending',
|
|
attempts integer not null default 0,
|
|
last_error text,
|
|
source_updated_at timestamp with time zone,
|
|
last_enqueued_at timestamp with time zone not null default timezone('utc', now()),
|
|
last_processed_at timestamp with time zone,
|
|
created_at timestamp with time zone not null default timezone('utc', now()),
|
|
updated_at timestamp with time zone not null default timezone('utc', now()),
|
|
constraint rag_index_sources_source_type_check check (
|
|
source_type = any (array['document'::text, 'mindmap'::text, 'media_asset'::text])
|
|
),
|
|
constraint rag_index_sources_status_check check (
|
|
status = any (array['pending'::text, 'processing'::text, 'completed'::text, 'failed'::text, 'skipped'::text])
|
|
)
|
|
);
|
|
|
|
create unique index rag_index_sources_unique_source
|
|
on public.rag_index_sources (source_type, source_id);
|
|
|
|
create index rag_index_sources_status_idx
|
|
on public.rag_index_sources (status, last_enqueued_at);
|
|
|
|
create index rag_index_sources_workspace_status_idx
|
|
on public.rag_index_sources (workspace_id, status, last_enqueued_at desc);
|
|
|
|
alter table public.rag_index_sources enable row level security;
|
|
|
|
create policy "Select rag index within workspace"
|
|
on public.rag_index_sources
|
|
as permissive
|
|
for select
|
|
to public
|
|
using (
|
|
exists (
|
|
select 1
|
|
from public.workspace_members wm
|
|
where wm.workspace_id = rag_index_sources.workspace_id
|
|
and wm.user_id = uid()
|
|
)
|
|
);
|
|
|
|
create or replace function public.set_rag_index_sources_updated_at()
|
|
returns trigger
|
|
language plpgsql
|
|
as $function$
|
|
begin
|
|
new.updated_at := timezone('utc', now());
|
|
return new;
|
|
end;
|
|
$function$;
|
|
|
|
create trigger set_rag_index_sources_updated_at
|
|
before update on public.rag_index_sources
|
|
for each row execute function public.set_rag_index_sources_updated_at();
|
|
|
|
create or replace function public.upsert_rag_index_source(
|
|
p_workspace_id uuid,
|
|
p_source_type text,
|
|
p_source_id uuid,
|
|
p_document_id uuid,
|
|
p_user_id uuid,
|
|
p_source_updated_at timestamp with time zone
|
|
)
|
|
returns void
|
|
language plpgsql
|
|
security definer
|
|
set search_path to 'public'
|
|
as $function$
|
|
begin
|
|
insert into public.rag_index_sources (
|
|
workspace_id,
|
|
source_type,
|
|
source_id,
|
|
document_id,
|
|
user_id,
|
|
status,
|
|
source_updated_at,
|
|
last_enqueued_at,
|
|
last_error
|
|
)
|
|
values (
|
|
p_workspace_id,
|
|
p_source_type,
|
|
p_source_id,
|
|
p_document_id,
|
|
p_user_id,
|
|
'pending',
|
|
p_source_updated_at,
|
|
timezone('utc', now()),
|
|
null
|
|
)
|
|
on conflict (source_type, source_id)
|
|
do update set
|
|
workspace_id = excluded.workspace_id,
|
|
document_id = excluded.document_id,
|
|
user_id = excluded.user_id,
|
|
status = 'pending',
|
|
source_updated_at = excluded.source_updated_at,
|
|
last_enqueued_at = timezone('utc', now()),
|
|
last_error = null,
|
|
updated_at = timezone('utc', now());
|
|
end;
|
|
$function$;
|
|
|
|
create or replace function public.enqueue_rag_for_document()
|
|
returns trigger
|
|
language plpgsql
|
|
as $function$
|
|
begin
|
|
perform public.upsert_rag_index_source(
|
|
new.workspace_id,
|
|
'document',
|
|
new.id,
|
|
new.id,
|
|
new.user_id,
|
|
new.updated_at
|
|
);
|
|
return new;
|
|
end;
|
|
$function$;
|
|
|
|
create or replace function public.enqueue_rag_for_media_asset()
|
|
returns trigger
|
|
language plpgsql
|
|
as $function$
|
|
declare
|
|
v_user_id uuid;
|
|
begin
|
|
select user_id
|
|
into v_user_id
|
|
from public.documents
|
|
where id = new.document_id;
|
|
|
|
perform public.upsert_rag_index_source(
|
|
new.workspace_id,
|
|
'media_asset',
|
|
new.id,
|
|
new.document_id,
|
|
coalesce(new.created_by, v_user_id),
|
|
new.updated_at
|
|
);
|
|
return new;
|
|
end;
|
|
$function$;
|
|
|
|
create or replace function public.enqueue_rag_for_mindmap_node()
|
|
returns trigger
|
|
language plpgsql
|
|
as $function$
|
|
declare
|
|
v_user_id uuid;
|
|
begin
|
|
select user_id
|
|
into v_user_id
|
|
from public.documents
|
|
where id = new.document_id;
|
|
|
|
perform public.upsert_rag_index_source(
|
|
new.workspace_id,
|
|
'mindmap',
|
|
new.mindmap_id,
|
|
new.document_id,
|
|
v_user_id,
|
|
new.updated_at
|
|
);
|
|
return new;
|
|
end;
|
|
$function$;
|
|
|
|
create trigger enqueue_rag_for_document_insert
|
|
after insert on public.documents
|
|
for each row execute function public.enqueue_rag_for_document();
|
|
|
|
create trigger enqueue_rag_for_document_update
|
|
after update on public.documents
|
|
for each row
|
|
when (
|
|
old.title is distinct from new.title
|
|
or old.content is distinct from new.content
|
|
or old.raw_text is distinct from new.raw_text
|
|
or old.mindmap_data is distinct from new.mindmap_data
|
|
or old.deleted_at is distinct from new.deleted_at
|
|
)
|
|
execute function public.enqueue_rag_for_document();
|
|
|
|
create trigger enqueue_rag_for_media_asset_insert
|
|
after insert on public.media_assets
|
|
for each row execute function public.enqueue_rag_for_media_asset();
|
|
|
|
create trigger enqueue_rag_for_media_asset_update
|
|
after update on public.media_assets
|
|
for each row
|
|
when (
|
|
old.file_url is distinct from new.file_url
|
|
or old.storage_path is distinct from new.storage_path
|
|
or old.bucket is distinct from new.bucket
|
|
or old.ocr_text is distinct from new.ocr_text
|
|
or old.ocr_status is distinct from new.ocr_status
|
|
or old.mime_type is distinct from new.mime_type
|
|
)
|
|
execute function public.enqueue_rag_for_media_asset();
|
|
|
|
create trigger enqueue_rag_for_mindmap_node_insert
|
|
after insert on public.mindmap_nodes
|
|
for each row execute function public.enqueue_rag_for_mindmap_node();
|
|
|
|
create trigger enqueue_rag_for_mindmap_node_update
|
|
after update on public.mindmap_nodes
|
|
for each row
|
|
when (
|
|
old.data is distinct from new.data
|
|
or old.parent_id is distinct from new.parent_id
|
|
or old.block_id is distinct from new.block_id
|
|
or old.order_index is distinct from new.order_index
|
|
)
|
|
execute function public.enqueue_rag_for_mindmap_node();
|
|
|
|
create or replace function public.backfill_rag_index_sources()
|
|
returns integer
|
|
language plpgsql
|
|
security definer
|
|
set search_path to 'public'
|
|
as $function$
|
|
declare
|
|
v_count integer := 0;
|
|
begin
|
|
-- 仅允许 service_role 调用,避免被普通用户滥用造成全量重索引
|
|
if auth.role() <> 'service_role' then
|
|
raise exception 'forbidden';
|
|
end if;
|
|
|
|
insert into public.rag_index_sources (
|
|
workspace_id,
|
|
source_type,
|
|
source_id,
|
|
document_id,
|
|
user_id,
|
|
status,
|
|
source_updated_at,
|
|
last_enqueued_at,
|
|
last_error
|
|
)
|
|
select
|
|
d.workspace_id,
|
|
'document',
|
|
d.id,
|
|
d.id,
|
|
d.user_id,
|
|
'pending',
|
|
d.updated_at,
|
|
timezone('utc', now()),
|
|
null
|
|
from public.documents d
|
|
on conflict (source_type, source_id)
|
|
do update set
|
|
workspace_id = excluded.workspace_id,
|
|
document_id = excluded.document_id,
|
|
user_id = excluded.user_id,
|
|
status = 'pending',
|
|
source_updated_at = excluded.source_updated_at,
|
|
last_enqueued_at = timezone('utc', now()),
|
|
last_error = null,
|
|
updated_at = timezone('utc', now());
|
|
|
|
get diagnostics v_count = row_count;
|
|
return v_count;
|
|
end;
|
|
$function$;
|
|
|
|
grant execute on function public.backfill_rag_index_sources() to service_role;
|
|
|