W swojej dotychczasowej pracy związanej z Lucene i Solr spotkałem się z różnymi zapytaniami. O ile w przypadku Lucene programista z reguły wie co chce osiągnąć i zastanawia się nad optymalnym rozwiązaniem, o tyle w przypadku Solr już tak nie jest. Solr jest produktem z którego teoretycznie może skorzystać każdy, zarówno osoba nie znająca języka Java, taka, która nie posiada szerokiej i specjalistycznej wiedzy technicznej, jak również programista. Właśnie ze względu na to, że Solr jest produktem, który bardzo łatwo uruchomić i z niego skorzystać, wiele osób nie zadaje sobie trudu związanego z przeczytaniem dokumentacji, czy przejrzeniem listy dyskusyjnej użytkowników. Co za tym idzie, ludzie Ci, wcześniej czy później popełniają błędy – błędy wynikające z różnych braków – braku wiedzy na temat Solr, umiejętności, doświadczenia, czy ze zwykłego braku czasu i napiętych terminów. Chciałbym dzisiaj przedstawić kilka podstawowych błędów przy składaniu zapytań i jak ich uniknąć.
1. Brak wykorzystania filtrów
Jednym z podstawowych błędów jakie spotykam co jakiś czas jest brak wykorzystania filtrów, czyli prosto mówiąc brak wykorzystania parametru fq w zapytaniach. Pamiętajmy o tym, filtry to nasi przyjaciele 😉 Dzięki filtrom cache wykorzystywany jest bardziej optymalnie. Filtry nie wpływają na ważność dokumentu w kontekście zapytania (współczynnik score), a co za tym idzie, możemy wykonywać zawężania bez obawy o zmianę wartości score poszczególnych dokumentów (przydatne np. w sklepach e-commerce w przypadku zawężania grup produktów).
2. Warunki logiczne i parametr q
Kolejny z „grzechów” jakie spotykam dość często, dość mocno związany z poprzednim punktem. Nie jest to może błąd w dosłownym tego słowa znaczeniu, aczkolwiek, jest to obszar, gdzie prosta zmiana będzie miała spory wpływ na wydajność. Zakładając, że domyślnym operatorem logicznym jest OR, wyobraźmy sobie zapytanie w postaci q=(java+wzorce+projektowe)+AND+kategoria:ksiazki+AND+promocja:true+AND+wydawnictwo:ABC
. Zapytanie to jest jak najbardziej poprawne z punktu widzenia logiki aplikacji, gdzie mamy dostać odpowiednią grupę wyników wyszukiwania. Co jednak, jeżeli chcemy, aby także optymalnie wykorzystywało cache Solr, a tym samym wykonywało się szybciej. Odpowiedź jest dość prosta – należy przenieść niektóre z warunków do filtrów. Zmieniając przedstawione zapytanie na q=java+wzrce+projektowe&fq=kategoria:ksiazki&fq=promocja:true&fq=wydawnictwo:ABC
Solr skorzysta z dwóch rodzajów cache – queryDocumentCache w celu pobrania dokumentów dla zapytania z parametru q oraz z filterCache dla każdego z filtrów. Zmiana zapytania w pokazany sposób pozwoliła na wykorzystanie dodatkowego cache oraz na uporządkowanie wpisów w queryDocumentCache (ze względu na skrócenie zapytania w parametrze q).
3. Ogromne ilości facet query
Kolejny „grzech” związany z obsługą grup dokumentów. Dość często, szczególnie, w przypadku aplikacji które mają dużą ilość parametrów pozwalających kategoryzować produkty spotykałem zapytania z dużą ilością parametrów facet.query. Zapytania te używane były do wyświetlania grup dokumentów – według ceny, lokalizacji, grup i tak dalej. Dobrym przykładem jest grupowanie po cenie, gdzie klient biznesowy może określać przedziały cenowe dla poszczególnych kategorii i na tym podstawie grupować produkty. Pamiętajmy, że dołożenie do zapytania 100, czy 200 razy parametru facet.query odbija się na wydajności i to czasami dość znacznie. Jeżeli interesuje nas szybka reakcja Solr nie możemy stosować takich zapytań. W takich wypadkach zawsze proponuję modyfikację struktury indeksu, czyli stworzenie odpowiednich przedziałów w importerze aplikacji i przekazywanie do Solr identyfikatorów tych przedziałów, które tłumaczy aplikacja. Pozwala to na zlikwidowanie dziesiątek, czy setek facet.query na rzecz jednego parametru facet, po odpowiednim polu (oczywiście w przykładzie z grupami zależnymi od kategorii mogą to być pola dynamiczne). Wiąże się z jeszcze jedną niedogodnością – wytłumaczeniem klientowi, dlaczego musi nacisnąć przycisk reindeksacji danych po zmianach przedziałów. Z reguły jednak testy wydajnościowe przy dużym obciążeniu i dużej różnorodności zapytań mówią same za siebie.
4. Limity facetingu
Problem pojawiający się na styku zapytań zadawanych do Solr, a logiki biznesowej jaką realizować ma aplikacja korzystająca z Solr. Przykładem tego „grzechu” jest prosta lista kategorii, którą klient chce mieć wyświetlaną w zależności od strony na jakiej znajduje się użytkownik. Kiedy kategorii jest mało nie mamy problemu, gorzej kiedy tych kategorii jest bardzo dużo. Bardzo często spotykałem się z podejściem ze strony programistów, aby pobierać wszystkie kategorie z Solr (z mocno zwiększonym w stosunku do domyślnego parametrem facet.limit) i dopiero w aplikacji zawężać te wyniki. Według mnie jest to podejście, które może generować problemy – po pierwsze faceting wymaga pamięci, po drugie czasu na przetworzenie i agregację wyników. Co więcej, pobranie wszystkich 50.000 kategorii wraz z licznościami może być bolesne dla Solr. Jeżeli zależy nam na szybkich zapytaniach, starajmy się korzystać z parametru facet.limit rozsądnie i ewentualnie tak budować aplikację, aby możliwe było stronicowanie wyników facetingu i korzystanie tym samym z parametru facet.offset. Jeżeli nie jest to możliwe, to przynajmniej skonfigurujmy tak kontener w którym działa Solr, aby miał wystarczającą ilość pamięci, aby obsłużyć równoległe zapytania i przygotujmy się, że zapytania z dużą wartością facet.limit mogą wykonywać się dłużej, niż inne.
5. Pobieranie niepotrzebnych danych
Bardzo często spotykanym problemem jest pobieranie wszystkich informacji, a nie tylko tych, które są nam potrzebne. Oczywiście problem nie dotyczy wdrożeń, gdzie Solr serwuje informacje takie jak na przykład identyfikator produktu i tylko identyfikator. Jednak, duża ilość wdrożeń z którą miałem do czynienia opierała się prawie w całości na Solr, a co za tym idzie indeks składał się z wielu pól z atrybutem stored ustawionym na wartość true. Programiści korzystający z Solr bardzo rzadko korzystali z parametru fl oraz możliwości ograniczania pól, które są zwracane odpowiedzi. W ekstremalnych przypadkach prowadziło to do problemów z ilością danych, jakie trzeba było przesyłać przez sieć.
6. Wiele zapytań do uzyskania liczności grup dokumentów
W niektórych wdrożeniach ważniejsze od standardowego wyszukiwania, gdzie użytkownik wpisuje frazę jaką chce znaleźć, jest przechodzenie do dokumentów za pomocą nawigacji, na przykład przy pomocy działów, kategorii, podkategorii i tak dalej. Bardzo często oprócz na przykład nazwa kategorii podaje się także informacje takie jak ilość dokumentów w tej kategorii, czyli np. produktów. Spotkałem się z przypadkami, gdzie ilości dokumentów w poszczególnych kategoriach były pobierane za pomocą oddzielnych zapytań. Skutek – wyświetlenie 100 kategorii na formatce skutkowało taką samą ilością zapytań do Solr. Nie róbmy tego – jeżeli trzeba zmodyfikujmy indeks Solr tak, aby możliwe było wykorzystanie mechanizmów facetingu. Może w danym momencie będzie to więcej pracy, ale na pewno w dłuższej perspektywie czasu się to opłaci.
Kilka słów na koniec
Należy pamiętać, że są to tylko przykłady, które uważam za dość powszechne, a przynajmniej na które dość często natknąłem się podczas pracy. Nie są to na pewno wszystkie błędy popełniane przy korzystaniu z Solr, jednak mam nadzieję, że przynajmniej w pewnym stopniu uwypuklają stronę w którą należy spoglądać generując zapytania do Solr, czy optymalizując już istniejące.