ORM django pozwala na wyszukiwanie pełnotekstowe za pomocą metody search. Niestety jak na razie jest to funkcja dostępna tylko dla bazy MySQL która wyszukiwaniem pełnotekstowym cieszy sie od dawna. PostgreSQL - zalecana baza django posiada wyszukiwanie pełnotekstowe dopiero w wresji 8.3. Niestety wersja 8.3 jest na razie niedostępna w większości dystrybucji (na gentoo jest zamaskowana) przez co zmuszony zostałem do poszukiwania innego rozwiązania. Z google trafiłem na strone projektu django-sphinx - któryy do wyszukiwania pełnotekstowego w django wykorzystuje sphinx'a.

Czym jest sphinx? Najogólniej mówiąc jest to aplikacja która zajmuje się indexowaniem zadanych zapytań z bazy danych i pozwala później na ich wyszukiwanie. Sphinx pozwala na indexowanie tabel zarówno z MySQL jak i PostgreSQL - mnie z wiadomych przyczyn interesował bardziej ten drugi. Na swojej stronie chwalą się że ich aplikacja jest wykorzystywana przez pokaźną liczbę stron - w tym główne forum phpbb.com, tracker (a tutaj to pewnie co druga akcja użytkownika to wyszukiwanie), tracker mininova.org (podobniez 3-5 milionów wyszukiwań dziennie). Nie znam niestety testów wydajnościowych porównujących bazodanowe mechanizmy full text search ze sphinxem - aczkolwiek postaram się zrobić w najbliższych dniach (mam nawet ładną tabelkę do przeindexowania do testów ;) ).

django vs sphinx

A teraz krótkie howto jak zaimplementować sphinx we własnej aplikacji django.

Po zainstalowaniu sphinxa musimy udać się do /etc/ i edytować sphinx.conf. Dla przykładu definicja indexu do tabeli z komentarzami:

/etc/sphinx.conf
source baza
{
        type = pgsql
        sql_host = localhost
        sql_user = login
        sql_pass = haslo
        sql_db = baza
        sql_port = nshot
}

source komentarze_komentarze : baza
{
        sql_query = \
        SELECT id, autor_id, extract('epoch' from data_publikacji) \
        AS data_publikacji, \
        CASE WHEN is_main THEN 1 ELSE 0 END AS is_main, \
        CASE WHEN zablokowany THEN 1 ELSE 0 END AS zablokowany, tresc\
        FROM komentarze_komentarze

        sql_attr_uint = autor_id
        sql_attr_timestamp = data_publikacji
        sql_attr_bool = is_main
        sql_attr_bool = zablokowany
}

index komentarze_komentarze_index
{
        source = komentarze_komentarze
        charset_type = utf-8
        min_infix_len = 1
        enable_star = 1

#usuwa polskie znaki i zamienia na literki
        charset_table = 0..9, A..Z->a..z, _, a..z, \
                U+0143->n, U+0144->n, \
                U+0104->a, U+0105->a, \
                U+0106->c, U+0107->c, \
                U+0118->e, U+0119->e, \
                U+0141->l, U+0142->l, \
                U+00D3->o, U+00F3->o, \
                U+015A->s, U+015B->s, \
                U+0179->z, U+017A->z, \
                U+017B->z, U+017C->z

# indexuje polskie znaki
#        charset_table = 0..9, A..Z->a..z, _, a..z, \
#        U+0143->U+0144, U+0104->U+0105, U+0106->U+0107, U+0118->U+0119, \
#        U+0141->U+0142, U+00D3->U+00F3, U+015A->U+015B, U+0179->U+017A, \
#        U+017B->U+017C, U+0105, U+0107, U+0119, U+0142, U+00F3, \
#        U+015B, U+017A, U+017C, U+0144 

        min_word_len = 2
        path = /var/lib/sphinx/data/baza/komentarze
}
  • source baza - definiuje połączenie z bazą dancyh - chyba nie musze wyjaśniać co oznacza który wiersz ;)
  • source komentarze_komentarze : baza - definiuje "zapytanie" do indexowania (rozszerza "baze") a w nim:
    • sql_query - nie musze wyjaśniać - w seleccie sa CASE - ze wzgledu na BUG z boolean z PostgreSQL
    • sql_attr_uint, sql_attr_timestamp, sql_attr_bool - typy pól po których można filtrować wyniki - dla przykładu możemy później przeszukać bazę po komentarzach zablokowanych
  • index komentarze_komentarze_index - definicja indexu do przeszukiwania
    a w nim:
    • source - zapytanie ktore indexujemy
    • min_infix_len - minimalna długość infixu - chodzi tu o to minimalnie ile znakow pozwala na wyszukiwania "wildcardem"
    • enable_star - pozwala na wyszukiwanie "wildcard"
    • charset_type - kodowanie znaków
    • charset_table - lista dostępnych znaków oraz ścieżka zamieniania - duże na małe litery itp (niezakomentowana opcja usuwa polskie znaki - zakomentowana zmienia duze litery na male)
    • min_word_len - minimana długość słowa do zaindexowania
    • path - ścieżka gdzie zapiane sa pliki indexu

po przygotowaniu pliku z konfiguracja uruchamiamy polecenie

shell
indexer --al

Które utworzy indexy i pozwoli na wyszukiwanie. Następnie uruchamiamy demon do wyszukiwania:

shell
searchd

Jeżeli chcemy przeindexować dane w trakcie musimy wykonac polecenie

shell
indexer --all --rotate

Rotuje ono pliki z indexami i następnie restartuje searchd.

Pozostaje teraz tylko połączyć sphinx z django. W tym celu w naszym modelu musimy zaimportować djangosphinx a następnie dopisać index do wyszukiwania

komentarze/model.py
from djangosphinx import SphinxSearch
#(...)
class Komentarze(models.Model):
    #(...)
    search = SphinxSearch(index='komentarze_komentarze_index')

Teraz możemy się cieszyć pełnotekstowym wyszukiwaniem za pomocą polecenia

./manage.py shell
Komentarze.search.query("*cculko*")

Komentarze do wpisu "django full text search za pomoca sphinx":

1. arag0rn napisał(a):
25 kwietnia 2008, 15:43:08

Dla ścisłości, wyszukiwanie pełnotekstowe jest w Postgresie od niepamiętnych czasów, dostępne jako moduł tsearch2 w contribie. Od 8.3 jest jedynie dołączone od razu.

Dodaj komentarz: