search.py 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. """搜索服务封装"""
  2. from app.database import get_connection, search_fts
  3. def search(config, query: str, page: int = 1, page_size: int = 20) -> dict:
  4. """全文搜索,返回带高亮摘要的分页结果"""
  5. conn = get_connection(config.db_path)
  6. try:
  7. limit = page * page_size + page_size
  8. results = search_fts(conn, query, limit=limit)
  9. # 提取摘要(包含关键词的上下文)
  10. snippet_length = 100
  11. items = []
  12. for r in results:
  13. body = _get_body(conn, r["id"])
  14. snippet = _extract_snippet(body, query, snippet_length)
  15. items.append({
  16. "id": r["id"],
  17. "body_snippet": snippet,
  18. "difficulty": r.get("difficulty", 0.5),
  19. "question_type": r.get("question_type", ""),
  20. "import_type": r.get("import_type", ""),
  21. "chapters": r.get("chapters", []),
  22. })
  23. total = len(items)
  24. start = (page - 1) * page_size
  25. end = start + page_size
  26. return {
  27. "items": items[start:end],
  28. "total": total,
  29. "page": page,
  30. "page_size": page_size,
  31. }
  32. finally:
  33. conn.close()
  34. def _get_body(conn, problem_id: str) -> str:
  35. """获取题目的 FTS body"""
  36. row = conn.execute(
  37. "SELECT body FROM problems_fts WHERE problem_id = ?",
  38. (problem_id,)
  39. ).fetchone()
  40. return row["body"] if row else ""
  41. def _extract_snippet(text: str, query: str, length: int = 100) -> str:
  42. """从文本中提取包含查询关键词的摘要"""
  43. if not text:
  44. return ""
  45. # 查找关键词位置
  46. idx = text.lower().find(query.lower())
  47. if idx == -1:
  48. # 尝试部分匹配
  49. for word in query.split():
  50. idx = text.lower().find(word.lower())
  51. if idx != -1:
  52. break
  53. if idx == -1:
  54. # 无匹配,返回开头
  55. return text[:length] + ("..." if len(text) > length else "")
  56. # 提取上下文
  57. start = max(0, idx - length // 2)
  58. end = min(len(text), idx + length // 2)
  59. snippet = text[start:end]
  60. prefix = "..." if start > 0 else ""
  61. suffix = "..." if end < len(text) else ""
  62. return f"{prefix}{snippet}{suffix}"