From 5013cceb6fd558b1672d065194aeec344e98df35 Mon Sep 17 00:00:00 2001 From: Sebastian Frank Date: Mon, 19 Oct 2026 20:49:12 +0000 Subject: [PATCH] =?UTF-8?q?Suche:=20Stoppw=C3=B6rter=20f=C3=BCr=20ngram=20?= =?UTF-8?q?dokumentieren?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Neuer Abschnitt „Stoppwörter für ngram (ngram.stopwords)" auf projekt/suche: symmetrische Wirkung auf Index und Anfrage, Presets de/en/fr/es/it, `words` als additive Ergänzung, 0 Treffer bei reinen Stoppwort-Anfragen, Sprach-Hinweis für gemischtsprachige Felder, Re-Index-Richtung (hinzufügen unkritisch, entfernen erst nach Neuaufbau) und Multi-Node-Warnung. Präzisiert nach Second Opinion: - Normalisierung ohne `normalize`-Block ergibt `fuer` (alle Zusatznormalisierer), nur `normalize.use: []` ergibt `fur`. - Setup-Fehler (falscher Modus, Mehr-Wort-Eintrag) landen in der Projekt-Fehlerliste; die Sammlung lädt trotzdem, die Stoppwörter bleiben aus. - `words` ergänzt nur, es gibt keinen Ausnahme-Pfad für Preset-Wörter. - Debug-Liste um `mongo(no-terms)` ergänzt (nur mit vorhandenem Index). Fehlerbilder um drei Fälle erweitert, Stempel aktualisiert (tibi-server 69fc4d8, tibi-types 9e12a8e). reference/schema-properties.md via gen:schemas neu erzeugt, Vollständigkeitstabelle in plans/doku-ausbau.md ergänzt. Build 71 Seiten grün, audit-docs: 0 tote Links/0 kaputte Tabellen. --- plans/audit-report.json | 7 +-- plans/doku-ausbau.md | 2 +- src/content/docs/projekt/suche.mdx | 47 ++++++++++++++++++- .../docs/reference/schema-properties.md | 2 +- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/plans/audit-report.json b/plans/audit-report.json index abda00a..1cea3a1 100644 --- a/plans/audit-report.json +++ b/plans/audit-report.json @@ -1,5 +1,5 @@ { - "generatedAt": "2026-10-17T19:58:49.510Z", + "generatedAt": "2026-10-19T20:49:08.257Z", "stats": { "files": 70, "byDir": { @@ -13,9 +13,9 @@ "server": 5 }, "components": { - "Aside": 108, + "Aside": 109, "Badge": 30, - "Steps": 87, + "Steps": 88, "TabItem": 64, "Tabs": 28, "Card": 11, @@ -33,6 +33,7 @@ "src/content/docs/projekt/mcp-nutzen.mdx", "src/content/docs/projekt/navigation-i18n.mdx", "src/content/docs/projekt/ssr-website.mdx", + "src/content/docs/projekt/suche.mdx", "src/content/docs/reference/schema-properties.md" ], "hollowMarkers": [], diff --git a/plans/doku-ausbau.md b/plans/doku-ausbau.md index 079fcf3..204bd00 100644 --- a/plans/doku-ausbau.md +++ b/plans/doku-ausbau.md @@ -55,7 +55,7 @@ Jede Fähigkeit von tibi-server / tibi-admin braucht genau eine Dokustelle. Die | audit (TTL, Lesen) | projekt/audit-backup | ✅ | | jobs (Cron) + actions | projekt/jobs-actions | ✅ | | snapshots + backup/restore | projekt/audit-backup | ✅ | -| search (Textindex, RRF-Hybrid, Regenerate) | projekt/suche (+ projekt/sammlungen-anlegen für Index-YAML) | ✅ | +| search (Textindex, RRF-Hybrid, ngram.stopwords, Regenerate) | projekt/suche (+ projekt/sammlungen-anlegen für Index-YAML) | ✅ | | embedding + vectorindex (inkl. Failover-Gruppen, Startprüfung/Quarantäne/Rückkehr, `goldenPath`, Status-Endpunkte) | server/konfiguration (Gruppen im Detail), server/betrieb (Status/Störungen), projekt/suche (Kurzüberblick) | ✅ | | rankfusion (RRF-Formel) | projekt/suche | ✅ | | permissions (public/user/token, Orgs/Teams-Matrix) | projekt/rechte-tokens | ✅ | diff --git a/src/content/docs/projekt/suche.mdx b/src/content/docs/projekt/suche.mdx index 79a55e5..482a736 100644 --- a/src/content/docs/projekt/suche.mdx +++ b/src/content/docs/projekt/suche.mdx @@ -108,6 +108,46 @@ Alle Modi aus `collection.go:314` — `fields` gilt für `regex` und `ngram`, `v Aus Projektsicht musst du nichts einrichten — lass `autoRegenerate: true` in der YAML stehen, dann pflegen sich `fuzzy`- und `semantic`-Indizes selbst. Der Regenerate-Flow im tibi-admin (Test-Plan `41-search-index-status`) nutzt genau den Endpunkt aus Schritt 4. +## Stoppwörter für `ngram` (`ngram.stopwords`) + +Füllwörter wie „zum", „der" oder „für" treffen in einer n-gram-Suche jeden Text, in dem sie irgendwo vorkommen — auch Beschreibungen, in denen sie nichts aussagen. `ngram.stopwords` entfernt sie aus **beiden** Seiten: aus den gespeicherten N-Grammen und aus der Suchanfrage. Einseitig wäre falsch: stehen gebliebene Stoppwort-N-Gramme blähen die Ähnlichkeitswerte auf und drücken die Unterschiede zwischen relevanten und irrelevanten Treffern zusammen. + +```yaml title="Projekt-YAML — Stoppwörter für fuzzy" +search: + - name: fuzzy + mode: ngram + fields: [name, meta.title, meta.description] + ngram: + sizes: [2, 3] + stopwords: + presets: [de] # de, en, fr, es, it + words: [zum, zur] # optionale Ergänzungen, werden gemerged +``` + +- Die Presets enthalten reine Funktionswörter: Artikel, Pronomen, Präpositionen, Konjunktionen, Hilfsverben und bedeutungsfreie Adverbien (`internal/utils/ngram_stopwords.go`). Negationswörter wie „nicht" oder „kein" bleiben bewusst draußen — „nicht wasserdicht" würde sonst zur Suche nach „wasserdicht". +- Einträge werden mit derselben Normalisiererkette gebildet wie Dokumente und Anfragen. Ohne `normalize`-Block laufen alle Zusatznormalisierer, aus „für" wird `fuer`; nur mit `normalize.use: []` bleibt es beim Standard-Normalisierer und damit bei `fur`. Einträge, die nicht genau ein Wort ergeben, erzeugen einen Projekt-Fehler (`internal/models/collection_setup.go`). +- `stopwords` gilt nur für `mode: ngram`. Ein Block mit anderem Modus und gesetzten Stoppwörtern erzeugt einen Projekt-Fehler; das Projekt lädt trotzdem, die Stoppwörter bleiben in diesem Fall aber aus. +- Ein Set gilt für den ganzen Block, nicht je Sprache: Es wirkt auf jedes Wort in den konfigurierten Feldern. Bei gemischtsprachigen Inhalten lieber `words` statt Presets nutzen oder einen Block je Sprache anlegen — sonst verschwinden Wörter der falschen Sprache (etwa `car`/`or` aus deutschen Texten, wenn `fr` aktiv ist). `words` ergänzt das Set nur; ein Preset-Wort lässt sich damit nicht wieder freistellen. +- Eine Anfrage, die nur aus Stoppwörtern besteht (oder nur aus Begriffen kürzer als jede konfigurierte Größe), liefert **null Treffer** statt der ganzen Sammlung (`internal/utils/ngram.go`). + +### Nach Änderungen neu aufbauen + +`ngram.stopwords` ist Teil des Anreicherungs-Hashs. Hinzufügen ist unkritisch: Die Anfrage verwirft die Wörter, der alte Index enthält nur mehr N-Gramme als nötig. **Entfernen** braucht den Neuaufbau zuerst — sonst liefern diese Wörter null Treffer, bis jedes Dokument neu angereichert ist: + + + +1. `stopwords` in der Projekt-YAML ändern und das Projekt neu laden. +2. `POST /_/:project/_/admin/regenerate-search` mit `{ collection, modes: ["ngram"] }` aufrufen — oder `autoRegenerate: true` setzen und den Reload abwarten. +3. `GET /_/:project/:collection/_queue` beobachten, bis der Zähler 0 ist. Erst danach die alten Wörter aus der Suche nehmen. + + + + + ## Performance-Optionen (Cache und In-Memory-Index) Beide Optionen sind **standardmäßig aus** und ändern ohne Konfiguration nichts. @@ -159,7 +199,7 @@ search: ``` - Gebaut wird der Index beim Projekt-(Re-)Load aus den **gespeicherten** `_search`-Ngrammen (nie aus den Quellfeldern), damit er nicht vom MongoDB-Pfad abweichen kann. -- Genutzt nur für ngram-Quellen von `combined`-Suchen, und nur wenn er fertig geladen und im Budget ist. Sonst — und immer bei Einzel-ngram-Suchen sowie `_count` — läuft die Suche über MongoDB. Der Debug-Header zeigt `inMemory=ram|mongo(loading)|mongo(budget)|mongo(candidates)`. +- Genutzt nur für ngram-Quellen von `combined`-Suchen, und nur wenn er fertig geladen und im Budget ist. Sonst — und immer bei Einzel-ngram-Suchen sowie `_count` — läuft die Suche über MongoDB. Der Debug-Header zeigt `inMemory=ram|mongo(loading)|mongo(budget)|mongo(candidates)|mongo(no-terms)`. - Rollen-`preFilter`, `?filter=` und Hook-Filter gelten weiter: der Index liefert Kandidaten-IDs, die danach gegen den effektiven Filter geprüft werden. - Schreibvorgänge (Anlegen/Ändern/Löschen/Bulk/Worker/Regenerate) halten den Index aktuell; **Snapshot- und Backup-Restore bauen alle In-Memory-Zustände neu auf** (Indizes + Cache) — alte Treffer können einen Restore nicht überleben. Während des Neuaufbaus laufen Suchen auf MongoDB. - Budget-Abbruch (zu viele Dokumente, Speicher- oder RSS-Grenze) lässt den Index leer und dauerhaft auf MongoDB; ein Reload startet einen neuen Versuch. @@ -183,10 +223,13 @@ search: | Worker-Queue steht | Provider defekt oder überlastet | `search-worker/status` und `embedding/status` lesen, dann Regenerate mit `force: true` | | Suche über `tag`/`navigation` unscharf | `default` ist `regex`, kein Fuzzy | Erwartung anpassen oder `content`-Indexe nutzen | | `inMemory=ram` erscheint nie im Debug-Header | Globaler Schalter aus, Config ohne `inMemory`, Index noch nicht fertig oder Budget-Abbruch | `search.inMemory.enabled`, `ngram.inMemory.enabled` prüfen, Reload, Budget-Logs lesen | +| Suche mit einem einzelnen Füllwort liefert nichts | `ngram.stopwords` verwirft das Wort aus der Anfrage | Inhaltliches Wort suchen; soll das Wort suchbar bleiben, das Preset weglassen und das Set nur über `words` bauen | +| Ein entferntes Stoppwort liefert weiterhin nichts | Neuaufbau nach dem Entfernen fehlt oder läuft noch | `regenerate-search` anstoßen, `_queue` auf 0 warten | +| `mongo(no-terms)` in `X-Search-Sources` | Combined-Quelle ohne verwertbare Terme (nur Stoppwörter oder zu kurze Begriffe) | Kein Fehler — Anfrage präzisieren | | Nach Restore kurz alte Treffer | Restore erfolgt, Index-Neuaufbau läuft noch | Warten bis `inMemory=ram` wieder erscheint (bis dahin MongoDB-Fallback) | | Combined-Suche liefert kurz eine ältere Reihenfolge | `combinedCache` aktiv, TTL noch nicht abgelaufen | TTL senken oder Cache für kritische Daten abschalten | | `?facet=` an der Suche wirkungslos | Facetten sind eigene Route (`…/_facet`) | [Lesen und suchen](/projekt/abfragen-lesen/) beachten | Weiter: [Lesen und suchen](/projekt/abfragen-lesen/), [Sammlungen anlegen](/projekt/sammlungen-anlegen/), [Felder und Widgets](/projekt/felder-widgets/). -Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 68b4278 2026-10-15 (tibi-server, rankfusion), am 2b091d8 2026-10-13 (tibi-admin-nova, Test-Plan 41-search-index-status), am 63a2848c 2026-10-19 (tibi-server: combinedCache, ngram.inMemory, _count-Fast-Path, Restore-Rebuild) +Verifiziert am e3f92eb 2026-10-14 (tibi-svelte-starter), am 68b4278 2026-10-15 (tibi-server, rankfusion), am 2b091d8 2026-10-13 (tibi-admin-nova, Test-Plan 41-search-index-status), am 63a2848 2026-10-19 (tibi-server: combinedCache, ngram.inMemory, _count-Fast-Path, Restore-Rebuild), am 69fc4d8 2026-10-19 (tibi-server: ngram.stopwords, Regenerate-Endpunkt, mongo(no-terms)), am 9e12a8e 2026-10-19 (tibi-types: ngram.stopwords/queryFilter/inMemory im Schema) diff --git a/src/content/docs/reference/schema-properties.md b/src/content/docs/reference/schema-properties.md index 8c9d9cc..36cec4f 100644 --- a/src/content/docs/reference/schema-properties.md +++ b/src/content/docs/reference/schema-properties.md @@ -400,4 +400,4 @@ Pflicht: `name`. | `permissions` | Liste von string | nein | — | Bare permission names for team-based access (Phase 2). Empty = admin-only in Phase 1. | | `enabled` | boolean | nein | `false` | Whether this snapshot profile is active. Disabled profiles are skipped by the scheduler but can still be triggered manually. | -Generiert aus `tibi-types 3bf8055` (Stand 2026-10-17) — Änderungen an den Schemas dort erfordern `npm run gen:schemas`. +Generiert aus `tibi-types 9e12a8e` (Stand 2026-10-19) — Änderungen an den Schemas dort erfordern `npm run gen:schemas`.