Szybkie spojrzenie – FieldCollapsing

FieldCollapsing, czyli inaczej grupowanie wyników wyszukiwania – funkcjonalność nad którą developerzy Lucene/Solr pracowali już od dłuższego czasu trafiła właśnie do repozytorium projektu Solr. Postanowiłem się przyjrzeć, w jaki sposób działa ta funkcjonalność.

Na początek mała informacja, FieldCollapsing dostępny jest tylko w wersji 4.0, czyli w wersji rozwojowej kodu projektu Solr i raczej mało prawdopodobnym jest przeniesienie tej funkcjonalności do wersji 3.X.

FieldCollapsing, czyli co ?

Wyobraźmy sobie, iż nasz indeks zawiera informacje o firmach z różnych miast. Chcemy pokazać użytkownikowi po jednej (lub np. dwie, czy trzy) firmie z każdego miasta, oczywiście firmie spełniającej kryteria wyszukiwania. W jaki sposób tego dokonać – wykorzystać właśnie mechanizm FieldCollapsing. Pozwala on na grupowanie zwróconych w wyników wyszukiwania na podstawie zawartości pól. Wyniki wyszukiwania mogą być zgrupowane do pojedynczego dokumentu, bądź stałej ich ilości.

Parametry

Podobnie, jak w przypadku większości funkcjonalności dostępnych w Solr, tak samo zachowanie mechanizmu FieldCollapsing można konfigurować szeregiem parametrów, oto one:

  • group – analogicznie do np. facetingu ustawienie tego parametru na wartość true włącza mechanizm FieldCollapsing. Wartość domyślna parametru to false.
  • group.field – określenie na podstawie jakiego pola ma się odbywać grupowanie.
  • group.func – określenie funkcji, na podstawie wyniku której będzie odbywać się grupowanie.
  • group.limit – ilość wyników jaka ma być zwrócona w poszczególnych grupach. Domyślna wartość parametru to 1.
  • group.sort – parametr określający w jaki sposób sortować dokumenty w ramach grup. Wartość domyślna, to wartość score desc.

Warto podkreślić, iż parametr rows przekazywany do zapytania będzie określał ilość grup jaka ma zostać zwrócona w wynikach wyszukiwania, a nie ilość pojedynczych dokumentów. Zmienia się także zachowanie parametru sort. Parametr ten będzie sortował grupy wyników, a nie poszczególne dokumenty. Grupy będą sortowane na podstawie zawartości pól pierwszych dokumentów tworzących grupy.

Wyniki wyszukiwania

Wyniki wyszukiwania różnią się od tych do których jesteśmy przyzwyczajeni. Są one pogrupowane według parametrów, które przekazaliśmy. Głównym elementem wyników wyszukiwania nie są już poszczególne dokumenty, a grupy dokumentów. Dopiero w ramach grup pokazywane są dokumenty (ich ilość definiuje parametr group.limit). Na przykład, zadając zapytanie:

http://localhost:8983/solr/select/?q=*:*&group=true&group.field=inStock&indent=true

do indeksu, który powstał poprzez zaindeksowanie wszystkich dokumentów w formacie XML z katalogu exampledocs przykładowego wdrożenia dostarczanego z Solr, otrzymujemy następujący 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="group.field">inStock</str>
    <str name="group">true</str>
    <str name="indent">true</str>
    <str name="q">*:*</str>
  </lst>
</lst>
<lst name="grouped">
  <lst name="inStock">
    <int name="matches">19</int>
    <arr name="groups">
     <lst>
        <str name="groupValue">T</str>
        <result name="doclist" numFound="15" start="0">
          <doc>
            <arr name="cat"><str>electronics</str><str>hard drive</str></arr>
            <arr name="features"><str>7200RPM, 8MB cache, IDE Ultra ATA-133</str><str>NoiseGuard, SilentSeek technology, Fluid Dynamic Bearing (FDB) motor</str></arr>
            <str name="id">SP2514N</str>
            <bool name="inStock">true</bool>
            <str name="manu">Samsung Electronics Co. Ltd.</str>
            <date name="manufacturedate_dt">2006-02-13T15:26:37Z</date>
            <str name="name">Samsung SpinPoint P120 SP2514N - hard drive - 250 GB - ATA-133</str>
            <int name="popularity">6</int>
            <float name="price">92.0</float>
            <str name="store">45.17614,-93.87341</str>
            <double name="store_0_d">45.17614</double>
            <double name="store_1_d">-93.87341</double>
            <str name="store_lat_lon">45.17614,-93.87341</str>
          </doc>
        </result>
      </lst>
      <lst>
        <str name="groupValue">F</str>
        <result name="doclist" numFound="4" start="0">
          <doc>
            <arr name="cat"><str>electronics</str><str>connector</str></arr>
            <arr name="features"><str>car power adapter, white</str></arr>
            <str name="id">F8V7067-APL-KIT</str>
            <bool name="inStock">false</bool>
            <str name="manu">Belkin</str>
            <date name="manufacturedate_dt">2005-08-01T16:30:25Z</date>
            <str name="name">Belkin Mobile Power Cord for iPod w/ Dock</str>
            <int name="popularity">1</int>
            <float name="price">19.95</float>
            <str name="store">45.17614,-93.87341</str>
            <double name="store_0_d">45.17614</double>
            <double name="store_1_d">-93.87341</double>
            <str name="store_lat_lon">45.17614,-93.87341</str>
            <float name="weight">4.0</float>
          </doc>
        </result>
      </lst>
    </arr>
  </lst>
</lst>
</response>

Na koniec

Ciekawa funkcjonalność, która na pewno znajdzie zastosowania w niektórych wdrożeniach. Należy jednak pamiętać, iż funkcjonalność ta będzie jeszcze rozwijana. Jak na razie nie ma wsparcia m.in. dla wyszukiwania rozproszonego, czy grupowania po polach wielowartościowych. W tym momencie nie ma sensu przeprowadzanie też testów wydajnościowych, po pierwsze ze względu na zmiany jakie zajdą w samym mechanizmie, a po drugie ze względu na to, iż jest to mocno rozwojowa wersja Lucene i Solr. Niemniej jednak, na pewno będę miał opisywaną funkcjonalność na oku 😉

This post is also available in: angielski

This entry was posted on poniedziałek, Wrzesień 20th, 2010 at 06:27 and is filed under Solr. 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.

5 komentarzy to “Szybkie spojrzenie – FieldCollapsing”

  1. ag Says:

    Witam. Wykonując zapytania z funkcją grupowania, wykorzystują one wszystkie rodzaje cache? (oczywiście w zależnosci od skontruowania zapytania), czy dopiero po ustawieniu opcji group.cache.percent? Czy z doświadczenia są jakieś zależności między ustawieniem wielkości tej opcji a do innych wartości cache? Oraz na co zwrócić uwagę by nie strzelić sobie w stopę?

  2. gr0 Says:

    Parametr group.cache.percent odpowiada za cache drugiego zapytania, które jest wywoływane wewnętrznie przy grupowaniu i dotyczy tylko wyników tego drugiego zapytania. Co do innych cache’y Solr – przy zapytaniach z grupowaniem są one normalnie wykorzystywane, oczywiście w zależności od skonstruowanego zapytania. Wiki dobrze opisuje kiedy warto rozważyć stosowanie group.cache.percent – w przypadku zapytań fuzzy, wildcard, czy dłuższych zapytaniach boolowskich, przy reszcie przypadków ten cache może mieć negatywny wpływ na wydajność i na to bym uważał.

  3. ag Says:

    Czy istnieje jakaś szybka receptura na ominięcie problemu szybkości odpowiedzi na zapytanie w przypadku kiedy używa się opcji ngroups?
    Mój indeks zawiera ok 3mln dokumentów, każdy dokument posiada pole po którym moge grupować dokumenty, grupa może zawierać średnio od 1 do 100 dokumentów. Po stronie aplikacji stronicuję wyniki wg grup(a więc w odpowiedzi mogę otrzymać np 400 dokumentów w 10 grupach – co będzie rezultatem na liście; lista(10 grup) zagłebią się o te 400 dokumentów.
    Aby postronicować wyniki użyłem tutaj właśnie wartości ngroups. Maksymalnie ngroups może być równe 300000;
    W przypadku ngroups=false prędkość odpowiedzi jest elegancka:), z tym że brakuje rozwiązania ilości wszystkich grup(wykorzystywanych do stronicowania).
    Próbowałem użyć parametru facet.field=pole_po_ktorym_grupuje wraz z facet.limit=-1, odpowiedź Solr byłaby zadawalająca, jednak lista może okazać się zbyt długa do obróbki – może tutaj da się wyciągnąć sumę z listy facetingu bez konieczności jej przesyłania i obróbki?

    Zobrazowanie co chce osiągnąć:
    -group1 groupValue = 1
    –doc1 (field1, field2, field_group=1)
    –doc2 (field1, field2, field_group=1)
    –doc3 (field1, field2, field_group=1)
    -group2 groupValue = 2
    –doc4 (field1, field2, field_group=2)
    –doc5 (field1, field2, field_group=2)
    ngroups = 2, matches=5

    Pozdrawiam 🙂

  4. gr0 Says:

    Z tego co wiem to było trochę zmian w kodzie w stosunku do Solr 3.6, który wylicza ilość grup, aczkolwiek jest to dalej problematyczne pod względem wydajności i większość użytkowników po prostu rezygnuje z tej liczności. Dodatkowo należy pamiętać, że w przypadku rozproszonego wyszukiwania wszystkie dokumenty z tej samej grupy muszą być umieszczone w tym samym shardzie, co dodatkowo komplikuje wykorzystanie tego parametru.

  5. ag Says:

    Dodam używam Solr 4.1