"""截图列表搜索:FTS + 子串模糊(兼容中文标签/标题)。""" from __future__ import annotations from sqlalchemy import or_, select, text from sqlalchemy.orm import Session from app.models.meta import ScreenshotMeta from app.models.screenshot import Screenshot from app.models.tag import Tag def fts_query_string(raw: str) -> str: """把用户输入处理成 FTS5 查询串(中英文均支持前缀匹配)。""" parts = [p for p in raw.replace("\n", " ").split() if p] if not parts: return raw cleaned: list[str] = [] for p in parts: p = p.replace('"', "").strip() if not p: continue cleaned.append(f'"{p}"*') return " ".join(cleaned) def collect_search_ids(session: Session, q: str, *, limit: int = 5000) -> set[int]: """联合 FTS5 与 LIKE 子串搜索,返回匹配的 screenshot id 集合。""" q = q.strip() if not q: return set() ids: set[int] = set() like = f"%{q}%" # 1) FTS5 全文索引 try: fts_sql = text( "SELECT rowid FROM screenshots_fts WHERE screenshots_fts MATCH :q LIMIT :lim" ) rows = session.execute(fts_sql, {"q": fts_query_string(q), "lim": limit}).fetchall() ids.update(int(row[0]) for row in rows) except Exception: pass # 2) 子串模糊:OCR/AI 文本(解决「三花」匹配「三花猫」) meta_ids = session.scalars( select(ScreenshotMeta.screenshot_id).where( or_( ScreenshotMeta.ocr_text.ilike(like), ScreenshotMeta.ai_title.ilike(like), ScreenshotMeta.ai_summary.ilike(like), ScreenshotMeta.ai_suggestion.ilike(like), ) ).limit(limit) ).all() ids.update(int(i) for i in meta_ids) # 3) 标签名子串匹配 tag_ids = session.scalars( select(Screenshot.id) .join(Screenshot.tags) .where(Tag.name.ilike(like)) .limit(limit) ).all() ids.update(int(i) for i in tag_ids) return ids