Prosta wyszukiwarka zdjęć

Mieliśmy niedawno okazję pomocy przy tworzeniu niekomercyjnego projektu, którego częścią była wyszukiwarka. Jednym z założeń, trochę pobocznych, było wyszukiwanie zdjęć, aby korzystający mogli bardzo szybko dotrzeć do interesujących ich obrazów. Wyszukiwarka miała działać w oparciu o meta dane zapisane w plikach JPEG, tak więc wybór był jasny – Apache Solr oraz Apache Tika.

Założenia

Założenia projektu były dość proste – aplikacja korzystająca z Solr miała móc wyszukiwać zdjęcia po tytule, autorze i innych danych zawartych w EXIF, takich jak przesłona, czas naświetlania, ogniskowa czy wartość ISO. Kolejnym założeniem było to, iż to Solr musi sobie poradzić z wyodrębnieniem tych danych z plików, które dostaje do indeksowania. To tyle jeżeli chodzi o założenia dotyczące wyszukiwania zdjęć.

Struktura indeksu

Struktura indeksu była bardzo prosta i zawierała jedynie najbardziej niezbędne pola. Sama definicja pól wygląda następująco:

<field name="id" type="string" indexed="true" stored="true" required="true" />
<field name="name" type="text" indexed="true" stored="true" />
<field name="author" type="text" indexed="true" stored="true" />
<field name="iso" type="text" indexed="true" stored="true" multiValued="true" />
<field name="iso_string" type="text" indexed="true" stored="true" multiValued="true" />
<field name="aperture" type="double" indexed="true" stored="true" />
<field name="exposure" type="string" indexed="true" stored="true" />
<field name="exposure_time" type="double" indexed="true" stored="true" />
<field name="focal" type="string" indexed="true" stored="true" />
<field name="focal_35" type="string" indexed="true" stored="true" />
<dynamicField name="ignored_*" type="string" indexed="false" stored="false" multiValued="true" />

Pole dynamiczne zostało wprowadzone po to, aby umożliwić ignorowanie danych, które nas nie interesują. Dodatkowo, obecny był copyField kopiujący wartość z pola iso do pola iso_string.

Konfiguracja Solr

Poniżej przedstawiona została konfiguracja handlera w pliku solrconfig.xml:

<requestHandler name="/update/extract" class="solr.extraction.ExtractingRequestHandler">
 <lst name="defaults">
  <str name="uprefix">ignored_</str>
  <str name="lowernames">true</str>
  <str name="captureAttr">true</str>
  <str name="fmap.stream_name">name</str>
  <str name="fmap.artist">author</str>
  <str name="fmap.exif_isospeedratings">iso</str>
  <str name="fmap.exif_fnumber">aperture</str>
  <str name="fmap.exposure_time">exposure</str>
  <str name="fmap.exif_exposuretime">exposure_time</str>
  <str name="fmap.focal_length">focal</str>
  <str name="fmap.focal_length_35">focal_35</str>
 </lst>
</requestHandler>

Kilka słów wyjaśnienia konfiguracji. Parametr uprefix mówi o tym, jaki przedrostek nadać nazwom pól, które nie zostały wymienione w konfiguracji. W przypadku powyższej konfiguracji nazwy pól, które nie zostały wymienione zostaną poprzedzone słowem ignored_. Oznacza, to, że trafią do pola dynamicznego (patrz struktura indeksu) i nie zostaną zaindeksowane (stored=”false” oraz indexed=”false”). Parametr lowernames z wartością true powoduje sprowadzanie nazw pól do małych liter. Parametr captureAttr mówi natomiast o tym, iż mają być pobierane atrybuty pliku. Następne parametry to mapowanie pól zwracanych przez bibliotekę Tika do pól w indeksie. Np. parametr fmap.exif_fnumber o wartości aperture mówi o tym, żeby dane z atrybutu exif_fnumber zostały zapisane w polu aperture w indeksie.

Dodatkowe, potrzebne biblioteki

Aby powyższa zmiana zaczęła działać potrzebujemy jeszcze dodatkowych bibliotek (podobnie, jak w przypadku identyfikacji języka). Z katalogu dist, który znajduje się w dystrybucji Solr kopiujemy plik apache-solr-cell-3.5.0.jar do np. katalogu tikaDir, który tworzymy na tym samym poziomie co katalog webapps w Solr. Oraz dodajemy następującą linię do pliku solrconfig.xml:

<lib dir="../tikaLib/" />

Powyższa linia mówi Solr, aby wczytał wszystkie biblioteki z podanego katalogu. Następnie kopiujemy wszystkie biblioteki z katalogu contrib/extraction/ (także z dystrybucji Solr) do poprzednio stworzonego katalogu tikaDir. Dodatkowe zmiany w pliku solrconfig.xml nie są już potrzebne.

Indeksowanie danych

Przyrost zdjęć, których meta dane należało zaindeksować, wynosił około 10 tysięcy tygodniowo. Zdjęcia były składowane we współdzielonej lokalizacji. Wybieraniem zdjęć do indeksowania zajmował się prosty skrypt powłoki, który dla każdego pliku, który został zakwalifikowany, jako nowy, uruchamiał następujące polecenie:

curl 'http://solrmaster:8983/solr/photos/update/extract?literal.id=9926&commit=true" -F "myfile=@Wisła_2011_10_10.JPG"

Powyższe polecenie wysyła plik o nazwie Wisła_2011_10_10.JPG do handlera /extract i mówi aby wywołać polecenie commit po jego przetworzeniu. Do tego ustawiony zostaje unikalny klucz dla dokumentu (parametr literal.id), który powstanie po przetworzeniu wysyłanego pliku.

Zapytania

Oprócz standardowego filtrowania po autorze,  czy innych atrybutach zdjęcia wymaganie odnośnie wyszukiwania było jedno – ma działać. Stwierdziliśmy, że jeżeli sami mielibyśmy korzystać z powstającej aplikacji, na pewno ważnymi polami byłby autor oraz nazwa pliku. Zapytania, które odpowiadały temu wymaganiu wyglądały mniej więcej, tak jak to poniższe:

q=jan+kowalski+wisła&qf=name^100+author^1000+iso+aperture+exposure_time+focal&defType=dismax

Jak widać zapytanie jest bardzo proste. Dwa pola, spośród obecnych w indeksie są promowane – nazwa zdjęcia oraz jego autor. Znaczenie tych pól zostało określone poprzez dodanie odpowiednich podbić na etapie zapytania. Reszcie przeszukiwanych pól nie określono podbicia, a zatem jego wartość wyniosła 1.

Podsumowując

Opisanie powyżej wdrożenie jest dość proste. Sama aplikacja działa i ma się dobrze, jak również działa wyszukiwanie. Następnymi krokami, jakie należałoby uczynić w przypadku aplikacji, która byłaby wykorzystywana w większym gronie użytkowników, to m.in. tunning JVM i samego Solr. Jednym z elementów poprawiania działania Solr byłoby na pewno strojenie jakości wyników wyszukiwania i wydajności samego rozwiązania. Ale to zostanie opisanie w przyszłości.

 

Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany. Wymagane pola są oznaczone *

We use cookies to personalise content and ads, to provide social media features and to analyse our traffic. We also share information about your use of our site with our social media, advertising and analytics partners. View more
Cookies settings
Accept
Privacy & Cookie policy
Privacy & Cookies policy
Cookie name Active
Save settings
Cookies settings