# RiccardoSearch – bessere Suche, steuerbare Ergebnisse

Setzt auf OpenSearch auf (Shopwares eigenes Paket) und ergänzt, was dort fehlt.

## Was drin ist (Etappe 1)

| Baustein | Stand |
| --- | --- |
| **Synonyme und Schreibweisen** | fertig – als ODER-Bedingung in der OpenSearch-Abfrage |
| **Auswertung der Suchanfragen** | fertig – jede Suche mit Trefferzahl, Liste der Suchen ohne Treffer |
| Steuerung der Reihenfolge (anheften, hochziehen, runterstufen) | Tabelle steht, Logik folgt |
| Suchbegriff auf Filter abbilden ("0 mg" → Nikotinstärke 0) | offen |

## Warum die Synonyme nicht in den Suchbegriff gehören

Naheliegend wäre, `elfbar` vor der Suche zu `elfbar elf bar` zu erweitern.
Das verschlechtert aber die Treffer: Shopware schaltet die **Teilwort-Suche ab,
sobald der Begriff aus mehreren Wörtern besteht** (damit „line" nicht in
„Portaline" trifft). Im Test fiel `nikotinsalz` dadurch von 65 auf 38 Treffer.

Deshalb bleibt der Begriff unangetastet, und die Synonyme kommen als eigene
ODER-Bedingung in die Abfrage (`SynonymQueryBuilder`). Nebeneffekt: die
Überschrift der Suchseite zeigt weiterhin genau das, was getippt wurde.

Die Reihenfolge der Dekoratoren ist dabei entscheidend – Shopware registriert
den OpenSearch-Aufbau mit Priorität **-50000**, und in Symfony liegt die
kleinere Zahl außen.

## Befehle

```bash
bin/console riccardo:suche:synonyme                 # Liste
bin/console riccardo:suche:synonyme --startbestand  # Grundstock für unser Sortiment
bin/console riccardo:suche:synonyme elfbar "elf bar"
bin/console riccardo:suche:synonyme --test="0 mg"   # zeigt die Erweiterung
bin/console riccardo:suche:bericht --tage=30        # meistgesucht + ohne Treffer
```

## Gemessen (Entwicklungssystem, 1.546 Artikel)

| Suche | Datenbanksuche | OpenSearch | + Synonyme |
| --- | --- | --- | --- |
| elfbar | 3 | 3 | **42** |
| coil | – | 34 | **219** |
| nikotinsalz | 7 | 65 | **79** |
| verdampferkopf | 25 | 176 | 176 |
| liquide | 11 | 214 | 214 |

## Offen und bewusst so

`0 mg` liefert weiterhin nikotinhaltige Artikel. Als Synonym ist der Begriff
nicht lösbar – er meint eine **Eigenschaft** (Nikotinstärke 0), keine
Wortähnlichkeit. Der Eintrag wurde deshalb wieder abgeschaltet; die Zuordnung
von Suchbegriffen auf Filter kommt in der nächsten Etappe.

## Bedienung im Admin

**Inhalte → Suche**, drei Seiten:

* **Auswertung** – meistgesuchte Begriffe mit Klickrate und, zuoberst, die
  Suchen ohne Treffer. Diese Liste ist der eigentliche Arbeitsvorrat: jede
  Zeile ist entweder ein fehlendes Synonym oder ein fehlender Artikel. Aus
  jeder Zeile heraus lässt sich direkt ein Synonym oder eine Regel anlegen.
* **Synonyme** – direkt in der Liste pflegbar. Beidseitig heißt: die Suche
  funktioniert in beide Richtungen.
* **Regeln** – Artikel anheften (fester Platz auf Seite 1), hochstufen,
  herabstufen, Begriff weiterleiten. Mit Gültigkeitszeitraum, damit
  Aktionsregeln von selbst wieder auslaufen.

Änderungen wirken sofort; der Zwischenspeicher wird beim Speichern geleert.

## Wie die Reihenfolge zustande kommt

Auf die Textrelevanz aus OpenSearch wirken drei Faktoren, alle multiplikativ:

1. Regeln (anheften 8,0 / hochstufen / herabstufen)
2. Verhalten – was zu genau diesem Begriff angeklickt wird, gedeckelt über
   `clickWeight`
3. Sortimentslogik – Verkaufszahlen (`salesWeight`) und ein Abschlag für
   nicht lieferbare Artikel (`unavailableFactor`)

Multiplikativ bewusst: ein Artikel, der zum Begriff nicht passt, hat als
Ausgangswert null und wird auch vom höchsten Faktor nicht nach vorn gespült.
Deshalb bringt „hochstufen“ mit Faktor 2 einen Artikel nicht zwingend auf
Platz 1 – er verdoppelt seine Punktzahl. Wer einen festen Platz will, nimmt
**anheften**.

**Fallstrick, einmal teuer bezahlt:** die Verkaufszahlen gingen zunächst mit
`log1p` ein. `log1p(0)` ist null, und weil multipliziert wird, hatte damit
jeder Artikel ohne Verkäufe die Punktzahl null – die gesamte Trefferliste war
unsortiert, ohne dass eine Fehlermeldung darauf hingewiesen hätte. Jeder
Modifikator, der null werden kann, ist hier verboten; es gilt `log2p`.

## Warum steht ein Artikel nicht vorn?

```bash
bin/console riccardo:suche:erklaeren "aroma" --anzahl=20 --abfrage
```

Zeigt die tatsächlich gestellte Abfrage und die Punktzahl je Treffer. Damit
lässt sich nachweisen, ob eine Regel greift: ohne Regel stand der Testartikel
auf Platz 38 mit 1216 Punkten, mit Faktor 2 auf Platz 9 mit 2432 Punkten.
