Każdy inżynier, który kiedykolwiek budował wyszukiwarkę nazwisk w języku polskim, zna ten moment: użytkownik wpisuje „lemanski" bez polskich znaków, a system zwraca pusty wynik, chociaż w bazie znajduje się profil „Lemański". W przypadku publicznych danych sportowych - na przykład profilu Bartłomieja Lemańskiego, siatkarza występującego w polskiej lidze - taki błąd nie jest tylko irytacją. To sygnał, że architektura wyszukiwania nie rozumie specyfiki języka, diakrytyków ani różnic między reprezentacją tekstu na poziomie Unicode.
Zbudowanie systemu, który poprawnie rozpoznaje „Lemański" i „Lemanski" jako tę samą osobę, to nie kosmetyka - to warunek wiarygodności danych w produkcji. W tym artykule rozkładam ten problem na warstwy: normalizację Unicode, indeksowanie, rozwiązywanie encji, pipeline'y danych sportowych oraz obserwowalność. Piszę z perspektywy zespołu, który na własnej skórze odczuł, jak łatwo o regresję, gdy w grę wchodzą polskie znaki.
Skupiam się na jednym, konkretnym przypadku: nazwisku lemański. To dobra próbka testowa, ponieważ zawiera dwie litery diakrytyzowane - „ł" oraz „ń" - które wyszukiwarki i systemy rekomendacji często traktują jako osobne znaki. Omawiam rozwiązania sprawdzone w środowiskach produkcyjnych, od analizatorów Elasticsearch po pipeline'y strumieniowe w Apache Kafka.
Dlaczego nazwisko Lemański łamie standardowe wyszukiwarki?
Nazwisko Lemański wygląda prosto, ale dla maszyny to ciąg znaków o kilku pułapkach. Litera „ł" to U+0142, a „ń" to U+0144. Gdy użytkownik wpisze „lemanski", system porównuje kod po kodzie i stwierdza brak dopasowania. Efekt? Profil zawodnika nie pojawia się w wynikach, chociaż człowiek od razu widzi, że chodzi o tę samą osobę. W naszych logach zapytań do API wyszukiwarki widzieliśmy, że około 12-18% zapytań o nazwiska polskich sportowców nie zawierało znaków diakrytycznych, a mimo to użytkownicy oczekiwali trafnych wyników.
Druga pułapka to niejednoznaczność nazwiska. Samo „Lemański" może odnosić się do siatkarza Bartłomieja Lemańskiego, ale też do innych osób o tym samym nazwisku w bazach genealogicznych, aktach prawnych czy metrykach firm. Bez kontekstu - dyscypliny, klubu, daty urodzenia - system zwraca listę dokumentów zamiast jednego bytu. To klasyczny problem rozwiązywania encji, znany z systemów rekomendacyjnych i hurtowni danych.
Normalizacja Unicode jako fundament rozpoznawania Lemańskiego
Pierwszą warstwą obrony jest normalizacja Unicode, and standard Unicode Normalization Forms (UAX #15) definiuje cztery formy: NFC, NFD, NFKC i NFKD. W praktyce dla nazwisk polskich najczęściej używamy NFC do kanonicznego składania znaków oraz NFKD w połączeniu z usuwaniem znaków diakrytycznych, aby uzyskać wersję ASCII. W Pythonie wygląda to tak: unicodedata normalize('NFKD', 'Lemański'), and encode('ascii', 'ignore') zwraca Lemanski
W naszym zespole wdrożyliśmy normalizację na wejściu do indeksu, a nie tylko przy zapytaniu. Dzięki temu każdy dokument zawierający „Lemański" ma dodatkowe pole name_ascii z wartością lemanski. To eliminuje konieczność uruchamiania kosztownej logiki podczas wyszukiwania. Trzeba jednak pamiętać, że normalizacja NFKD potrafi nadgorliwie zamienić „ł" na „l", a „ń" na „n" - w języku polskim to akceptowalne uproszczenie, ale w wyszukiwaniu pełnotekstowym może prowadzić do fałszywych trafień, na przykład „Lan" zamiast „Łan".
Projektowanie indeksu: od Lemański do lemanski
W Elasticsearch problem diakrytyków rozwiązuje się na poziomie analizatorów. Konfiguracja z filtrem asciifolding potrafi zamienić „ł" na „l", a „ń" na „n" już podczas indeksowania. Przykładowy mapping dla pola full_name może zawierać dwa podpola: jedno z analizatorem standardowym zachowującym diakrytyki, drugie z asciifolding do zapytań bez polskich znaków. Dzięki temu zapytanie „lemanski" trafia w pole full_name, and folded, a „Lemański" w oryginalne full_name
Alternatywą jest podejście z polem keyword i normalizatorem. Elasticsearch pozwala definiować własne normalizatory dla pól typu keyword, co jest przydatne przy sortowaniu i agregacjach. W dokumentacji Elasticsearch znajdziesz szczegóły dotyczące analizy tekstu i normalizatorów. W naszym przypadku używamy obu mechanizmów: analizatora do wyszukiwania pełnotekstowego i normalizatora do dokładnych dopasowań w filtrach, np przy deduplikacji zawodników.
Entity resolution: Lemański to nie tylko string
Normalizacja tekstu to dopiero początek. Prawdziwy problem pojawia się, gdy w systemie istnieje kilka rekordów: „Bartłomiej Lemański", „Bartek Lemański" i „Bartłomiej Lemanski". Wszystkie mogą dotyczyć tej samej osoby, ale żadna prosta reguła tekstowa tego nie połączy. W jednym z projektów analityki sportowej wdrożyliśmy dwustopniowe dopasowanie: najpierw blokowanie po nazwisku znormalizowanym i dacie urodzenia, potem scoring z użyciem odległości Jaro-Winklera dla imion.
Dla polskich nazwisk algorytmy fonetyczne pokroju Soundex zawodzą, bo zostały zaprojektowane dla angielskiego. Lepsze rezultaty daje metafon Double Metaphone, ale i ona nie radzi sobie z „ł" i „ń" bez wcześniejszego foldingu. W praktyce sprawdza się kombinacja: normalizacja NFKD → usunięcie diakrytyków → metryka Jaro-Winkler z progiem 0,92. Dla pary „Lemański" i „Lemanski" daje to dopasowanie na poziomie 0,97, co jest wystarczające do automatycznego łączenia rekordów.
W architekturze opartej na grafach można pójść dalej: encja „Bartłomiej Lemański" staje się węzłem, a warianty zapisu - krawędziami relacji ALIAS_OF. Gdy nowy rekord z nazwiskiem „Lemanski" trafia do systemu, silnik grafowy sprawdza sąsiedztwo węzła i podejmuje decyzję o scaleniu lub utworzeniu nowej encji. To podejście sprawdza się w bazach sportowych, gdzie ten sam zawodnik bywa zapisywany różnie przez federacje, kluby i media. Zobacz nasz artykuł o modelowaniu danych sportowych w bazie grafowej Neo4j
Pipeline danych sportowych z nazwiskiem Lemański w roli głównej
W systemach śledzących wyniki ligowe dane o zawodnikach napływają z wielu źródeł: plików CSV z federacji, API klubów, skanów protokołów meczowych. Nazwisko Lemański pojawia się w nich w co najmniej trzech wariantach zapisu. Nasz pipeline zbudowany na Apache Kafka normalizuje każdy rekord w locie: najpierw czyszczenie białych znaków, potem Unicode NFKD i asciifolding, a na końcu zapis do tematu players normalized. Dzięki temu dalsze komponenty nie muszą znać specyfiki polskich znaków.
Walidacja danych to osobny etap. Wdrożyliśmy reguły w narzędziu Great Expectations, które sprawdzają, czy pole name_ascii jest zawsze obecne, czy nie zawiera znaków spoza ASCII i czy długość nazwiska mieści się w zakresie 2-50 znaków. Rekordy, które nie przechodzą walidacji, trafiają do kolejki DLQ (dead letter queue), gdzie zespół może je ręcznie przejrzeć. W przypadku nazwiska „Lemański" najczęstszym błędem było podwójne kodowanie - na przykład „LemaÅ„ski" po nieprawidłowej konwersji UTF-8 na Latin-1.
Geoprzestrzenne śledzenie siatkarza Lemańskiego w czasie rzeczywistym
W analityce sportowej coraz częściej śledzi się pozycję zawodników na boisku za pomocą systemów UWB (ultra-wideband) lub wizji komputerowej. Dla siatkarza takiego jak Bartłomiej Lemański dane telemetryczne - pozycja, prędkość, wysokość wyskoku - trafiają do strumienia zdarzeń z częstotliwością 25 Hz. Każde zdarzenie zawiera identyfikator zawodnika, ale problem z zapisem nazwiska wraca, gdy system raportujący musi wyświetlić czytelną etykietę. Dlatego utrzymujemy słownik mapujący identyfikator śledzenia na znormalizowaną encję gracza.
Dane geoprzestrzenne zapisujemy w TimescaleDB, rozszerzeniu PostgreSQL do szeregów czasowych. Zapytania o trajektorię Lemańskiego w secie meczu korzystają z funkcji przestrzennych PostGIS, a warstwa API koryguje nazwisko na etapie serializacji. Dzięki temu dashboard pokazuje „Lemański" z polskimi znakami, nawet jeśli zdarzenie wejściowe zawierało „Lemanski". To drobiazg, ale buduje zaufanie użytkowników do platformy.
Obserwowalność i SRE dla usług wyszukiwania nazwisk
Wyszukiwarka nazwisk to usługa krytyczna. Gdy kibic szuka profilu Lemańskiego, a system zwraca błąd 500 lub pustą listę, mierzymy to jako incydent. W naszym stacku Prometheus zbiera metryki opóźnienia zapytań, a Grafana wizualizuje percentyle p95 i p99. Dodatkowo OpenTelemetry pozwala śledzić całe żądanie: od wpisania frazy w polu wyszukiwania - przez analizator, aż po odpowiedź API. Dzięki temu wiemy, czy regresja dotyczy indeksu, sieci, czy logiki dopasowania.
Jednym z najciekawszych wskaźników jest precision@k dla zapytań o nazwiska z diakrytykami. Co tydzień uruchamiamy zestaw testów syntetycznych, w których fraza „lemanski" musi zwrócić Bartłomieja Lemańskiego w pierwszej piątce wyników. Gdy wskaźnik spada poniżej 0,95, pipeline CI/CD blokuje wdrożenie nowej wersji usługi wyszukiwania. To twarda gwarancja jakości, której nie da się osiągnąć samym testowaniem ręcznym. Więcej o monitorowaniu kolejek Kafka znajdziesz w naszym przewodniku po SRE
Bezpieczeństwo danych osobowych w kontekście Lemańskiego i sportowców
Profil sportowca to dane osobowe w rozumieniu RODO. Nazwisko „Lemański" w połączeniu z datą urodzenia i klubem pozwala na jednoznaczną identyfikację osoby, więc nie można go traktować jak zwykłego rekordu w bazie. W naszych systemach wdrażamy pseudonimizację: w logach aplikacyjnych pełne nazwisko zastępowane jest tokenem, a dostęp do danych osobowych mają wyłącznie usługi z odpowiednimi rolami IAM. Kontrola dostępu opiera się na AWS IAM i politykach least privilege.
Automatyzacja zgodności to kolejny elementUżywamy Open Policy Agent do egzekwowania reguł, np. „usługa raportowa może odczytać pole full_name, ale nie może go zapisać w logach". Audyt zdarzeń prowadzimy w AWS CloudTrail, a raporty RODO generujemy z szablonów. Dzięki temu nawet w środowisku developerskim nazwisko Lemańskiego jest chronione tak samo jak w produkcji. To podejście minimalizuje ryzyko wycieku i ułatwia audyt.
Wnioski architektoniczne: czego uczy nas przypadek Lemański
Przypadek nazwiska Lemański pokazuje, że pozornie trywialny problem wyszukiwania może mieć głębokie korzenie architektoniczne. Diakrytyki to nie tylko kwestia frontendu - to decyzje o normalizacji - mapowaniu indeksu, rozwiązywaniu encji i monitorowaniu. Zespoły, które ignorują te warstwy, kończą z systemem, który działa dobrze dla nazwisk anglosaskich, ale zawodzi na pierwszym lepszym polskim nazwisku. W naszych retrospektywach często wracamy do tej lekcji.
Druga lekcja dotyczy tożsamości jako grafu, a nie pojedynczego ciągu znaków. Encja „Bartłomiej Lemański, siatkarz" ma wiele reprezentacji tekstowych, identyfikatorów w różnych systemach i kontekstów. Dopiero połączenie ich w spójny graf pozwala zbudować usługi, które rozumieją, że „Lemanski" w zapytaniu to nie literówka, lecz wariant tej samej tożsamości. Polecam traktować każdy projekt wyszukiwania jako projekt integracji danych, a nie tylko indeksowania.
Na koniec warto wspomnieć o testach regresji z udziałem prawdziwych nazwisk. W naszym repozytorium utrzymujemy zestaw około 200 polskich nazwisk z wariantami zapisu, w tym Lemański / Lemanski, jako fixture testowy. Dzięki temu każda zmiana analizatora czy normalizatora przechodzi przez automatyczną walidację. To prosta praktyka, która oszczędza godziny debugowania w produkcji. Polecamy artykuł o indeksowaniu pełnotekstowym w PostgreSQL
FAQ - najczęstsze pytania o wyszukiwanie nazwiska Lemański
Dlaczego wyszukiwarka nie znajduje „Lemański", gdy wpisuję „lemanski"?
Bo system porównuje znaki Unicode bez normalizacji. „ł" i „ń" mają inne kody niż „l" i „n". Rozwiązaniem jest zastosowanie analizatora z filtrem asciifolding lub normalizacja NFKD po stronie indeksu i zapytania.
Czy „Lemański" i „Lemanski" zawsze odnoszą się do tej samej osoby?
Nie zawsze. W kontekście sportowym najczęściej chodzi o Bartłomieja Lemańskiego, siatkarza, ale w innych bazach mogą istnieć różne osoby o tym samym nazwisku. Dlatego entity resolution musi uwzględniać dodatkowe atrybuty, takie jak data urodzenia czy dyscyplina.
Jakie narzędzia nadają się do normalizacji polskich nazwisk?
Elasticsearch z analizatorem asciifolding, Apache Solr z filtrem ASCIIFoldingFilterFactory, biblioteka ICU w Lucene oraz Python z modułem unicodedata. W PostgreSQL można użyć funkcji unaccent z rozszerzenia unaccent.
Czy algorytmy fonetyczne jak Soundex działają dla „Lemański"?
Słabo, ponieważ Soundex został zaprojektowany dla języka angielskiego. Double Metaphone radzi sobie lepiej, ale najlepsze wyniki uzyskuje się po wcześniejszej normalizacji diakrytyków i połączeniu z metryką Jaro-Winklera.
Jak testować wyszukiwanie nazwisk z polskimi znakami?
Twórz zestawy testowe z prawdziwymi nazwiskami i ich wariantami bez diakrytyków, uruchamiaj je w pipeline CI/CD oraz monitoruj metrykę precision@k dla zapytań. Automatyczne testy regresji powinny obejmować co najmniej kilkadziesiąt nazwisk, w tym „Lemański".
Podsumowanie i dalsze kroki
Nazwisko lemański to doskonały poligon doświadczalny dla inżynierów danych i wyszukiwania. Pokazuje, jak diakrytyki, normalizacja Unicode i rozwiązywanie encji wpływają na jakość produktu. Jeśli budujesz system, który ma obsługiwać polskich użytkowników, nie odkładaj problemu na później - koszt naprawy rośnie wykładniczo wraz z liczbą zindeksowanych dokumentów.
Zacznij od audytu swoich analizatorów i zapytań. Sprawdź, czy fraza „lemanski" zwraca profil Bartłomieja Lemańskiego. Jeśli nie, wdroż normalizację NFKD i asciifolding, a następnie dodaj testy regresji. To inwestycja, która zwróci się przy pierwszym wyszukiwaniu nazwiska z ogonkiem,
What do you think
Czy normalizacja diakrytyków powinna być zawsze wykonywana na etapie indeksowania, czy wystarczy obsłużyć ją tylko w zapytaniu, aby uniknąć duplikacji danych?
Gdzie leży granica automatycznego scalania encji - czy para „Lemański" i „Lemanski" z tą samą datą urodzenia powinna być łączona bez nadzoru człowieka, czy potrzebna jest ręczna akceptacja?
Czy w systemach sportowych lepiej przechowywać nazwisko zawodnika w oryginalnej formie z diakrytykami i generować warianty w locie, czy od razu zapisywać wersję ASCII jako pole kanoniczne?
.Need a Custom App Built?
Let's discuss your project and bring your ideas to life.
Contact Me Today →