Pełnotekstowe wyszukiwanie w SQLite
Od SQLite w wersji 3.5.9 dostępny jest moduł, mechanizm pełnotekstowego wyszukiwania FTS3 (starsze wersje mogą mieć tylko FTS1, FTS2). Dzięki temu modułowi bardzo łatwo i szybko możemy dodać opcję pełnotekstowego wyszukiwania. W przypadku Pythona to pod Pythonem 2.5 pysqlite nie obsługuje FTS3 (może rekompilacja z -DSQLITE_ENABLE_FTS3=1 by temu zaradziła), lecz Python 2.6 już powinien obsługiwać FTS3 bez problemów (przy problemach przekompiluj sqlite z podaną wcześniej flagą).
Tworzenie wirtualnej tabeli
Oto przykładowa wirtualna tabela:
Obsługa FTS1 i FTS2 wygląda tak samo jak FTS3. Więcej dowiemy się na sqlite.org.
Dodawanie danych do tabeli wirtualnej
Można zacząć od zaimportowania danych z istniejącej tabeli, np. taki skrypt odpalony z katalogu projektu Django zaimportuje dane:
# -*- coding: utf-8 -*-
import sys
import urllib2
from os import environ
environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from settings import *
from django.contrib.sessions.models import *
from django.db import connection, transaction
from MYAPP.models import *
cursor = connection.cursor()
j = MY_SOME_MODEL.objects.all()
iterr = 1
for i in j:
print iterr
txt = i.some_txt + ' ' + i.more_txt + ' ' + i.city_of_something
# txt should be stripped from HTML, stop words etc. to get smaller size of the database
cursor.execute("INSERT INTO my_search (slug, body) VALUES (%s, %s)", (i.slug, txt))
iterr += 1
transaction.commit_unless_managed()
Indeksowanie nowych wpisów można w Django zrealizować np. za pomocą sygnałów. Do np. models.py danej aplikacji można dodać:
from django.db.models import signals
#....
def update_index(sender, instance, created, **kwargs):
cursor = connection.cursor()
txt = instance.some_txt + ' ' + instance.more_txt + ' ' + instance.city_of_something
# txt should be stripped from HTML, stop words etc. to get smaller size of the database
txt = clean_to_search(txt)
if created:
# add if object is created, not updated
cursor.execute("INSERT INTO my_search (slug, body) VALUES (%s, %s)", (instance.slug, txt))
transaction.commit_unless_managed()
signals.post_save.connect(update_index, sender=MY_SOME_MODEL)
Wyszukiwanie pełnotekstowe
Zapytanie ma postać typu:
cursor = connection.cursor()
cursor.execute("SELECT slug FROM offers_search WHERE body MATCH %s", (query,))
results = cursor.fetchall()
Testując pełnotekstowe wyszukiwanie w SQLite na megiteam.pl, gdzie stosowany jest Nginx+FastCGI nie stwierdziłem jak na razie żadnych problemów, nadmiernego zużycia RAMu itp. przez takie operacje. Jako że nie trzeba dodatkowych aplikacji (whoosh, sphinx itd.) jest to rozwiązanie prostsze i łatwiejsze w implementacji.
Comment article