search.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. """
  2. SQLite FTS5 search backend - search and flush operations.
  3. This module provides the search interface for the SQLite FTS backend.
  4. Environment variables:
  5. SQLITEFTS_DB: Database filename (default: search.sqlite3)
  6. FTS_SEPARATE_DATABASE: Use separate database file (default: true)
  7. FTS_TOKENIZERS: FTS5 tokenizer config (default: porter unicode61 remove_diacritics 2)
  8. """
  9. import os
  10. import sqlite3
  11. from pathlib import Path
  12. from typing import List, Iterable
  13. # Config with old var names for backwards compatibility
  14. SQLITEFTS_DB = os.environ.get('SQLITEFTS_DB', 'search.sqlite3').strip()
  15. FTS_SEPARATE_DATABASE = os.environ.get('FTS_SEPARATE_DATABASE', 'true').lower() in ('true', '1', 'yes')
  16. FTS_TOKENIZERS = os.environ.get('FTS_TOKENIZERS', 'porter unicode61 remove_diacritics 2').strip()
  17. def _get_data_dir() -> Path:
  18. data_dir = os.environ.get('DATA_DIR', '').strip()
  19. if data_dir:
  20. return Path(data_dir)
  21. return Path.cwd() / 'data'
  22. def get_db_path() -> Path:
  23. """Get path to the search index database."""
  24. return _get_data_dir() / SQLITEFTS_DB
  25. def search(query: str) -> List[str]:
  26. """Search for snapshots matching the query."""
  27. db_path = get_db_path()
  28. if not db_path.exists():
  29. return []
  30. conn = sqlite3.connect(str(db_path))
  31. try:
  32. cursor = conn.execute(
  33. 'SELECT DISTINCT snapshot_id FROM search_index WHERE search_index MATCH ?',
  34. (query,)
  35. )
  36. return [row[0] for row in cursor.fetchall()]
  37. except sqlite3.OperationalError:
  38. # Table doesn't exist yet
  39. return []
  40. finally:
  41. conn.close()
  42. def flush(snapshot_ids: Iterable[str]) -> None:
  43. """Remove snapshots from the index."""
  44. db_path = get_db_path()
  45. if not db_path.exists():
  46. return
  47. conn = sqlite3.connect(str(db_path))
  48. try:
  49. for snapshot_id in snapshot_ids:
  50. conn.execute('DELETE FROM search_index WHERE snapshot_id = ?', (snapshot_id,))
  51. conn.commit()
  52. except sqlite3.OperationalError:
  53. pass # Table doesn't exist
  54. finally:
  55. conn.close()