fix(retrieval): 修复路径逃逸、部分提交、失效向量与分页问题

- 路径逃逸:清洗 folder(拒绝 ..、绝对路径/盘符),_abs_path 增加 Vault 边界校验
- 部分提交:create/update 索引失败时回滚文件
- 失效向量残留:replace_note_metadata 返回旧 id,index_note 清理旧向量
- 分页不完整:fts_count 返回真实命中总数,候选池覆盖 offset+limit

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
yxx
2026-08-27 21:38:43 +08:00
co-authored by Claude
parent 39d1aa0fe9
commit 87717450fd
4 changed files with 179 additions and 24 deletions
+21 -2
View File
@@ -71,11 +71,18 @@ def replace_note_metadata(
created_at: datetime,
updated_at: datetime,
blocks: list[NoteBlock],
) -> None:
"""整体替换一条笔记的元数据、Block 与 FTS5 索引(单事务)。"""
) -> list[str]:
"""整体替换一条笔记的元数据、Block 与 FTS5 索引(单事务)。
返回替换前的旧 block_id 列表,供调用方清理 vec_blocks 中已失效的向量。
"""
conn = connect()
try:
with transaction(conn):
old_block_ids = [
row["block_id"]
for row in conn.execute("SELECT block_id FROM blocks WHERE note_id = ?", (note_id,))
]
conn.execute(
"""
INSERT INTO notes (note_id, title, file_path, folder, tags, created_at, updated_at)
@@ -109,6 +116,7 @@ def replace_note_metadata(
"INSERT INTO blocks_fts (block_id, note_id, heading_path, content) VALUES (?, ?, ?, ?)",
(block.block_id, note_id, segment(" ".join(block.heading_path)), segment(block.content)),
)
return old_block_ids
finally:
conn.close()
@@ -208,6 +216,17 @@ def fts_search(match: str, limit: int = 100) -> list[FtsHit]:
conn.close()
def fts_count(match: str) -> int:
"""返回 FTS5 命中总数,用于分页 total(不受候选池截断影响)。"""
conn = connect()
try:
return conn.execute(
"SELECT COUNT(*) FROM blocks_fts WHERE blocks_fts MATCH ?", (match,)
).fetchone()[0]
finally:
conn.close()
def get_block_hits(block_ids: list[str]) -> list[BlockHit]:
if not block_ids:
return []