Browse Source

fix(backend): 数据库初始化幂等自愈与 vector_store 物理删除

- DatabaseInitConfig 改为全幂等自愈(ADD COLUMN IF NOT EXISTS + 回填 + 索引/触发器幂等)
- HybridSearchService 移除对 vector_store 的 is_delete 过滤(该表无此列,物理删除)
Spring-AI-1.1.2
wanghanlin 2 weeks ago
parent
commit
562370ece5
  1. 19
      src/main/java/com/wok/supportbot/config/DatabaseInitConfig.java
  2. 4
      src/main/java/com/wok/supportbot/rag/HybridSearchService.java
  3. 6
      src/main/resources/init-database.sql

19
src/main/java/com/wok/supportbot/config/DatabaseInitConfig.java

@ -930,7 +930,9 @@ public class DatabaseInitConfig {
/**
* vector_store 表添加全文检索支持tsvector + GIN 索引 + 触发器
* 幂等操作已存在则跳过
* 全幂等自愈每一步都用幂等 SQLADD COLUMN IF NOT EXISTS / CREATE INDEX IF NOT EXISTS /
* CREATE OR REPLACE + DROP TRIGGER IF EXISTS不做列已存在就 return的早退
* 避免半途失败或触发器被手动删除后新文档 content_tsvector 恒为 NULL 的静默缺口
*/
private void initVectorStoreFullTextSearch() {
try {
@ -940,19 +942,12 @@ public class DatabaseInitConfig {
return;
}
// 检查 content_tsvector 列是否已存在
String checkSql = "SELECT COUNT(*) FROM information_schema.columns WHERE table_name = 'vector_store' AND column_name = 'content_tsvector'";
Integer count = jdbcTemplate.queryForObject(checkSql, Integer.class);
if (count != null && count > 0) {
return; // 已初始化
}
log.info("为 vector_store 添加全文检索支持");
log.info("初始化 vector_store 全文检索支持");
// 添加 tsvector
jdbcTemplate.execute("ALTER TABLE vector_store ADD COLUMN content_tsvector tsvector");
// 添加 tsvector 已存在则跳过
jdbcTemplate.execute("ALTER TABLE vector_store ADD COLUMN IF NOT EXISTS content_tsvector tsvector");
// 已有数据
// 回填已有数据幂等只补 NULL
jdbcTemplate.execute("UPDATE vector_store SET content_tsvector = to_tsvector('simple', coalesce(content, '')) WHERE content_tsvector IS NULL");
// GIN 索引

4
src/main/java/com/wok/supportbot/rag/HybridSearchService.java

@ -97,6 +97,9 @@ public class HybridSearchService {
* 全文关键词检索
* 使用 PostgreSQL tsvector 全文索引 ts_rank 排序
* P1-2.1: 始终过滤禁用的文档metadata->>'enabled' = 'true'
* 注意vector_store 为物理删除 is_delete 仅保证有
* id / content / metadata / embedding / content_tsvector / create_time 六列
* 禁止在原生 SQL 中引用 is_deleteupdate_time 等业务表字段
*/
private List<SearchResult> keywordSearch(String query, int topK, List<Long> categoryIds) {
String categoryFilter = buildCategoryFilter(categoryIds);
@ -105,7 +108,6 @@ public class HybridSearchService {
ts_rank(content_tsvector, plainto_tsquery('simple', ?)) AS rank
FROM vector_store
WHERE content_tsvector @@ plainto_tsquery('simple', ?)
AND is_delete = false
AND metadata->>'enabled' = 'true'
%s
ORDER BY rank DESC

6
src/main/resources/init-database.sql

@ -367,6 +367,12 @@ CREATE TABLE IF NOT EXISTS vector_store (
update_time TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);
-- ⚠️ 与 Spring AI 自动建表的差异(勿误判):
-- 1. Spring AI PgVectorStore 自动建表时 metadata 是 json(非 jsonb),且不含 create_time/update_time。
-- create_time/content_tsvector 由 DatabaseInitConfig 补加;update_time 仅本手动脚本含、无代码引用。
-- 2. vector_store 为物理删除(pgVectorVectorStore.delete),没有 is_delete 列。
-- 任何原生 SQL 禁止引用 is_delete / update_time,只允许 id/content/metadata/embedding/content_tsvector/create_time。
-- HNSW 向量索引(余弦距离)
CREATE INDEX IF NOT EXISTS idx_vector_store_embedding ON vector_store
USING hnsw (embedding vector_cosine_ops) WITH (m = 16, ef_construction = 64);

Loading…
Cancel
Save