From 4321cfa62f6d716c9d516a48cce80da0de03dc33 Mon Sep 17 00:00:00 2001 From: lychang <632945494@qq.com> Date: Tue, 21 Jul 2026 03:50:02 +0800 Subject: [PATCH] update code level --- docs/api.md | 7 +- src/engine/mod.rs | 4 +- src/engine/person_merger.rs | 493 ------------------------------------ src/engine/pipeline.rs | 89 +++---- src/main.rs | 2 +- src/routes/person.rs | 2 +- src/routes/pipeline.rs | 14 +- src/routes/tag.rs | 2 +- 8 files changed, 39 insertions(+), 574 deletions(-) delete mode 100644 src/engine/person_merger.rs diff --git a/docs/api.md b/docs/api.md index d079903..48a5a3f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -460,7 +460,7 @@ Base URL: `http://:/api` ## 管线 ### POST /api/pipeline/run -手动触发完整管线。 +手动触发完整管线(dict 优先匹配解析)。 **参数**:无 @@ -478,9 +478,6 @@ Base URL: `http://:/api` "new_albums_found": 0, "persons_updated": 0, "tags_updated": 0, - "persons_merged": 0, - "aliases_moved": 0, - "tags_removed_person": 0, "empty_albums_deleted": 0, "dup_albums_deleted": 0, "search_db_synced": 0, @@ -522,8 +519,6 @@ Base URL: `http://:/api` "success": true, "albums_parsed": 100, "persons_updated": 80, - "persons_merged": 5, - "aliases_moved": 3, "non_persons_deleted": 2, "stop_word_persons_removed": 1, "synced_to_search_db": 100, diff --git a/src/engine/mod.rs b/src/engine/mod.rs index 9582e96..46e76eb 100644 --- a/src/engine/mod.rs +++ b/src/engine/mod.rs @@ -1,7 +1,6 @@ -//! 核心引擎层 — 标题解析、人名匹配、标签提取、推荐计算 +//! 核心引擎层 — 标题解析、标签提取、推荐计算 pub mod title_parser; -pub mod person_merger; pub mod tag_dict; pub mod tag_extractor; pub mod recommend; @@ -9,7 +8,6 @@ pub mod pipeline; pub mod crawler; #[allow(unused)] pub use title_parser::*; -#[allow(unused)] pub use person_merger::*; #[allow(unused)] pub use tag_dict::*; #[allow(unused)] pub use tag_extractor::*; pub use recommend::*; diff --git a/src/engine/person_merger.rs b/src/engine/person_merger.rs deleted file mode 100644 index f199aa0..0000000 --- a/src/engine/person_merger.rs +++ /dev/null @@ -1,493 +0,0 @@ -//! 人名匹配与合并 -//! 对应 Python 的 match_person_from_title.py + merge_persons_by_core.py -//! 所有函数均为 async,因为底层 Database 操作是异步的 - -use crate::db::Database; -use crate::engine::title_parser::{extract_cn, is_excluded_name}; -use std::collections::{HashMap, HashSet}; - -/// 匹配模式 -#[derive(Debug, Clone)] -pub struct Pattern { - pub text: String, - pub person_name: String, - pub source: String, // "name" | "alias" - pub length: usize, -} - -/// 匹配结果 -#[derive(Debug, Clone)] -pub struct MatchResult { - pub matched: bool, - pub method: String, - pub person: Option, - pub matched_text: Option, - pub all_persons: Vec, - pub multi_person: bool, -} - -/// 深度匹配结果 -#[derive(Debug, Clone)] -pub struct DeepMatchResult { - pub matched: bool, - pub method: String, // "jieba_extract" | "exact_name_match" | "alias_fuzzy_match" - pub person: Option, - pub matched_text: Option, - pub all_persons: Vec, - pub multi_person: bool, -} - -// ── Pass1: 收集模式 ── - -/// 收集所有人名和别名,构建匹配模式列表 -/// 按长度降序排列,确保长名优先匹配 -pub async fn collect_person_patterns(db: &Database) -> Vec { - let mut patterns = Vec::new(); - - // 1. 收集所有人名 - let rows = db.get_all_person_names().await; - for (name, _albums) in &rows { - if is_excluded_name(name) { - continue; - } - patterns.push(Pattern { - text: name.clone(), - person_name: name.clone(), - source: "name".to_string(), - length: name.len(), - }); - } - - // 2. 收集所有别名 - let aliases = db.get_all_aliases().await; - for (alias_name, person_name) in &aliases { - if is_excluded_name(person_name) || is_excluded_name(alias_name) { - continue; - } - if alias_name != person_name { - patterns.push(Pattern { - text: alias_name.clone(), - person_name: person_name.clone(), - source: "alias".to_string(), - length: alias_name.len(), - }); - } - } - - // 3. 去重 - let mut best_map: HashMap = HashMap::new(); - for p in patterns { - let key = p.text.to_lowercase(); - best_map.entry(key).or_insert(p); - } - let mut patterns: Vec = best_map.into_values().collect(); - - // 4. 按长度降序排列 - patterns.sort_by(|a, b| b.length.cmp(&a.length)); - - patterns -} - -/// Pass1: 简单子串匹配 -pub fn match_title(title: &str, patterns: &[Pattern]) -> Option<(String, String, String)> { - if title.is_empty() { - return None; - } - - for p in patterns { - if p.text.len() < 2 { - continue; - } - if title.contains(&p.text) { - return Some((p.text.clone(), p.person_name.clone(), p.source.clone())); - } - } - - None -} - -/// Pass2: 深度匹配(多重策略) -pub fn deep_match_title( - title: &str, - known_names: &HashSet, - known_aliases: &HashSet, -) -> DeepMatchResult { - let mut result = DeepMatchResult { - matched: false, - method: String::new(), - person: None, - matched_text: None, - all_persons: Vec::new(), - multi_person: false, - }; - - if title.is_empty() { - return result; - } - - let title_clean = title.trim(); - - // 策略1: 用正则提取人名 - let extracted = crate::engine::title_parser::extract_person_name_from_title(title_clean); - if let Some(e) = extracted.first() { - if known_names.contains(e) { - result.matched = true; - result.method = "jieba_extract".to_string(); - result.person = Some(e.clone()); - result.matched_text = Some(e.clone()); - return result; - } - } - - // 策略2: 标题中精确子串匹配已知人名(长名优先) - let mut found: Vec = Vec::new(); - let mut sorted_names: Vec<&String> = known_names.iter().collect(); - sorted_names.sort_by(|a, b| b.len().cmp(&a.len())); - - for name in &sorted_names { - if name.len() < 2 { - continue; - } - if title_clean.contains(name.as_str()) { - found.push(name.to_string()); - } - } - - if !found.is_empty() { - found.sort_by(|a, b| b.len().cmp(&a.len())); - let best = found[0].clone(); - result.matched = true; - result.method = "exact_name_match".to_string(); - result.person = Some(best.clone()); - result.matched_text = Some(best); - result.all_persons = found.clone(); - result.multi_person = found.len() > 1; - return result; - } - - // 策略3: 别名模糊匹配 - let title_lower = title_clean.to_lowercase(); - let mut sorted_aliases: Vec<&String> = known_aliases.iter().collect(); - sorted_aliases.sort_by(|a, b| b.len().cmp(&a.len())); - - for alias in &sorted_aliases { - if alias.len() < 2 { - continue; - } - if title_lower.contains(alias.to_lowercase().as_str()) { - result.matched = true; - result.method = "alias_fuzzy_match".to_string(); - result.person = Some(alias.to_string()); - result.matched_text = Some(alias.to_string()); - return result; - } - } - - result -} - -/// 收集所有已知人名和别名 -pub async fn collect_known_names(db: &Database) -> (HashSet, HashSet) { - let mut known_names = HashSet::new(); - let mut known_aliases = HashSet::new(); - - for (name, _) in db.get_all_person_names().await { - known_names.insert(name); - } - for (alias_name, _) in db.get_all_aliases().await { - known_aliases.insert(alias_name); - } - - (known_names, known_aliases) -} - -/// 检测含多个人名的相册 -pub async fn detect_multi_person_titles( - db: &Database, - known_names: Option<&HashSet>, -) -> Vec<(String, String, Vec)> { - let names = match known_names { - Some(n) => n.clone(), - None => collect_known_names(db).await.0, - }; - - let albums = db.get_all_albums_full().await; - let mut multi_person = Vec::new(); - - for album in &albums { - let title = album.title.as_deref().unwrap_or(""); - if title.is_empty() { - continue; - } - - let mut found: Vec = Vec::new(); - let mut sorted_names: Vec<&String> = names.iter().collect(); - sorted_names.sort_by(|a, b| b.len().cmp(&a.len())); - - for name in &sorted_names { - if name.len() < 2 { - continue; - } - if title.contains(name.as_str()) { - found.push(name.to_string()); - } - } - - if found.len() > 1 { - multi_person.push(( - album.id.clone(), - title.to_string(), - found, - )); - } - } - - multi_person -} - -// ── 中文核心合并分析 ── - -/// 分析所有人名的中文核心分组 -/// 返回: {core: [(id, name, albums, images), ...]} -pub async fn analyze_groups(db: &Database) -> HashMap> { - let persons = db.get_all_persons_with_counts().await; - let mut groups: HashMap> = HashMap::new(); - - for (pid, name, albums, images) in persons { - if let Some(cn) = extract_cn(&name) { - if cn.len() >= 2 { - groups.entry(cn).or_default().push((pid, name, albums, images)); - } - } - } - - for items in groups.values_mut() { - items.sort_by(|a, b| b.2.cmp(&a.2)); - } - - groups.retain(|_, v| v.len() >= 2); - - groups -} - -/// 合并结果统计 -#[derive(Debug, Clone, Default)] -pub struct MergeResult { - pub groups_analyzed: usize, - pub persons_merged: usize, - pub aliases_moved: usize, -} - -/// 执行合并:扫描所有可合并的人物组,合并到相册数最多的那个 -/// -/// 合并分两个阶段: -/// Phase 1: 中文核心相同 → 合并到相册数最多的 -/// Phase 2: 名字包含关系(A包含B,且B更短)→ A合并到B(短名保留) -/// -/// 返回合并统计 -pub async fn execute_merge(db: &Database) -> MergeResult { - let mut result = MergeResult::default(); - - // ── Phase 1: 中文核心合并 ── - let groups = analyze_groups(db).await; - result.groups_analyzed = groups.len(); - - for (_core, items) in &groups { - if items.len() < 2 { - continue; - } - - // 第一个(相册数最多)作为目标 - let dst_id = items[0].0; - let dst_name = &items[0].1; - - // 其余的合并到目标 - for (src_id, src_name, _, _) in &items[1..] { - println!("[合并·核心] {} (id={}) → {} (id={})", src_name, src_id, dst_name, dst_id); - - let src_aliases = db.get_person_aliases(*src_id).await; - let alias_count_before = src_aliases.len(); - - db.merge_person_into(*src_id, dst_id).await; - - result.persons_merged += 1; - let moved = alias_count_before + 1; - result.aliases_moved += moved; - } - } - - // ── Phase 2: 包含关系合并 ── - // 重新获取当前所有人物(Phase 1 后可能有变化) - let mut persons = db.get_all_persons_with_counts().await; - - // 过滤掉排除名单中的 - persons.retain(|(_, name, _, _)| !is_excluded_name(name)); - - // 按名字长度升序排列(短名在前,作为合并目标候选) - persons.sort_by(|a, b| a.1.len().cmp(&b.1.len())); - - // 用一个 set 记录已被合并的 id,避免重复操作 - let mut merged_ids: HashSet = HashSet::new(); - - for i in 0..persons.len() { - let (dst_id, dst_name, _dst_albums, _) = &persons[i]; - if merged_ids.contains(dst_id) { continue; } - if dst_name.len() < 2 { continue; } - - // 找所有比 dst_name 长且包含 dst_name 的人 - let dst_lower = dst_name.to_lowercase(); - - for j in (i + 1)..persons.len() { - let (src_id, src_name, _, _) = &persons[j]; - if merged_ids.contains(src_id) { continue; } - if src_name.len() <= dst_name.len() { continue; } - - let src_lower = src_name.to_lowercase(); - if src_lower.contains(&dst_lower) { - // 额外保护:dst 的中文核心必须和 src 的中文核心一致, - // 或者 dst 完全匹配 src 的开头/结尾,避免误合并(如 "小美" 包含 "小") - let dst_cn = extract_cn(dst_name).unwrap_or_default(); - let src_cn = extract_cn(src_name).unwrap_or_default(); - let cn_ok = !dst_cn.is_empty() && !src_cn.is_empty() && src_cn.contains(&dst_cn); - let starts_with = src_lower.starts_with(&dst_lower); - let ends_with = src_lower.ends_with(&dst_lower); - - if !cn_ok && !starts_with && !ends_with { - continue; - } - - println!("[合并·包含] {} (id={}) → {} (id={})", src_name, src_id, dst_name, dst_id); - - let src_aliases = db.get_person_aliases(*src_id).await; - let alias_count_before = src_aliases.len(); - - db.merge_person_into(*src_id, *dst_id).await; - - merged_ids.insert(*src_id); - result.persons_merged += 1; - let moved = alias_count_before + 1; - result.aliases_moved += moved; - } - } - } - - if result.persons_merged > 0 { - println!("[合并] 完成:分析 {} 组,合并 {} 个人物,迁移 {} 个别名", - result.groups_analyzed, result.persons_merged, result.aliases_moved); - } - - result -} - -/// 统计匹配分析 -#[derive(Debug, Clone)] -pub struct MatchStats { - pub total_albums: usize, - pub matched: usize, - pub unmatched: usize, - pub already_set: usize, - pub changed: usize, - pub by_source: HashMap, - pub name_frequency: HashMap, -} - -/// 执行 Pass1 匹配分析 -pub async fn analyze_matches(db: &Database, patterns: &[Pattern]) -> MatchStats { - let albums = db.get_all_albums_full().await; - let mut stats = MatchStats { - total_albums: albums.len(), - matched: 0, - unmatched: 0, - already_set: 0, - changed: 0, - by_source: HashMap::new(), - name_frequency: HashMap::new(), - }; - - for album in &albums { - let title = album.title.as_deref().unwrap_or(""); - let current = ""; // person_name_original 已迁移到 search.db - - if let Some((_text, person, source)) = match_title(title, patterns) { - *stats.by_source.entry(source).or_insert(0) += 1; - *stats.name_frequency.entry(person.clone()).or_insert(0) += 1; - - if current.is_empty() { - stats.matched += 1; - } else if current != person { - stats.changed += 1; - } else { - stats.already_set += 1; - } - } else { - stats.unmatched += 1; - } - } - - stats -} - -/// 深度匹配分析统计 -#[derive(Debug, Clone)] -pub struct DeepMatchStats { - pub total_albums: usize, - pub pass1_matched: usize, - pub pass1_unmatched: usize, - pub pass2_jieba_match: usize, - pub pass2_exact_match: usize, - pub pass2_alias_match: usize, - pub pass2_still_unmatched: usize, - pub pass2_multi_person: usize, -} - -/// 执行 Pass2 深度匹配分析 -pub async fn analyze_deep_matches(db: &Database) -> DeepMatchStats { - let (known_names, known_aliases) = collect_known_names(db).await; - let albums = db.get_all_albums_full().await; - - let mut stats = DeepMatchStats { - total_albums: albums.len(), - pass1_matched: 0, - pass1_unmatched: 0, - pass2_jieba_match: 0, - pass2_exact_match: 0, - pass2_alias_match: 0, - pass2_still_unmatched: 0, - pass2_multi_person: 0, - }; - - for album in &albums { - let title = album.title.as_deref().unwrap_or(""); - let current = ""; // person_name_original 已迁移到 search.db - - if title.is_empty() { - continue; - } - - if !current.is_empty() && known_names.contains(current) { - stats.pass1_matched += 1; - continue; - } - - stats.pass1_unmatched += 1; - - let result = deep_match_title(title, &known_names, &known_aliases); - if !result.matched { - stats.pass2_still_unmatched += 1; - continue; - } - - match result.method.as_str() { - "jieba_extract" => stats.pass2_jieba_match += 1, - "exact_name_match" => stats.pass2_exact_match += 1, - "alias_fuzzy_match" => stats.pass2_alias_match += 1, - _ => {} - } - - if result.multi_person { - stats.pass2_multi_person += 1; - } - } - - stats -} \ No newline at end of file diff --git a/src/engine/pipeline.rs b/src/engine/pipeline.rs index f75852a..8452d73 100644 --- a/src/engine/pipeline.rs +++ b/src/engine/pipeline.rs @@ -1,20 +1,20 @@ //! 处理管线 — 批量处理相册元数据 //! -//! 核心架构:脏集追踪 + 增量解析 +//! 核心架构:脏集追踪 + 增量解析,dict 优先匹配 //! -//! 精简后流程(6 阶段,2 次全库扫描): -//! Phase ① 合并预处理(清洗标点/短标签/人名标签/新相册)→ 1次相册扫描 -//! Phase ② 合并人物清理(非人物/停用词人物) → 1次人物扫描 -//! Phase ③ 增量解析脏集相册 → 只扫脏集 -//! Phase ④ 人物合并 + 重计 → 1次人物扫描 -//! Phase ⑤ 后校验标签中的人名 → 可优化 +//! 流程: +//! Phase Z0 删除空相册 +//! Phase Z1 去重同名相册 +//! Phase ① 收集新相册(未处理) +//! Phase ② 合并人物清理(非人物/停用词人物)→ 1次人物扫描 +//! Phase ③ dict 优先解析脏集相册 → 只扫脏集 +//! Phase C 全量重计人物计数 //! //! 脏集(DirtySet)贯穿各阶段,每个阶段发现的"需要重新解析"的相册都会加入脏集, //! 最终只处理脏集里的相册,而非全库扫描,大幅提升效率。 use crate::db::Database; use crate::engine::title_parser; -use crate::engine::person_merger; use crate::engine::tag_dict; use crate::search_db::SearchDb; use std::collections::HashSet; @@ -76,16 +76,19 @@ pub async fn phase_collect_new_albums(db: &Database) -> DirtySet { // ┌─ Phase 1a:并行解析(CPU 密集,rayon par_iter) // └─ Phase 1b:批量写库(IO 密集,一个事务) -pub async fn phase_parse_dirty(db: &Database, dirty_set: &DirtySet) -> (usize, usize) { +pub async fn phase_parse_dirty(db: &Database, search_db: &SearchDb, dirty_set: &DirtySet) -> (usize, usize) { if dirty_set.is_empty() { - println!("[管线·Phase1] 增量解析跳过:脏集为空"); + println!("[管线·Phase3] dict 优先解析跳过:脏集为空"); return (0, 0); } let dirty_ids: Vec = dirty_set.clone().into_inner().into_iter().collect(); let albums = db.get_albums_by_ids(&dirty_ids).await; - // ── Phase 1a:并行解析(CPU bound,rayon) ── + // 加载 search.db 的 dict 条目(提供 merged_to 别名解析) + let dict_entries = search_db.load_dict_all().await.unwrap_or_default(); + + // ── Phase 3a:并行解析(CPU bound,rayon) ── let dirty_albums: Vec<&crate::models::AlbumMeta> = albums.iter() .filter(|a| dirty_set.contains(&a.id)) .collect(); @@ -100,22 +103,23 @@ pub async fn phase_parse_dirty(db: &Database, dirty_set: &DirtySet) -> (usize, u .filter_map(|album| { let title = album.title.as_deref()?; if title.is_empty() { return None; } - Some(title_parser::parse_album_title(&album.id, title)) + // 使用 dict 优先匹配解析 + Some(title_parser::parse_album_title_with_dict(&album.id, title, &dict_entries)) }) .collect(); let person_count = parsed.iter().filter(|r| !r.person_names.is_empty()).count(); let tag_count = parsed.iter().filter(|r| !r.tags.is_empty()).count(); - // ── Phase 1b:批量写入 ── + // ── Phase 3b:批量写入 ── let _guard = db.write_lock.lock().await; let mut tx = db.pool.begin().await.expect("begin tx"); db.batch_write_parse_results(&mut tx, &parsed).await.unwrap_or_else(|e| { - eprintln!("[管线·Phase1b] 批量写入失败: {}", e); + eprintln!("[管线·Phase3b] 批量写入失败: {}", e); }); tx.commit().await.expect("commit tx"); - println!("[管线·Phase1] 并行解析+批量写入完成: 脏集 {} 个, 有效 {} 个 (跳过 {} 无标题), 更新人名 {} 个, 更新标签 {} 个", + println!("[管线·Phase3] dict 优先解析+批量写入完成: 脏集 {} 个, 有效 {} 个 (跳过 {} 无标题), 更新人名 {} 个, 更新标签 {} 个", total, parsed.len(), skipped, person_count, tag_count); (person_count, tag_count) } @@ -149,29 +153,7 @@ pub async fn phase_recount_all(db: &Database) -> usize { fixed } // ════════════════════════════════════════════════ - -pub async fn phase_merge_persons(db: &Database) -> (usize, usize) { - let result = person_merger::execute_merge(db).await; - if result.persons_merged > 0 { - println!("[管线·Phase2] 人物合并完成:分析 {} 组,合并 {} 人,迁移 {} 个别名", - result.groups_analyzed, result.persons_merged, result.aliases_moved); - } else { - println!("[管线·Phase2] 人物合并完成:无需要合并的人物"); - } - (result.persons_merged, result.aliases_moved) -} - -// ════════════════════════════════════════════════ -// Phase B:后校验标签 — 用合并后的人名检查 -// ════════════════════════════════════════════════ - -pub async fn phase_clean_tags_after_parse(db: &Database) -> usize { - println!("[管线·PhaseB] 后校验标签完成:已迁移到 search.db,跳过"); - 0 -} - -// ════════════════════════════════════════════════ -// 合并阶段 — 减少全库扫描次数 +// Preprocess + Dirty Tracking // ════════════════════════════════════════════════ /// 合并预处理结果 @@ -318,22 +300,18 @@ pub struct PipelineResult { pub new_albums_found: usize, // Phase ① pub persons_updated: usize, // Phase ③ pub tags_updated: usize, // Phase ③ - pub persons_merged: usize, // Phase ④ - pub aliases_moved: usize, // Phase ④ - pub tags_removed_person: usize, // Phase ⑤ pub empty_albums_deleted: usize, // Phase Z0 pub dup_albums_deleted: usize, // Phase Z1 } -/// 一键执行精简管线 +/// 一键执行精简管线(dict 优先匹配) /// /// Phase Z0 删除空相册 /// Phase Z1 去重同名相册 -/// Phase ① 合并预处理(清洗+短标签+人名标签+新相册) → 1次相册扫描 -/// Phase ② 合并人物清理(非人物+停用词人物) → 1次人物扫描 -/// Phase ③ 增量解析脏集 → 只扫脏集 -/// Phase ④ 人物合并 + 重计 → 1次人物扫描 -/// Phase ⑤ 后校验标签中的人名 +/// Phase ① 收集新相册 +/// Phase ② 合并人物清理(非人物+停用词人物) → 1次人物扫描 +/// Phase ③ dict 优先解析脏集 → 只扫脏集 +/// Phase C 全量重计 pub async fn run_pipeline(db: &Database, search_db: &SearchDb) -> PipelineResult { // Phase Z0:删除空相册 let empty_albums_deleted = phase_delete_empty_albums(db).await; @@ -341,7 +319,7 @@ pub async fn run_pipeline(db: &Database, search_db: &SearchDb) -> PipelineResult // Phase Z1:去重同名相册(含删除文件夹和 search.db 数据) let dup_albums_deleted = phase_dedup_albums(db, search_db).await; - // Phase ①:合并预处理(1 次相册扫描) + // Phase ①:收集新相册 let pre = phase_preprocess_combined(db).await; let tags_cleaned = pre.tags_cleaned; let short_tags_removed = pre.short_tags_removed; @@ -357,23 +335,16 @@ pub async fn run_pipeline(db: &Database, search_db: &SearchDb) -> PipelineResult master_dirty.extend(dirty_persons.into_inner()); let dirty_albums_detected = master_dirty.len(); - // Phase ③:增量解析 - let (persons_updated, tags_updated) = phase_parse_dirty(db, &master_dirty).await; + // Phase ③:dict 优先解析脏集 + let (persons_updated, tags_updated) = phase_parse_dirty(db, search_db, &master_dirty).await; - // Phase ④:人物合并(内部会做包含关系 + 核心合并) - let (persons_merged, aliases_moved) = phase_merge_persons(db).await; - - // Phase C:全量重计(合并后修正计数) + // Phase C:全量重计(修正计数) let _recounted = phase_recount_all(db).await; - // Phase ⑤:后校验 - let tags_removed_person = phase_clean_tags_after_parse(db).await; - PipelineResult { tags_cleaned, short_tags_removed, person_like_tags_removed, dirty_albums_detected, non_persons_deleted, stop_word_persons_removed, new_albums_found, - persons_updated, tags_updated, persons_merged, - aliases_moved, tags_removed_person, + persons_updated, tags_updated, empty_albums_deleted, dup_albums_deleted, } } diff --git a/src/main.rs b/src/main.rs index 9889ab4..b86599e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -170,7 +170,7 @@ async fn main() -> std::io::Result<()> { let new_albums = engine::pipeline::phase_collect_new_albums(&timer_db).await; if !new_albums.is_empty() { info!("[定时] 发现 {} 个新相册,增量解析...", new_albums.len()); - engine::pipeline::phase_parse_dirty(&timer_db, &new_albums).await; + engine::pipeline::phase_parse_dirty(&timer_db, &timer_search_db, &new_albums).await; info!("[定时] 同步到 search.db..."); engine::pipeline::sync_dirty_albums(&timer_db, &timer_search_db, &new_albums).await; } diff --git a/src/routes/person.rs b/src/routes/person.rs index b8c62b5..cebd944 100644 --- a/src/routes/person.rs +++ b/src/routes/person.rs @@ -154,7 +154,7 @@ pub async fn delete_person( if !album_ids.is_empty() { let mut dirty = engine::pipeline::DirtySet::new(); for aid in &album_ids { dirty.add(aid); } - let (persons_updated, tags_updated) = engine::pipeline::phase_parse_dirty(&db, &dirty).await; + let (persons_updated, tags_updated) = engine::pipeline::phase_parse_dirty(&db, &search_db, &dirty).await; let synced = engine::pipeline::sync_dirty_albums(&db, &search_db, &dirty).await; // 5. 重载内存词典 diff --git a/src/routes/pipeline.rs b/src/routes/pipeline.rs index 122dee3..940b7d0 100644 --- a/src/routes/pipeline.rs +++ b/src/routes/pipeline.rs @@ -22,19 +22,15 @@ pub async fn pipeline_run( "new_albums_found": result.new_albums_found, "persons_updated": result.persons_updated, "tags_updated": result.tags_updated, - "persons_merged": result.persons_merged, - "aliases_moved": result.aliases_moved, - "tags_removed_person": result.tags_removed_person, "empty_albums_deleted": result.empty_albums_deleted, "dup_albums_deleted": result.dup_albums_deleted, "search_db_synced": synced, "message": format!( - "管线执行完毕:删除空相册{},去重{},清洗标点{}相册,移除短标签{},检测人名标签{},脏集{}相册,删除非人物{},清除停用词重叠人物{},发现新相册{},更新人名{},更新标签{},合并人物{},迁移别名{},后移除人名标签{},同步标签/人物到search.db{}", + "管线执行完毕:删除空相册{},去重{},清洗标点{}相册,移除短标签{},检测人名标签{},脏集{}相册,删除非人物{},清除停用词重叠人物{},发现新相册{},更新人名{},更新标签{},同步标签/人物到search.db{}", result.empty_albums_deleted, result.dup_albums_deleted, result.tags_cleaned, result.short_tags_removed, result.person_like_tags_removed, result.dirty_albums_detected, result.non_persons_deleted, result.stop_word_persons_removed, result.new_albums_found, - result.persons_updated, result.tags_updated, - result.persons_merged, result.aliases_moved, result.tags_removed_person, synced + result.persons_updated, result.tags_updated, synced ) })) } @@ -96,15 +92,13 @@ pub async fn pipeline_reparse_persons( "success": true, "albums_parsed": result.new_albums_found, "persons_updated": result.persons_updated, - "persons_merged": result.persons_merged, - "aliases_moved": result.aliases_moved, "non_persons_deleted": result.non_persons_deleted, "stop_word_persons_removed": result.stop_word_persons_removed, "synced_to_search_db": synced, "message": format!( - "人物重新解析完成:重置{}个相册,解析{}个,更新人物{}个,合并{}个,迁移别名{}个,删除非人物{},清除重叠{},同步{}个到search.db", + "人物重新解析完成:重置{}个相册,解析{}个,更新人物{}个,删除非人物{},清除重叠{},同步{}个到search.db", reset_count, result.new_albums_found, - result.persons_updated, result.persons_merged, result.aliases_moved, + result.persons_updated, result.non_persons_deleted, result.stop_word_persons_removed, synced, ) })) diff --git a/src/routes/tag.rs b/src/routes/tag.rs index 8b50bd9..78c0894 100644 --- a/src/routes/tag.rs +++ b/src/routes/tag.rs @@ -89,7 +89,7 @@ pub async fn delete_tag( // 3. 重新解析受影响相册 let mut dirty = engine::pipeline::DirtySet::new(); for aid in &album_ids { dirty.add(aid); } - let (persons_updated, tags_updated) = engine::pipeline::phase_parse_dirty(&db, &dirty).await; + let (persons_updated, tags_updated) = engine::pipeline::phase_parse_dirty(&db, &search_db, &dirty).await; // 4. 同步到 search.db let synced = engine::pipeline::sync_dirty_albums(&db, &search_db, &dirty).await;