Spaces:
Running
Running
Commit ·
7fd2d51
1
Parent(s): f33f622
Implement hot-reloaded search rules config file for monk prefixes
Browse files
webapp/tipitaka-api/app/services/search_rules.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
{
|
| 2 |
+
"prefixes": [
|
| 3 |
+
"เรื่องพระมหา",
|
| 4 |
+
"ท่านพระมหา",
|
| 5 |
+
"เรื่องพระ",
|
| 6 |
+
"ท่านพระ",
|
| 7 |
+
"พระมหา",
|
| 8 |
+
"พระนาง",
|
| 9 |
+
"เรื่อง",
|
| 10 |
+
"ท่าน",
|
| 11 |
+
"พระ",
|
| 12 |
+
"มหา",
|
| 13 |
+
"นาง"
|
| 14 |
+
],
|
| 15 |
+
"suffixes": [
|
| 16 |
+
"เถริยาปทาน",
|
| 17 |
+
"เถริยา",
|
| 18 |
+
"เถรวัตถุ",
|
| 19 |
+
"ภิกษุณี",
|
| 20 |
+
"อุบาสิกา",
|
| 21 |
+
"อุบาสก",
|
| 22 |
+
"เถระ",
|
| 23 |
+
"เถรี",
|
| 24 |
+
"ภิกขุ",
|
| 25 |
+
"ภิกษุ",
|
| 26 |
+
"วัตถุ",
|
| 27 |
+
"คาถา",
|
| 28 |
+
"อปทาน",
|
| 29 |
+
"เถร"
|
| 30 |
+
]
|
| 31 |
+
}
|
webapp/tipitaka-api/app/services/search_service.py
CHANGED
|
@@ -1,6 +1,8 @@
|
|
| 1 |
import time
|
| 2 |
import re
|
| 3 |
import asyncio
|
|
|
|
|
|
|
| 4 |
from typing import List, Optional
|
| 5 |
from app.database.sqlite_db import SQLiteDB
|
| 6 |
from app.schemas import SearchResponse, SearchResultItem
|
|
@@ -19,6 +21,47 @@ class SearchService:
|
|
| 19 |
self.rag_service = rag_service
|
| 20 |
self.qts = query_transform_service
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
# ── Autocomplete suggestions ────────────────────────────────────
|
| 23 |
|
| 24 |
|
|
@@ -262,8 +305,7 @@ class SearchService:
|
|
| 262 |
tokens = [t.strip() for t in query_text.split() if t.strip()]
|
| 263 |
variants = []
|
| 264 |
|
| 265 |
-
prefixes
|
| 266 |
-
suffixes = ["เถริยาปทาน", "เถริยา", "เถรวัตถุ", "ภิกษุณี", "อุบาสิกา", "อุบาสก", "เถระ", "เถรี", "ภิกขุ", "ภิกษุ", "วัตถุ", "คาถา", "อปทาน"]
|
| 267 |
|
| 268 |
for t in tokens:
|
| 269 |
t_clean = t.replace('"', '').replace('*', '').strip()
|
|
@@ -287,23 +329,19 @@ class SearchService:
|
|
| 287 |
break
|
| 288 |
|
| 289 |
if len(base) >= 3:
|
| 290 |
-
variants.
|
| 291 |
-
|
| 292 |
-
f"
|
| 293 |
-
|
| 294 |
-
f"{base}
|
| 295 |
-
|
| 296 |
-
|
| 297 |
-
|
| 298 |
-
|
| 299 |
-
f"{base}วัตถุ",
|
| 300 |
-
f"พระ{base}เถรี",
|
| 301 |
-
f"พระ{base}เถระ"
|
| 302 |
-
])
|
| 303 |
else:
|
| 304 |
variants.append(t_clean)
|
| 305 |
|
| 306 |
-
return list(
|
| 307 |
|
| 308 |
def _get_fts_results(self, query_text: str, limit: int = 50) -> List[dict]:
|
| 309 |
"""Run SQLite FTS5 search on query_text with prefix matching and prefix/suffix expansion."""
|
|
@@ -314,11 +352,7 @@ class SearchService:
|
|
| 314 |
# Split into tokens by whitespace
|
| 315 |
tokens = [t.strip() for t in thai_q.split() if t.strip()]
|
| 316 |
|
| 317 |
-
prefixes
|
| 318 |
-
prefixes.sort(key=len, reverse=True)
|
| 319 |
-
|
| 320 |
-
suffixes = ["เถริยาปทาน", "เถริยา", "เถรวัตถุ", "ภิกษุณี", "อุบาสิกา", "อุบาสก", "เถระ", "เถรี", "ภิกขุ", "ภิกษุ", "วัตถุ", "คาถา", "อปทาน"]
|
| 321 |
-
suffixes.sort(key=len, reverse=True)
|
| 322 |
|
| 323 |
processed_tokens = []
|
| 324 |
for t in tokens:
|
|
@@ -349,7 +383,17 @@ class SearchService:
|
|
| 349 |
break
|
| 350 |
|
| 351 |
if len(base) >= 3:
|
| 352 |
-
variations = [
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
clause = " OR ".join(f'"{v}"*' for v in variations)
|
| 354 |
processed_tokens.append(f"({clause})")
|
| 355 |
else:
|
|
|
|
| 1 |
import time
|
| 2 |
import re
|
| 3 |
import asyncio
|
| 4 |
+
import json
|
| 5 |
+
from pathlib import Path
|
| 6 |
from typing import List, Optional
|
| 7 |
from app.database.sqlite_db import SQLiteDB
|
| 8 |
from app.schemas import SearchResponse, SearchResultItem
|
|
|
|
| 21 |
self.rag_service = rag_service
|
| 22 |
self.qts = query_transform_service
|
| 23 |
|
| 24 |
+
def _load_search_rules(self):
|
| 25 |
+
"""Load prefixes and suffixes from search_rules.json with hot-reloading based on file modified time."""
|
| 26 |
+
config_path = Path(__file__).resolve().parent / "search_rules.json"
|
| 27 |
+
|
| 28 |
+
# Default fallback values if file loading fails or file doesn't exist
|
| 29 |
+
default_prefixes = [
|
| 30 |
+
"เรื่องพระมหา", "ท่านพระมหา", "เรื่องพระ", "ท่านพระ",
|
| 31 |
+
"พระมหา", "พระนาง", "เรื่อง", "ท่าน", "พระ", "มหา", "นาง"
|
| 32 |
+
]
|
| 33 |
+
default_suffixes = [
|
| 34 |
+
"เถริยาปทาน", "เถริยา", "เถรวัตถุ", "ภิกษุณี", "อุบาสิกา",
|
| 35 |
+
"อุบาสก", "เถระ", "เถรี", "ภิกขุ", "ภิกษุ", "วัตถุ", "คาถา", "อปทาน", "เถร"
|
| 36 |
+
]
|
| 37 |
+
|
| 38 |
+
if not config_path.exists():
|
| 39 |
+
return default_prefixes, default_suffixes
|
| 40 |
+
|
| 41 |
+
try:
|
| 42 |
+
mtime = config_path.stat().st_mtime
|
| 43 |
+
# Check if cached values exist and file has not been modified
|
| 44 |
+
if hasattr(self, "_rules_cached_mtime") and self._rules_cached_mtime == mtime:
|
| 45 |
+
return self._cached_prefixes, self._cached_suffixes
|
| 46 |
+
|
| 47 |
+
with open(config_path, "r", encoding="utf-8") as f:
|
| 48 |
+
data = json.load(f)
|
| 49 |
+
|
| 50 |
+
prefixes = data.get("prefixes", default_prefixes)
|
| 51 |
+
suffixes = data.get("suffixes", default_suffixes)
|
| 52 |
+
|
| 53 |
+
# Sort lists by length in descending order to avoid greedy prefix/suffix stripping issues
|
| 54 |
+
prefixes.sort(key=len, reverse=True)
|
| 55 |
+
suffixes.sort(key=len, reverse=True)
|
| 56 |
+
|
| 57 |
+
self._cached_prefixes = prefixes
|
| 58 |
+
self._cached_suffixes = suffixes
|
| 59 |
+
self._rules_cached_mtime = mtime
|
| 60 |
+
|
| 61 |
+
return prefixes, suffixes
|
| 62 |
+
except Exception:
|
| 63 |
+
return default_prefixes, default_suffixes
|
| 64 |
+
|
| 65 |
# ── Autocomplete suggestions ────────────────────────────────────
|
| 66 |
|
| 67 |
|
|
|
|
| 305 |
tokens = [t.strip() for t in query_text.split() if t.strip()]
|
| 306 |
variants = []
|
| 307 |
|
| 308 |
+
prefixes, suffixes = self._load_search_rules()
|
|
|
|
| 309 |
|
| 310 |
for t in tokens:
|
| 311 |
t_clean = t.replace('"', '').replace('*', '').strip()
|
|
|
|
| 329 |
break
|
| 330 |
|
| 331 |
if len(base) >= 3:
|
| 332 |
+
variants.append(base)
|
| 333 |
+
for pref in prefixes:
|
| 334 |
+
variants.append(f"{pref}{base}")
|
| 335 |
+
for suff in suffixes:
|
| 336 |
+
variants.append(f"{base}{suff}")
|
| 337 |
+
# Add some combination cases for common titles
|
| 338 |
+
variants.append(f"พระ{base}เถระ")
|
| 339 |
+
variants.append(f"พระ{base}เถรี")
|
| 340 |
+
variants.append(f"พระมหา{base}เถระ")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
else:
|
| 342 |
variants.append(t_clean)
|
| 343 |
|
| 344 |
+
return list(dict.fromkeys(v for v in variants if v.strip()))
|
| 345 |
|
| 346 |
def _get_fts_results(self, query_text: str, limit: int = 50) -> List[dict]:
|
| 347 |
"""Run SQLite FTS5 search on query_text with prefix matching and prefix/suffix expansion."""
|
|
|
|
| 352 |
# Split into tokens by whitespace
|
| 353 |
tokens = [t.strip() for t in thai_q.split() if t.strip()]
|
| 354 |
|
| 355 |
+
prefixes, suffixes = self._load_search_rules()
|
|
|
|
|
|
|
|
|
|
|
|
|
| 356 |
|
| 357 |
processed_tokens = []
|
| 358 |
for t in tokens:
|
|
|
|
| 383 |
break
|
| 384 |
|
| 385 |
if len(base) >= 3:
|
| 386 |
+
variations = [
|
| 387 |
+
base,
|
| 388 |
+
f"พระ{base}",
|
| 389 |
+
f"มหา{base}",
|
| 390 |
+
f"พระมหา{base}",
|
| 391 |
+
f"เรื่องพระ{base}",
|
| 392 |
+
f"เรื่องพระมหา{base}",
|
| 393 |
+
f"ท่านพระ{base}",
|
| 394 |
+
f"ท่านพระมหา{base}"
|
| 395 |
+
]
|
| 396 |
+
variations = list(dict.fromkeys(v for v in variations if v.strip()))
|
| 397 |
clause = " OR ".join(f'"{v}"*' for v in variations)
|
| 398 |
processed_tokens.append(f"({clause})")
|
| 399 |
else:
|