mirror of
https://gitee.com/mateos/mateclaw.git
synced 2026-09-14 03:33:43 +08:00
Directory-scan ingestion deduped only by content hash, so when a file at a known path changed, the new hash missed the existing raw and a second row was inserted for the same source_path — both rows then generated wiki pages, accumulating duplicates. Make source path the primary dedup key: same path + same hash skips, same path + changed hash updates the existing raw in place (reset to pending, re-process), falling back to the content-hash check only for genuine copies at new paths. Reprocessing reuses the same rawId, so deleteExclusiveBySourceRawId cleans the old pages before regeneration — no duplicate rows and no duplicate pages. findBySourcePath gains LIMIT 1 to tolerate pre-existing duplicates; docs/fix-duplicate-raws.sql remediates existing data. Closes #271
118 lines
4.2 KiB
SQL
118 lines
4.2 KiB
SQL
-- ============================================================
|
||
-- 修复:知识库原始材料重复入库
|
||
-- 适用:MySQL 8.0+(使用 JSON 函数处理 source_raw_ids)
|
||
-- 说明:同一 (kb_id, source_path) 可能因文件内容变更
|
||
-- 被多次 INSERT 而形成多行。本脚本保留最新行,
|
||
-- 并级联清理其关联的 chunk、citation、page。
|
||
-- ============================================================
|
||
|
||
-- ──────────────────────────────────────────────────────────
|
||
-- STEP 0:预览(只读,不改数据,先跑这一步确认影响范围)
|
||
-- ──────────────────────────────────────────────────────────
|
||
|
||
-- 0-A:查看所有重复组(按 kb_id + source_path 分组,count > 1)
|
||
SELECT
|
||
kb_id,
|
||
source_path,
|
||
COUNT(*) AS duplicate_count,
|
||
MAX(id) AS keep_id,
|
||
GROUP_CONCAT(id ORDER BY id DESC) AS all_ids
|
||
FROM mate_wiki_raw_material
|
||
WHERE source_path IS NOT NULL
|
||
GROUP BY kb_id, source_path
|
||
HAVING COUNT(*) > 1;
|
||
|
||
-- 0-B:查看待删除的具体行(排除每组最新的那一行)
|
||
SELECT
|
||
r.id, r.kb_id, r.source_path,
|
||
r.content_hash, r.processing_status, r.create_time
|
||
FROM mate_wiki_raw_material r
|
||
WHERE r.source_path IS NOT NULL
|
||
AND r.id NOT IN (
|
||
SELECT MAX(id)
|
||
FROM mate_wiki_raw_material
|
||
WHERE source_path IS NOT NULL
|
||
GROUP BY kb_id, source_path
|
||
)
|
||
ORDER BY r.kb_id, r.source_path, r.id;
|
||
|
||
|
||
-- ──────────────────────────────────────────────────────────
|
||
-- STEP 1:开事务,执行清理(确认 STEP 0 结果后再运行)
|
||
-- ──────────────────────────────────────────────────────────
|
||
|
||
START TRANSACTION;
|
||
|
||
-- 1-A:把待删除的 raw id 暂存到临时表,后续步骤复用
|
||
CREATE TEMPORARY TABLE IF NOT EXISTS _stale_raw_ids AS
|
||
SELECT id AS raw_id, kb_id
|
||
FROM mate_wiki_raw_material
|
||
WHERE source_path IS NOT NULL
|
||
AND id NOT IN (
|
||
SELECT MAX(id)
|
||
FROM mate_wiki_raw_material
|
||
WHERE source_path IS NOT NULL
|
||
GROUP BY kb_id, source_path
|
||
);
|
||
|
||
-- 1-B:删除这些 raw 产生的 citation(通过 chunk_id 关联)
|
||
DELETE c
|
||
FROM mate_wiki_page_citation c
|
||
INNER JOIN mate_wiki_chunk ch ON c.chunk_id = ch.id
|
||
INNER JOIN _stale_raw_ids s ON ch.raw_id = s.raw_id;
|
||
|
||
-- 1-C:删除 chunk
|
||
DELETE ch
|
||
FROM mate_wiki_chunk ch
|
||
INNER JOIN _stale_raw_ids s ON ch.raw_id = s.raw_id;
|
||
|
||
-- 1-D:删除仅由该 raw 派生的 page(source_raw_ids 数组长度为 1)
|
||
-- 使用 JSON_CONTAINS 判断 page 是否引用了待删 raw
|
||
DELETE p
|
||
FROM mate_wiki_page p
|
||
WHERE JSON_LENGTH(p.source_raw_ids) = 1
|
||
AND EXISTS (
|
||
SELECT 1
|
||
FROM _stale_raw_ids s
|
||
WHERE JSON_CONTAINS(p.source_raw_ids, CAST(s.raw_id AS CHAR))
|
||
);
|
||
|
||
-- 1-E:对多来源 page,将待删 raw 从 source_raw_ids 中移除
|
||
-- 通过 JSON_TABLE 把数组展开再重组,排除掉 stale raw id
|
||
UPDATE mate_wiki_page p
|
||
SET p.source_raw_ids = (
|
||
SELECT JSON_ARRAYAGG(jt.v)
|
||
FROM JSON_TABLE(p.source_raw_ids, '$[*]' COLUMNS (v BIGINT PATH '$')) jt
|
||
WHERE jt.v NOT IN (SELECT raw_id FROM _stale_raw_ids)
|
||
)
|
||
WHERE JSON_LENGTH(p.source_raw_ids) > 1
|
||
AND EXISTS (
|
||
SELECT 1
|
||
FROM _stale_raw_ids s
|
||
WHERE JSON_CONTAINS(p.source_raw_ids, CAST(s.raw_id AS CHAR))
|
||
);
|
||
|
||
-- 1-F:删除 stale raw 行
|
||
DELETE r
|
||
FROM mate_wiki_raw_material r
|
||
INNER JOIN _stale_raw_ids s ON r.id = s.raw_id;
|
||
|
||
-- 1-G:确认结果
|
||
SELECT
|
||
'stale raws deleted' AS action,
|
||
ROW_COUNT() AS affected_rows;
|
||
|
||
SELECT
|
||
'remaining duplicates' AS check_item,
|
||
COUNT(*) AS count
|
||
FROM mate_wiki_raw_material
|
||
WHERE source_path IS NOT NULL
|
||
GROUP BY kb_id, source_path
|
||
HAVING COUNT(*) > 1;
|
||
|
||
-- 确认无误后提交;如有问题改为 ROLLBACK
|
||
COMMIT;
|
||
-- ROLLBACK;
|
||
|
||
DROP TEMPORARY TABLE IF EXISTS _stale_raw_ids;
|