|
- import argparse
- import pathlib
- import sqlite3
- import sys
-
-
- def quoted_phrase(value: str) -> str:
- return '"' + value.replace('"', '""') + '"'
-
-
- def main() -> None:
- parser = argparse.ArgumentParser(description="Search the Xinje manual page index without scanning PDFs.")
- parser.add_argument("--index", required=True, type=pathlib.Path)
- parser.add_argument("--manual", help="Case-insensitive filename substring filter")
- parser.add_argument("--limit", type=int, default=12)
- parser.add_argument("query", nargs="+")
- args = parser.parse_args()
- query = " ".join(args.query).strip()
- if not query:
- raise SystemExit("query cannot be empty")
- if args.limit < 1:
- raise SystemExit("--limit must be positive")
- connection = sqlite3.connect(args.index)
- try:
- where = ""
- parameters = []
- if args.manual:
- where = " AND lower(filename) LIKE ?"
- parameters.append(f"%{args.manual.lower()}%")
- try:
- rows = connection.execute(
- "SELECT filename, pdf_page, snippet(page_search, 3, '[', ']', ' ... ', 18) "
- "FROM page_search WHERE page_search MATCH ?" + where + " ORDER BY bm25(page_search) LIMIT ?",
- [quoted_phrase(query), *parameters, args.limit],
- ).fetchall()
- except sqlite3.OperationalError:
- rows = []
- if not rows:
- rows = connection.execute(
- "SELECT filename, pdf_page, substr(text, max(1, instr(text, ?) - 120), 420) "
- "FROM page_search WHERE instr(text, ?) > 0" + where + " LIMIT ?",
- [query, query, *parameters, args.limit],
- ).fetchall()
- print(f"query: {query}")
- print(f"hits: {len(rows)}")
- for filename, page, excerpt in rows:
- print(f"\n[{filename}] PDF page {page}")
- print(excerpt)
- finally:
- connection.close()
-
-
- if __name__ == "__main__":
- if hasattr(sys.stdout, "reconfigure"):
- sys.stdout.reconfigure(encoding="utf-8")
- main()
|