dfd8421dc6
**Storage Layer:** - Database 类(aiosqlite + WAL + 并发保护) - WikiPage 数据模型 - fix_dirty_states 恢复机制 - FTS5 全文搜索支持 - 完整的 SQLite 表结构 **Service Layer:** - CacheService(LRU 缓存 + TTL + 大小限制) - QueryService(查询服务) - ParserService(Markdown 解析) - IndexerService(索引服务) - GraphService(链接图服务) **Tool Layer (8 个 MCP Tools):** - wiki_query - FTS5 全文搜索 + 标签过滤 - memory_bridge - 按工具来源浏览 - wiki_status - 索引状态 - wiki_lint - 健康审计 - cross_linker - 缺失链接发现 - tag_taxonomy - 标签一致性 - wiki_synthesize - 跨概念综合分析 - daily_update - 日常维护 + hot.md 生成 **MCP Protocol Layer:** - MCP 协议解析和封装 - 工具注册和路由 - 错误处理和日志 - stdio 模式支持 **配置和部署:** - requirements.txt - config.example.yaml - ecosystem.config.cjs (PM2) Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
121 lines
3.7 KiB
Python
121 lines
3.7 KiB
Python
"""
|
|
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)
|