dhammawatthumpra commited on
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.extend([
291
- base,
292
- f"พระ{base}",
293
- f"เรื่องพระ{base}",
294
- f"{base}เถรี",
295
- f"{base}ภิกษุณี",
296
- f"{base}เถระ",
297
- f"{base}เถริยา",
298
- f"{base}เถริยาปทาน",
299
- f"{base}วัตถุ",
300
- f"พระ{base}เถรี",
301
- f"พระ{base}เถระ"
302
- ])
303
  else:
304
  variants.append(t_clean)
305
 
306
- return list(set(variants))
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 = [base, f"พระ{base}", f"เรื่องพระ{base}"]
 
 
 
 
 
 
 
 
 
 
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: