Solr i autocomplete (cz. 1)

Niemal każdy z nas widział jak wygląda funkcjonalność autocomplete, czyli podpowiadanie użytkownikowi wyrazów lub całych fraz wyszukiwania. Nic dziwnego więc, że także Solr udostępnia mechanizmy przy pomocy których możemy zbudować takie funkcjonalności. W dzisiejszym wpisie pokażę w jaki sposób można zbudować podpowiadanie przy pomocy facetingu.

Indeks

Załóżmy, że chcemy podpowiadać użytkownikowi nazwy produktów w sklepie internetowym. Załóżmy, że nasz indeks składa się z następujących pól:

<field name="id" type="string" indexed="true" stored="true" multiValued="false" required="true"/>
<field name="name" type="text" indexed="true" stored="true" multiValued="false" />
<field name="description" type="text" indexed="true" stored="true" multiValued="false" />

A typ text zdefiniowany jest w następujący sposób:

<fieldType name="text" class="solr.TextField" positionIncrementGap="100"> 
 <analyzer>
  <tokenizer class="solr.WhitespaceTokenizerFactory"/>
  <filter class="solr.WordDelimiterFilterFactory" generateWordParts="1" generateNumberParts="1" catenateWords="1" catenateNumbers="1" catenateAll="0" splitOnCaseChange="1"/> 
  <filter class="solr.LowerCaseFilterFactory"/>
 </analyzer>
</fieldType>

Konfiguracja

Na początku należy zastanowić się co chcemy uzyskać. Czy chcemy aby podpowiadane były tylko poszczególne wyrazy składające się na frazy, czy może jednak pełne nazwy rozpoczynające się na podane przez użytkownika litery. W zależności od naszych wyborów musimy przygotować odpowiednie pole, na podstawie którego będziemy budować podpowiedzi.

Podpowiadanie pojedynczych wyrazów składających się na nazwę

W przypadku podpowiadania pojedynczych wyrazów powinniśmy podpowiadanie oprzeć o pole, które będzie odpowiednio tokenizowane. W naszym przypadku pole name będzie wystarczające. Należy jednak pamiętać, iż jeżeli chcemy korzystać np. ze stemmingu należałoby zdefiniować kolejny typ, który były go pozbawiony, ze względu na to jak taka analiza działa na zawartość pola.

Podpowiadanie pełnych nazw

W przypadku podpowiadania pełnych nazw produktów potrzebujemy innej konfiguracji pola na którym oprzemy podpowiadanie – najlepsze do tego będzie pole nietokenizowane. Nie możemy jednak wykorzystać do tego celu typu string – ze względu na to. W tym celu definiujemy pole następująco:

<field name="name_auto" type="text_auto" indexed="true" stored="true" multiValued="false" />

Typ ten został zdefiniowany w następujący sposób:

<fieldType name="text_auto" class="solr.TextField">
 <analyzer>
  <tokenizer class="solr.KeywordTokenizerFactory"/>
  <filter class="solr.LowerCaseFilterFactory"/>
 </analyzer>
</fieldType>

Aby nie było konieczności modyfikacji formatu danych dodajemy dodatkowo odpowiednią definicję kopiowania informacji:

<copyField source="name" dest="name_auto" />

Jak to wykorzystać ?

Mając już odpowiednio zaindeksowane dane możemy zacząć korzystanie z Solr. Aby skorzystać z tak przygotowanych danych skorzystać wystarczy dość proste zapytanie:

q=*:*&facet=true&facet.field=POLE&facet.mincount=1&facet.prefix=ZAPYTANIE_UŻYTKOWNIKA

Gdzie:

  • POLE – pole na jakiego podstawie mamy zamiar realizować podpowiadanie. W naszym przypadku name_auto.
  • ZAPYTANIE_UŻYTKOWNIKA – litery, które wpisał użytkownik.

Warto zauważyć parametr rows=0 dodany po to, aby widoczny był tylko wynik facetingu. Oczywiście nie jest to konieczność.

Na przykład:

fl=id,name&rows=0&q=*:*&facet=true&facet.field=name_auto&facet.mincount=1&facet.prefix=dys

Wynik powyższego zapytania mógłby wyglądać następująco:

<response>
 <lst name="responseHeader">
  <int name="status">0</int>
  <int name="QTime">0</int>
 </lst>
 <result name="response" numFound="4" start="0"/>
 <lst name="facet_counts">
  <lst name="facet_queries"/>
  <lst name="facet_fields">
   <lst name="name_auto">
    <int name="dysk twardy">1</int>
    <int name="dysk twardy samsung">1</int>
    <int name="dysk twardy seagate">1</int>
    <int name="dysk twardy toshiba">1</int>
   </lst>
  </lst>
  <lst name="facet_dates"/></lst>
</response>

Dodatkowe możliwości

Warto wspomnieć o dodatkowych możliwościach jakie niesie za sobą ta metoda.

Pierwsza z możliwości to pokazanie użytkownikowi dodatkowych informacji takich jak ilość wyników jaką dostanie użytkownik po wybraniu odpowiedniej podpowiedzi. Jeżeli chcemy pokazywać takie informacje będzie to na pewno ciekawa opcja.

Możliwość wyboru sortowania wyników facetingu, czyli standardowe wykorzystanie parametru facet.sort. W zależności od potrzeb możemy posortować wyniki po ilości dokumentów (domyślne zachowanie, wartość parametru ustawiamy na true) lub alfabetycznie (wartość parametru ustawiamy na false).

Możemy ograniczyć podpowiedzi, aby nie były generowane te, które mają mniejszą, niż określona ilość wyników. Aby skorzystać z tej możliwości należy przekazać w parametrze facet.mincount ilość dokumentów od jakiej mają być pokazywane wyniki.

I jak dla mnie największy plus tej metody – możliwość prostego pobierania podpowiedzi tylko tych, które pasują do zapytania użytkownika oraz dodatkowych parametrów, takich jak np. kategoria w której się znajduje. Na przykład chcemy pokazać podpowiedzi dla użytkownika, który znajduje się w dziale AGD naszego sklepu. Podejrzewamy, że w tej chwili nie będą go interesować produkty typu odtwarzacze DVD i dlatego dodajemy parametr fq=dział:agd (zakładając, że mamy taki dział). Po tak zmodyfikowanym zapytaniu, użytkownik nie dostanie podpowiedzi wygenerowanych z całego indeksu, a zawężone, do tego jaki dział przegląda.

Kilka słów na zakończenie

Jak wiele metod i ta ma swoje plusy i minusy. Plusem tego rozwiązania jest łatwość jego wykorzystania, brak dodatkowych komponentów oraz to, że wyniki podpowiedzi można w bardzo prosty sposób zawężać, aby były generowane tylko z tych dokumentów, które pasują do zapytania wpisanego przez użytkownika. Jako duży plus można zaliczyć pokazywanie ilości dokumentów, jakie dostanie użytkownik wybierając daną podpowiedź (oczywiście przy zachowaniu takich samych parametrów wyszukiwania). Do minusów należy na pewno konieczność posiadania dodatkowych typów i pól obsługujących podpowiedzi, wbrew pozorom dość ograniczone możliwości oraz obciążenie Solr powodowane przez mechanizm facetingu.

W następnym wpisie dotyczącym funkcjonalności autocomplete postaram się rozszerzyć temat i pokazać kolejne metody generowania podpowiedzi za pomocą Solr.

This post is also available in: angielski

This entry was posted on poniedziałek, Październik 18th, 2010 at 07:22 and is filed under Autocomplete, Solr, Tutorial. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.

12 komentarzy to “Solr i autocomplete (cz. 1)”

  1. Piotrek Says:

    dobre:)

  2. gr0 Says:

    Dzięki 😉

  3. lury Says:

    jakoś nie ma searcha na tym blogu o searchu 🙂

  4. negativ Says:

    Trafna uwaga. I póki co nie będzie. Ale testowo włączyłem „toto”, które jest standardowo dostępne w CMSie 🙂

  5. Janek Says:

    „odejrzewamy, że w tej chwili nie będą go interesować produkty typu odtwarzacze DVD i dlatego dodajemy parametr fq=dział:agd (zakładając, że mamy taki dział). Po tak zmodyfikowanym zapytaniu, użytkownik nie dostanie podpowiedzi wygenerowanych z całego indeksu, a zawężone, do tego jaki dział przegląda.”

    Ta opcja dla przykładowego, przedstawionego powyżej, zapytania bez względu na poprawność parametru fq u coś nie działa – lista wyników nie zostaje zawężona. Czy aby na pewno „samotne” fq odfiltrowuje wyniki !?

  6. gr0 Says:

    Te zaszyte w konfiguracji handlerów parametry – proponuję dodać jeszcze dodatkowy parametr – facet.mincount=1 (w sumie do głównego zapytania także), wtedy pokażą się tylko te wyniki, które występują po zawężeniu.

    Dzięki za zwrócenie uwagi, poprawię wpis.

  7. Janek Says:

    Dzięki za szybką odpowiedź.

    Niestety zastosowanie u mnie facet.mincount=1 nic nie daje ze względu na to, że wszystkie zwracane „podpowiedzi” mają count „0” (co nie jest zgodne z rzeczywistymi wynikami dla danej frazy).

    Trochę inaczej zdefiniowałem „name_auto” tzn. u mnie ma ono multiValued=”true” bo sprawa się tyczy tagów a każda treść może mieć ich wiele. Czy to może powodować problem z błędną ilością wyników !?

  8. gr0 Says:

    Przed odpowiedzią na pytanie – z jakiego query parsera korzystasz – ze standard, czy z dismax’a ?

  9. Janek Says:

    standard

  10. gr0 Says:

    Dla pewności sprawdzę jeszcze jak to wszystko zachowuje się przy polach wielowartościowych i wtedy napiszę co i jak.

  11. gr0 Says:

    Moim zdaniem, jeżeli count dla danego tagu masz 0, to nie powinieneś podpowiadać tego. Wielowartościowość nie powinna mieć tutaj znaczenia. Zrobiłem mały test:

    schema.xml:
    <fields>
    <field name=”id” type=”string” indexed=”true” stored=”true” multiValued=”false” required=”true”/>
    <field name=”name_auto” type=”text_auto” indexed=”true” stored=”true” multiValued=”true” />
    </fields>

    data.xml:
    <add>
    <doc>
    <field name=”id”>1</field>
    <field name=”name_auto”>Dysk</field>
    <field name=”name_auto”>twardy</field>
    </doc>
    <doc>
    <field name=”id”>2</field>
    <field name=”name_auto”>Dysk</field>
    <field name=”name_auto”>twardy</field>
    <field name=”name_auto”>samsung</field>
    </doc>
    <doc>
    <field name=”id”>3</field>
    <field name=”name_auto”>Dysk</field>
    <field name=”name_auto”>twardy</field>
    <field name=”name_auto”>toshiba</field>
    </doc>
    <doc>
    <field name=”id”>4</field>
    <field name=”name_auto”>Dysk</field>
    <field name=”name_auto”>twardy</field>
    <field name=”name_auto”>seagate</field>
    </doc>
    </add>

    Zapytanie:
    http://localhost:8983/solr/select?fl=id,name_auto&rows=0&q=*:*&facet=true&facet.field=name_auto&facet.mincount=1&facet.prefix=dys

    Wynik:
    <?xml version=”1.0″ encoding=”UTF-8″?>
    <response>
    <lst name=”responseHeader”>
    <int name=”status”>0</int>
    <int name=”QTime”>0</int>
    <lst name=”params”>
    <str name=”facet”>true</str>
    <str name=”fl”>id,name_auto</str>
    <str name=”facet.mincount”>1</str>
    <str name=”q”>*:*</str>
    <str name=”facet.prefix”>dys</str>
    <str name=”facet.field”>name_auto</str>
    <str name=”rows”>0</str>
    </lst>
    </lst>
    <result name=”response” numFound=”4″ start=”0″/>
    <lst name=”facet_counts”>
    <lst name=”facet_queries”/>
    <lst name=”facet_fields”>
    <lst name=”name_auto”>
    <int name=”dysk”>4</int>
    </lst>
    </lst>
    <lst name=”facet_dates”/>
    </lst>
    </response>

    Jak widzisz, pomimo wielowartościowości Solr nie miał problemów z wyliczeniem ilości dokumentów i wygenerowaniem podpowiedzi. Najlepiej wklej przykład danych i definicję pól ze schemy oraz zapytanie, wtedy będę mógł pomóc bardziej konkretnie.

  12. Solr i autocomplete (cz. 2) | Solr Enterprise Search Says:

    […] poprzedniej części pokazałem w jaki sposób można wykorzystać mechanizm facetingu, aby zrealizować […]