Files
ai-contract-platform/project-docs/V2-database-search-document-tsvector-2026-06-24.md
T
yuqei a3bb78e06b
Security Quality Gate / npm audit, Bandit, pip-audit, detect-secrets (pull_request) Failing after 16m35s
release: freeze V2 release candidate
2026-07-12 15:32:24 +08:00

7.5 KiB
Raw Blame History

AI 合同平台 V2 数据库搜索专用表改造闭环记录

日期:2026-06-24

结论

本轮完成 DB-5 第三阶段:统一搜索专用表 / tsvector 搜索列改造。

目标没有跑偏:本轮只处理合同系统统一搜索的数据库性能瓶颈,不改变前端主流程,不把 AI 中台检索逻辑塞回合同系统。合同系统仍然只维护合同、附件、审查任务这些业务读模型。

已经解决:

  • 合同正文搜索不再直接跨 contracts.body_content 做多字段 OR + ILIKE。
  • 审查任务搜索不再直接 join review_tasks 和 contracts 后全表过滤。
  • /api/v1/search 前端接口保持不变,搜索结果 URL、分组、状态字段继续兼容。
  • PostgreSQL 环境已具备 search_vector、GIN 全文索引、trigram 索引。
  • 旧库搜索表为空时会自动懒回填,避免迁移后必须手工执行一次重建任务。

本轮代码改造

新增迁移

新增:

yuqei-contract-platform/services/contract-api/migrations/versions/20260624_0009_add_contract_search_documents.py

新增表:

contract_search_documents

核心字段:

  • source_type:contract、file、review
  • source_id:来源业务 ID
  • contract_id:关联合同 ID
  • title
  • subtitle
  • summary
  • status
  • status_label
  • updated_at
  • search_text
  • PostgreSQL 专用生成列:search_vector

核心约束和索引:

  • ux_contract_search_documents_source
  • ix_contract_search_documents_type_updated_id
  • ix_contract_search_documents_status_updated_id
  • ix_contract_search_documents_contract_updated_id
  • ix_contract_search_documents_vector
  • ix_contract_search_documents_text_trgm
  • ix_contract_search_documents_title_trgm

说明:

  • SQLite 测试环境只创建普通表和 B-tree 索引。
  • PostgreSQL 额外启用 pg_trgm,并创建 tsvector 生成列和 GIN 索引。
  • 中文搜索同时保留 trigram 兜底,因为 simple 分词对中文连续文本不一定能稳定切词。

仓储改造

修改:

yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/contracts.py

新增 ContractSearchDocumentModel,并在以下写入路径维护搜索读模型:

  • 创建合同
  • 修改合同
  • Demo 数据 seed
  • 上传附件
  • 下载附件产生审计事件
  • 发起审查
  • 审查任务重新排队
  • worker 领取审查任务
  • 审查完成
  • 审查失败
  • 审批提交、退回、通过等状态流转

搜索逻辑由原来的三段跨业务表查询:

  • contracts
  • contract_files
  • review_tasks + contracts

改为:

  1. 先查 contract_search_documents。
  2. 由 source_type 生成兼容的 SearchResult。
  3. 保留原有分组、状态、URL、排序语义。

API 状态挂载

修改:

yuqei-contract-platform/services/contract-api/src/yuqei_contract_api/main.py

新增:

app.state.contract_repository = repository

用途:

  • 方便测试和运行验证直接访问仓储实例。
  • 不改变任何公开 HTTP API。

PostgreSQL 搜索 SQL 修复

远端 smoke API 验证首次暴露了一个 PostgreSQL 方言问题:

psycopg.errors.AmbiguousParameter: could not determine data type of parameter

原因:

原 SQL 写法为:

(:item_type IS NULL OR source_type = :item_type)

PostgreSQL 在参数同时参与 IS NULL 和等值比较时无法推断参数类型。

修复:

  • 改为动态拼接过滤条件。
  • 有 type 时才加入 source_type = :item_type。
  • 有 status 时才加入 status = :status。

收益:

  • 消除 500。
  • SQL 更简单。
  • 规划器更容易利用 source_type/status + updated_at 组合索引。

验证结果

本地验证

已通过:

  • python -m compileall yuqei-contract-platform\services\contract-api\src\yuqei_contract_api yuqei-contract-platform\services\contract-api\migrations
  • python -m pytest yuqei-contract-platform\services\contract-api\tests -q

结果:

  • 合同 API 测试:24 passed
  • 空 SQLite Alembic 迁移链升级到:20260624_0009 (head)

新增测试覆盖:

  • 统一搜索能命中合同正文中的关键字。
  • 统一搜索能命中附件。
  • 统一搜索能命中审查任务。
  • 审查任务完成后,回写的审查摘要能被统一搜索检索到。

远端 smoke 验证

执行:

yuqei-infra/scripts/deploy-v2-smoke-docker.ps1 -UseSshpass -AllowDirtyWorktree -SkipSmoke

通过记录:

  • project-docs/qa-artifacts/v2-smoke-docker-deploy/2026-06-24T19-26-51
  • project-docs/qa-artifacts/v2-smoke-docker-deploy/2026-06-24T19-31-41

远端环境:

  • Server:192.168.3.90
  • Compose project:yuqei-contract-v2-smoke
  • Contract API:http://192.168.3.90:18100
  • Contract DB:yuqei_contract

远端数据库检查:

  • Contract API Alembic version:20260624_0009
  • contract_search_documents.search_text:text
  • contract_search_documents.search_vector:tsvector
  • pg_trgm 扩展存在
  • 搜索表索引存在:
    • ix_contract_search_documents_vector
    • ix_contract_search_documents_text_trgm
    • ix_contract_search_documents_title_trgm
    • ix_contract_search_documents_type_updated_id
    • ix_contract_search_documents_status_updated_id

远端真实 API 链路

已验证:

  1. 创建合同。
  2. 使用正文关键字搜索合同。
  3. 发起审查任务。
  4. 使用审查关键词搜索 queued 审查任务。

结果:

  • body_search_hit = true
  • review_search_hit = true

验证样本:

  • contract-bc96db19d023
  • review-747e59a2b937

远端 EXPLAIN 结果

合同正文搜索:

  • 查询表:contract_search_documents
  • 计划:Index Scan Backward using ix_contract_search_documents_type_updated_id
  • Execution Time:约 0.273 ms
  • 未再扫描 contracts.body_content

审查任务搜索:

  • 查询表:contract_search_documents
  • 计划:Index Scan Backward using ix_contract_search_documents_status_updated_id
  • Execution Time:约 0.166 ms
  • 未再扫描 review_tasks
  • 未再 join contracts

说明:

当前 smoke 数据量较小,规划器优先使用 source_type/status + updated_at 组合索引再过滤关键字。大数据量下仍需继续观察 GIN search_vector / trigram 索引是否被稳定选择。

当前限制

本轮解决的是统一搜索主路径的全表扫描风险,不代表搜索系统已经最终完成。

仍需后续处理:

  • 文件正文目前没有进入 contract_search_documents.search_text,只索引文件名、上传人、关联合同标题。后续等文件解析/OCR worker 稳定后,把解析文本写入同一张搜索表。
  • PostgreSQL 中文全文分词仍采用 simple + trigram 混合兜底,未来如需要更强中文分词,可评估专用中文分词扩展或应用层切词。
  • 当前搜索表没有 tenant 字段。DB-2/DB-4 完成租户模型后,需要把 tenant_id 加入搜索表、唯一约束和所有查询条件。
  • 当前搜索表采用懒回填和同步写入,后续数据量变大后应增加显式“重建搜索索引”运维命令和后台任务。

下一步建议

DB-5 下一步建议进入:

pgvector 查询形态改造

目标:

  • 让 AI Platform 的知识库向量检索稳定命中 HNSW 索引。
  • 修复当前 embedding <=> query_vector 查询形态没有命中表达式 HNSW 的问题。
  • 在真实 PostgreSQL 上复测法律问答 / 合同审查 RAG 检索的 EXPLAIN。

之后再进入:

  • DB-2:Contract Platform 清洁领域模型。
  • DB-3:AI Platform 清洁领域模型。
  • DB-4:tenant/RLS/权限隔离。
  • DB-6:备份恢复、慢查询监控、数据库安全基线。