diff --git a/backend/src/tracker/api/routes/vorlagen.py b/backend/src/tracker/api/routes/vorlagen.py index 207ba19..82ae58c 100644 --- a/backend/src/tracker/api/routes/vorlagen.py +++ b/backend/src/tracker/api/routes/vorlagen.py @@ -54,16 +54,31 @@ def list_vorlagen( params.append(partei) if suche: - where_clauses.append( - "(v.betreff LIKE ? OR v.aktenzeichen LIKE ?" - " OR v.volltext_clean LIKE ?" - " OR v.id IN (" - " SELECT kb.vorlage_id FROM ki_bewertungen kb" - " WHERE kb.typ = 'zusammenfassung' AND kb.begruendung LIKE ?" - "))" - ) - like = f"%{suche}%" - params.extend([like, like, like, like]) + # Try FTS5 first, fall back to LIKE + has_fts = conn.execute( + "SELECT name FROM sqlite_master WHERE type='table' AND name='vorlagen_fts'" + ).fetchone() + if has_fts: + # Escape FTS5 special characters and build query + fts_query = " ".join( + f'"{word}"' for word in suche.split() if word.strip() + ) + if fts_query: + where_clauses.append( + "v.id IN (SELECT rowid FROM vorlagen_fts WHERE vorlagen_fts MATCH ?)" + ) + params.append(fts_query) + else: + where_clauses.append( + "(v.betreff LIKE ? OR v.aktenzeichen LIKE ?" + " OR v.volltext_clean LIKE ?" + " OR v.id IN (" + " SELECT kb.vorlage_id FROM ki_bewertungen kb" + " WHERE kb.typ = 'zusammenfassung' AND kb.begruendung LIKE ?" + "))" + ) + like = f"%{suche}%" + params.extend([like, like, like, like]) where_sql = ("WHERE " + " AND ".join(where_clauses)) if where_clauses else "" @@ -97,6 +112,28 @@ def list_vorlagen( {"kuerzel": a["kuerzel"], "name": a["name"], "farbe": a["farbe"]} ) + # FTS5 snippets for search results + snippet_map: dict = {} + if suche and has_fts and fts_query and vorlage_ids: + placeholders = ",".join("?" * len(vorlage_ids)) + try: + snippet_rows = conn.execute( + f"""SELECT rowid, + snippet(vorlagen_fts, 1, '', '', '…', 12) as snip_betreff, + snippet(vorlagen_fts, 2, '', '', '…', 20) as snip_volltext, + snippet(vorlagen_fts, 3, '', '', '…', 16) as snip_zusammenfassung + FROM vorlagen_fts + WHERE vorlagen_fts MATCH ? AND rowid IN ({placeholders})""", + [fts_query] + vorlage_ids, + ).fetchall() + for sr in snippet_rows: + # Pick best non-empty snippet + snip = sr["snip_zusammenfassung"] or sr["snip_volltext"] or sr["snip_betreff"] or "" + if snip: + snippet_map[sr["rowid"]] = snip + except Exception: + pass # Snippets are optional + items = [ { "id": r["id"], @@ -106,6 +143,7 @@ def list_vorlagen( "datum_eingang": r["datum_eingang"], "ist_verwaltungsvorlage": bool(r["ist_verwaltungsvorlage"]), "antragsteller": antragsteller_map.get(r["id"], []), + **({"snippet": snippet_map[r["id"]]} if r["id"] in snippet_map else {}), } for r in rows ] diff --git a/frontend/src/routes/vorlagen/+page.svelte b/frontend/src/routes/vorlagen/+page.svelte index 16518f8..c90850b 100644 --- a/frontend/src/routes/vorlagen/+page.svelte +++ b/frontend/src/routes/vorlagen/+page.svelte @@ -147,11 +147,16 @@ {/if} -