fix(discovery): sort source_contributions by rank on read

MergePendingSources re-aggregates the jsonb array with
ORDER BY source_name for DB determinism, but the admin UI treats
index 0 as "Rang 1 = winning source." Legacy auto-merged rows were
therefore surfacing mittelalterkalender (alphabetically first) as
Rang 1 instead of the actual rank-1 source mittelaltermarkt_online.

- Export crawler.SourceRank (was unexported rankOf) so other packages
  in the discovery domain can reference the canonical rank map.
- scanDiscoveredMarket: sort.SliceStable SourceContributions by rank
  after unmarshal. Every read path now sees contributions in rank
  order regardless of how they were persisted; legacy rows
  self-correct on next read, no migration needed.
This commit is contained in:
2026-04-24 13:38:23 +02:00
parent 0d2c9c0f7f
commit a2dffcb112
2 changed files with 12 additions and 2 deletions

View File

@@ -95,7 +95,7 @@ func mergeKey(r RawEvent) string {
func mergeGroup(raws []RawEvent) MergedEvent {
// Sort by source rank so "first non-empty" == "best source's value".
sort.SliceStable(raws, func(i, j int) bool {
return rankOf(raws[i].SourceName) < rankOf(raws[j].SourceName)
return SourceRank(raws[i].SourceName) < SourceRank(raws[j].SourceName)
})
m := MergedEvent{}
@@ -176,7 +176,7 @@ func mergeGroup(raws []RawEvent) MergedEvent {
return m
}
func rankOf(name string) int {
func SourceRank(name string) int {
if r, ok := sourceRank[name]; ok {
return r
}

View File

@@ -6,12 +6,14 @@ import (
"encoding/json"
"errors"
"fmt"
"sort"
"time"
"github.com/google/uuid"
"github.com/jackc/pgx/v5"
"github.com/jackc/pgx/v5/pgxpool"
"marktvogt.de/backend/internal/domain/discovery/crawler"
"marktvogt.de/backend/internal/domain/discovery/enrich"
)
@@ -350,6 +352,14 @@ func scanDiscoveredMarket(s scanner) (DiscoveredMarket, error) {
if d.SourceContributions == nil {
d.SourceContributions = []SourceContribution{}
}
// Persisted order (alphabetical from MergePendingSources) doesn't match the
// rank order the UI relies on for "Rang 1 = winning source". Sort by rank
// here so every read path sees contributions in rank order regardless of
// how they were stored.
sort.SliceStable(d.SourceContributions, func(i, j int) bool {
return crawler.SourceRank(d.SourceContributions[i].SourceName) <
crawler.SourceRank(d.SourceContributions[j].SourceName)
})
return d, nil
}