""" Service Layer - QueryService(查询服务) 负责所有查询逻辑,包括 FTS5 全文搜索、标签搜索、页面查询等。 参考设计文档:第 2.3 节 """ import logging from typing import List, Set, Optional from ..storage import Database, WikiPage from .cache import CacheService logger = logging.getLogger(__name__) class QueryService: """查询服务 - 负责所有查询逻辑""" def __init__(self, db: Database, cache: CacheService): self.db = db self.cache = cache async def search(self, query: str, limit: int = 10) -> List[WikiPage]: """FTS5 全文搜索""" # 1. 检查缓存 cache_key = f"search:{query}:{limit}" cached = await self.cache.get(cache_key) if cached: logger.debug(f"Cache hit for search: {query}") return cached # 2. FTS5 搜索 results = await self.db.fts_search(query, limit) # 3. 缓存结果 await self.cache.set(cache_key, results, ttl=3600) return results async def search_by_tags(self, tags: List[str]) -> List[WikiPage]: """按标签搜索""" cache_key = f"tags:{','.join(tags)}" cached = await self.cache.get(cache_key) if cached: return cached results = await self.db.search_by_tags(tags) await self.cache.set(cache_key, results, ttl=3600) return results async def search_by_source_tool(self, tool_name: str, limit: int = 50) -> List[WikiPage]: """按来源工具搜索(memory_bridge 使用)""" cache_key = f"tool:{tool_name}:{limit}" cached = await self.cache.get(cache_key) if cached: return cached results = await self.db.search_by_source_tool(tool_name, limit) await self.cache.set(cache_key, results, ttl=1800) # 30 分钟 TTL return results async def get_page(self, path: str) -> Optional[WikiPage]: """获取单个页面""" cache_key = f"page:{path}" cached = await self.cache.get(cache_key) if cached: return cached page = await self.db.get_page(path) if page: await self.cache.set(cache_key, page, ttl=3600) return page async def get_links(self, path: str) -> Set[str]: """获取页面的出链""" cache_key = f"links:{path}" cached = await self.cache.get(cache_key) if cached: return cached links = await self.db.get_links(path) await self.cache.set(cache_key, links, ttl=3600) return links async def get_backlinks(self, path: str) -> Set[str]: """获取页面的反向链接""" cache_key = f"backlinks:{path}" cached = await self.cache.get(cache_key) if cached: return cached backlinks = await self.db.get_backlinks(path) await self.cache.set(cache_key, backlinks, ttl=3600) return backlinks async def find_orphans(self) -> Set[str]: """查找孤立页面(无反向链接)""" cache_key = "orphans" cached = await self.cache.get(cache_key) if cached: return cached all_pages = await self.db.get_all_pages() orphans = set() for page in all_pages: backlinks = await self.db.get_backlinks(page.path) if not backlinks and page.path != "index.md": orphans.add(page.path) await self.cache.set(cache_key, orphans, ttl=1800) return orphans async def get_stats(self) -> dict: """获取索引统计信息""" return await self.db.get_stats() async def invalidate_cache(self, pattern: str = "*") -> int: """使缓存失效""" return await self.cache.invalidate(pattern)