Such-Ranking existiert in Produktion gar nicht: MBrianReader.Search ist flacher Substring-Match, sortiert nach slug #10
Reference in New Issue
Block a user
No description provided.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
projax hat zwei Read-Backends, und nur der ungenutzte kann ranken:
store/store.go:891(Legacy,projax.items_unified): sauberes Bucket-Ranking — exact-slug → title-prefix → title-contains → alias → content.store/mbrian.go:670(MBrianReader.Search): flacher Substring-Match, sortiert nachslug. Kein Ranking, keine Buckets.Produktion läuft
PROJAX_BACKEND=mbrian. Das Bucket-Ranking, das an mehreren Stellen als Vertrag zitiert wird, ist in Produktion also schlicht nicht vorhanden. Ein Treffer imcontent_mdsteht gleichberechtigt neben einem exakten Slug-Treffer; die Reihenfolge entscheidet das Alphabet.Gefunden von
apollo(#7). Ich (head) hatte in apollos Brief genau dieses Bucket-Ranking als „fertige und korrekte Read-Seite" zitiert — aus der falschen Datei. apollo hat gegengeprüft statt es zu glauben. Derselbe Fehler war mir schon bei #4 unterlaufen (dort hatte ich einen Funktionsnamen aus dem Issue statt aus dem Code gegriffen).Warum das zählt
Die Suche ist eine der wenigen Stellen, an denen m tippt statt klickt. Ohne Ranking gewinnt bei „mental" nicht das Projekt
health.mental, sondern was der Slug-Sortierung nach zufällig oben landet. Mit dem neuen Alias-Write-Path (#7) wird das relevanter: Aliase sind gerade erst beschreibbar geworden, und ein Alias-Treffer ist ein starkes Signal — im Legacy-Pfad war er bewusst Bucket 3, im mbrian-Pfad ist er gleichwertig mit einem beiläufigen Wort im Fließtext.Entscheidung für m
MBrianReader.Searchbekommt dieselben Buckets wie der Legacy-Pfad. Damit stimmt der dokumentierte Vertrag wieder, undfindfühlt sich an wie gedacht.Was nicht bleiben sollte: der jetzige Zustand, in dem die Doku ein Ranking verspricht, das es nicht gibt.
Nebenbefund (eigentliche Lehre)
Solange
PROJAX_BACKENDzwei Backends selektiert, ist jedesfile:line-Zitat ohne Backend-Angabe wertlos — es kann aus dem Pfad stammen, der gar nicht läuft. Billiger Gegen-Check, der beide meiner Fehler heute verhindert hätte: die id gegen beide Tabellen abfragen, bevor man einen Read-Pfad zitiert. (health.mentalhat z.B. null Zeilen initems_unified.) Gehört indocs/design.md, wenn (a) gewählt wird.Refs
store/mbrian.go:670— der Pfad, der in Prod läuftstore/store.go:891— der Pfad, der ranken kann, aber nicht läuft(a) umgesetzt — Buckets laufen jetzt auf dem Backend, das auch wirklich läuft
Commit:
22931ddc5eBranch:
mai/zeus/issue-10-such-rankingBefund zuerst bestätigt, dann gebaut
Beide Behauptungen aus dem Issue stimmen inhaltlich — die Zeilennummern nicht:
store/mbrian.go:670Searchsteht auf 634; 670 istitemMatchesSubstringstore/store.go:891Store.Searchsteht auf 910Die Semantik war exakt wie beschrieben: flacher Substring-Match,
sort by slug, kein Ranking.Der Bug, live in Produktion reproduziert
Nicht theoretisch — via MCP
searchgegen prod, vor dem Fix:mental<projaxim Alphabet, also gewinnt das Alphabet. Genau der im Issue beschriebene Schaden, an einem echten Query.Was gebaut wurde
Semantik portiert, nicht das SQL: exact-slug → title-prefix → title-contains → alias → content. Tie-Break wie im Legacy-
order by: primärer Pfad (pfadlos zuletzt), dann Slug.Drei Dinge, die beim Portieren aufgefallen sind:
WHERElässt sie nie zu, der mbrian-Pfad hat immer auf sie gematcht. Ich habe sie behalten (sonst schrumpft die Trefferliste stillschweigend), aber unter allen benannten Buckets einsortiert — das ist die Bedeutung vonelse 5im SQL.itemMatchesSubstringist jetzt übersearchRankdefiniert. Der List-Filter (ListFiltered,f.Q) und das Ranking können damit nicht mehr auseinanderdriften — genau der Driftmodus, der dieses Issue verursacht hat.limitschon beim Sammeln ab, also entschied Go's Map-Iterationsreihenfolge, wer es in die Liste schafft. Bei kleinemlimitkonnte der exakte Slug-Treffer komplett rausfallen. Jetzt wird erst gerankt, dann geschnitten.Verifikation — die Falle war gestellt
Der Hinweis war berechtigt:
Psychologiesteht auch inmental'scontent_md("Notizen zu m's Psychologie. Alias: Psychologie."). Ein Alias-Bucket-Treffer und ein Content-Bucket-Treffer sehen damit von außen identisch aus — ein Test darauf kann nicht fehlschlagen. Die Tests nutzen deshalb ein alias-only Token (zzqx), ohne prod-Daten anzufassen.Zweite Falle, selbst gestellt und entschärft: mein erster Test hatte den Comparator nachgebaut — er hätte den Produktionscode gar nicht geprüft. Deshalb ist die Sortierung nach
rankSearchResultsextrahiert; Test undSearchrufen dieselbe Funktion.Jeder Test wurde mutationsgeprüft — Code kaputtmachen, Rot sehen:
alias hit must rank above content hitorder = [aaa-content bbb-alias … zzqx], want [zzqx …]limit 1 = [aaa-content], want [zzqx]Zusätzlich gegen den echten mBrian-Graph gelaufen:
search "projax"liefert mit dem Fixprojaxvormental.go build ./...undgo vet ./...sauber. Die Failures inTestParity*,TestProjectFilter*,TestTimeline*,TestBackfillTagsFromAreahabe ich pergit stashgegen sauberen HEAD gegengeprüft — identisch, meine Änderung fügt keinen einzigen hinzu. (Hübsches Detail:TestParity*schlägt fehl, weilitems_unifiedveraltet ist — der Befund dieses Issues, als Testfailure.)Doku
docs/design.md§2.6 (Vorbild: §2.5): die Bucket-Tabelle als ein Vertrag, der auf dem Backend gelten muss, dasPROJAX_BACKENDgerade auswählt — plus der eigentliche Nebenbefund aus dem Issue: solange zwei Read-Pfade existieren, ist einfile:line-Zitat ohne Backend-Angabe wertlos. Gegen-Check ist billig: die id gegen beide Stores abfragen (mental: null Zeilen initems_unified, wird in prod trotzdem sauber ausgeliefert).Für m
Eine Kleinigkeit fürs Protokoll: der Slug ist
mental(Titel "Mental", Pfadhealth.mental) — nichthealth.mental, wie Issue und Brief schreiben. Ändert nichts am Befund, aber falls jemand danach greppt.Ranking ist nach dem Deploy live prüfbar:
search "projax"mussprojaxzuerst liefern.