2015-04-23 9 views
69

Trudno mi zrozumieć, jak korzystać z wyszukiwania pełnotekstowego (FTS) w systemie Android. Przeczytałem numer SQLite documentation on the FTS3 and FTS4 extensions. I znam it's possible to do on Android. Mam jednak trudności ze znalezieniem przykładów, które mogę zrozumieć.Przykład wyszukiwania pełnotekstowego w Androidzie

Podstawowy model w bazie

tabeli bazy danych SQLite (nazwane example_table) ma 4 kolumny. Jednak istnieje tylko jedna kolumna (o nazwie text_column), która musi zostać zaindeksowana do wyszukiwania pełnotekstowego. Każdy wiersz text_column zawiera tekst o długości od 0 do 1000 słów. Całkowita liczba wierszy jest większa niż 10 000.

  • Jak skonfigurować tabelę i/lub wirtualny stół FTS?
  • Jak wykonać kwerendę FTS na text_column?

dodatkowe uwagi:

  • Ponieważ tylko jedna kolumna musi być indeksowane, tylko stosując tabelę FTS (i opuszczając example_table) byłby inefficient for non-FTS queries.
  • Dla takiej dużej tabeli przechowywanie zduplikowanych wpisów text_column w tabeli FTS byłoby niepożądane. This post sugeruje użycie external content table.
  • Tabele treści zewnętrznych używają FTS4, ale FTS4 to not supported before Android API 11. Odpowiedź może przyjąć interfejs API> = 11, ale pomocne byłyby komentarze dotyczące opcji obsługi niższych wersji.
  • Zmiana danych w oryginalnej tabeli nie powoduje automatycznej aktualizacji tabeli FTS (i odwrotnie). W tym triggers w odpowiedzi nie jest konieczne dla tego podstawowego przykładu, ale byłoby jednak pomocne.
+2

dobrze udokumentowane pytanie, jestem przeciwdziałaniu arbitralną downvote tu dostałeś. – Mekap

Odpowiedz

92

Najbardziej podstawowa Answer

używam zwykłego sql poniżej tak, że wszystko jest jasne i czytelne, jak to możliwe. W swoim projekcie możesz skorzystać z wygodnych metod Android. Zastosowany poniżej obiekt db jest instancją SQLiteDatabase.

Create FTS Table

db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 (col_1, col_2, text_column)"); 

To może iść w sposobie swojego rozszerzonego SQLiteOpenHelper klasy onCreate().

Populate FTS Table

db.execSQL("INSERT INTO fts_table VALUES ('3', 'apple', 'Hello. How are you?')"); 
db.execSQL("INSERT INTO fts_table VALUES ('24', 'car', 'Fine. Thank you.')"); 
db.execSQL("INSERT INTO fts_table VALUES ('13', 'book', 'This is an example.')"); 

Byłoby lepiej używać SQLiteDatabase#insert lub prepared statements niż execSQL.

Query FTS Table

String[] selectionArgs = { searchString }; 
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs); 

Można również użyć metody SQLiteDatabase#query. Zwróć uwagę na słowo kluczowe MATCH.

Fuller Odpowiedź

powyżej wirtualny stół FTS ma z tym problem. Każda kolumna jest indeksowana, ale jest to strata miejsca i zasobów, jeśli niektóre kolumny nie muszą być indeksowane. Jedyną kolumną, która potrzebuje indeksu FTS, jest prawdopodobnie text_column.

Aby rozwiązać ten problem, użyjemy kombinacji zwykłego stołu i wirtualnej tabeli FTS. Tabela FTS będzie zawierała indeks, ale żadne rzeczywiste dane z normalnej tabeli. Zamiast tego będzie zawierał link do zawartości zwykłej tabeli. Nazywa się to external content table.

enter image description here

utworzyć tabele

db.execSQL("CREATE TABLE example_table (_id INTEGER PRIMARY KEY, col_1 INTEGER, col_2 TEXT, text_column TEXT)"); 
db.execSQL("CREATE VIRTUAL TABLE fts_example_table USING fts4 (content='example_table', text_column)"); 

Zauważ, że mamy do wykorzystania FTS4 to zrobić zamiast FTS3. FTS4 nie jest obsługiwany w systemie Android przed wersją interfejsu API 11. Możesz (1) zapewnić tylko funkcję wyszukiwania dla API> = 11 lub (2) użyć tabeli FTS3 (ale oznacza to, że baza danych będzie większa, ponieważ kolumna pełnego tekstu istnieje w obu bazach danych).

wypełnić tabele

db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('3', 'apple', 'Hello. How are you?')"); 
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('24', 'car', 'Fine. Thank you.')"); 
db.execSQL("INSERT INTO example_table (col_1, col_2, text_column) VALUES ('13', 'book', 'This is an example.')"); 

(Ponownie, istnieją lepsze sposoby na temat wkładki niż execSQL. Ja po prostu używając go do jego czytelności.)

Jeśli próbowali zrobić Zapytanie FTS teraz na fts_example_table nie dostaniesz żadnych wyników. Powodem jest to, że zmiana jednej tabeli nie powoduje automatycznej zmiany drugiej tabeli. Trzeba ręcznie zaktualizować tabelę FTS:

db.execSQL("INSERT INTO fts_example_table (docid, text_column) SELECT _id, text_column FROM example_table"); 

(. Na docid jest jak rowid do regularnego tabeli) trzeba się upewnić, aby zaktualizować tabelę FTS (tak, że można go zaktualizować indeks) za każdym razem, wprowadzasz zmianę (INSERT, DELETE, UPDATE) do zewnętrznej tabeli treści. Może to być uciążliwe. Jeśli tworzysz tylko wstępnie wypełnioną bazę danych, możesz wykonać , która odbuduje całą tabelę. Może to być powolne, więc nie jest to coś, co chcesz zrobić po każdej małej zmianie. Zrobiłbyś to po skończeniu wszystkich wstawień w zewnętrznej tabeli treści. Jeśli zachodzi potrzeba automatycznego synchronizowania baz danych, można użyć numeru triggers. Go here i przewiń w dół, aby znaleźć wskazówki.

kwerendy baz danych

String[] selectionArgs = { searchString }; 
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs); 

To jest taki sam jak poprzednio, ale tym razem masz tylko dostęp do text_column (i docid). Co się stanie, jeśli chcesz pobrać dane z innych kolumn z zewnętrznej tabeli treści? Ponieważ docid tabeli FTS pasuje do rowid (iw tym przypadku _id) tabeli zawartości zewnętrznej, można użyć łączenia. (Podziękowania dla this answer za pomoc.)

String sql = "SELECT * FROM example_table WHERE _id IN " + 
     "(SELECT docid FROM fts_example_table WHERE fts_example_table MATCH ?)"; 
String[] selectionArgs = { searchString }; 
Cursor cursor = db.rawQuery(sql, selectionArgs); 

Dalsze Reading

przejść przez te dokumenty starannie aby zobaczyć inne sposoby wykorzystania FTS wirtualne tabele:

Uwagi dodatkowe

  • Operatory zestawu (AND, OR, NOT) w zapytaniach SQLite FTS mają Standard Query Syntax i Enhanced Query Syntax. Niestety, Android najwyraźniej nie obsługuje składni Enhanced Query (zobacz here, here, here i here). Oznacza to, że mieszanie ORAZ i OR staje się trudne (wymaga użycia UNION lub sprawdzenia, czy wygląda na to PRAGMA compile_options). Bardzo niefortunne. Dodaj komentarz, jeśli jest aktualizacja w tym obszarze.
+1

W rzeczywistości, jeśli używasz tabeli fts w sposób, w którym podałeś (wybierając z tabeli non-fts, gdzie _id jest zawarty w zestawie docid zwróconym przez dopasowanie tabeli fts), możesz zaoszczędzić miejsce przy użyciu content = " ". Spowoduje to utworzenie indeksu pełnotekstowego bez powielania treści. Zobacz [Contentless FTS4 Tables] (http://www.sqlite.org/fts3.html#section_6_2_1) – astyanaxas

+0

Opcja zawartości FTS4 została dodana nie wcześniej niż w SQLite 3.7.9 (http://www.sqlite.org/releaselog/ 3_7_11.html), co oznacza, że ​​jest niedostępna przed Androidem API 16. SQLiteDatabase rzuci próbę użycia. – Knuckles

1

Nie zapomnij, że podczas korzystania z zawartości z do odbudowania tabeli fts.

zrobić to z wyzwalaczem na aktualizacji, dodawania, usuwania

+1

Więcej informacji z kodem byłoby pomocne. – Suragch

+0

'INSERT INTO foo_fts VALUES (" przebuduj ")' –

Powiązane problemy