2015-04-23 21 views
69

我很難理解如何在Android上使用全文搜索(FTS)。我讀過SQLite documentation on the FTS3 and FTS4 extensions。我知道it's possible to do on Android。但是,我很難找到任何可以理解的例子。Android中的全文搜索範例

基本數據庫模型

SQLite數據庫表(名爲example_table)具有4列。但是,只有一列(名爲text_column)需要爲全文搜索編制索引。每行text_column包含長度從0到1000個字變化的文本。總行數大於10,000。

  • 您將如何設置表格和/或FTS虛擬表格?
  • 您將如何在text_column上執行FTS查詢?

其他備註:

  • 由於只有一列需要被編入索引,只使用FTS表(以及丟棄example_table)將inefficient for non-FTS queries
  • 對於這樣一個大表,將text_column的重複條目存儲在FTS表中將是不理想的。建議使用external content table
  • 外部內容表使用FTS4,但FTS4是not supported before Android API 11。答案可以假定API> = 11,但評論支持較低版本的選項會有所幫助。
  • 更改原始表中的數據不會自動更新FTS表(反之亦然)。在你的回答中包括triggers對於這個基本的例子來說並不是必須的,但是仍然會有幫助。
+2

證據充分的問題,我對付你來到這裏任意downvote。 – Mekap

回答

92

最基本的答案

我用下面讓一切都清晰可讀儘可能普通的SQL。在您的項目中,您可以使用Android便捷方法。下面使用的db對象是SQLiteDatabase的實例。

Create FTS Table

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

這可以去你的外擴SQLiteOpenHelper類的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.')"); 

這將是更好地使用SQLiteDatabase#insertprepared statementsexecSQL

Query FTS Table

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

您也可以使用SQLiteDatabase#query方法。請注意0​​關鍵字。

Fuller答案

上面的虛擬FTS表有一個問題。每列都被編入索引,但如果某些列不需要編制索引,則這會浪費空間和資源。唯一需要FTS指數的列可能是text_column

要解決這個問題,我們將使用常規表和虛擬FTS表的組合。 FTS表將包含索引,但不包含常規表中的實際數據。相反,它將鏈接到常規表格的內容。這被稱爲external content table

enter image description here

創建表

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)"); 

注意,我們必須使用FTS4要做到這一點,而不是FTS3。在API版本11之前,Android不支持FTS4。您可以(1)僅爲API> = 11提供搜索功能,或者(2)使用FTS3表(但這意味着數據庫將變大,因爲全文列存在在兩個數據庫中)。

填充表

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.')"); 

(同樣,也有更好的方式執行插入比execSQL。我只是用它的可讀性。)

如果你試圖做一個FTS查詢現在在fts_example_table你將不會得到任何結果。原因是更改一個表格不會自動更改其他表格。您必須手動更新FTS表:

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

(該docid就像rowid一個普通表)你必須確保更新FTS表(以便它可以更新索引)每次您對外部內容表進行更改(INSERT,DELETE,UPDATE)。這可能會很麻煩。如果你只是做一個預填充的數據庫,你可以做

db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')"); 

這將重建整個表。但是,這可能會很慢,所以在每次小改動之後,這都不是你想要做的。在完成外部內容表中的所有插入之後,您可以執行此操作。如果您確實需要自動同步數據庫,則可以使用triggersGo here並向下滾動以查找路線。

查詢的數據庫

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

這是和以前一樣,但這個時候你只能訪問text_column(和docid)。如果您需要從外部內容表中的其他列中獲取數據,該怎麼辦?由於FTS表的docid與外部內容表的rowid(在此例中爲_id)匹配,因此可以使用聯接。 (感謝this answer尋求幫助。)

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); 

進一步閱讀

經過這些文件仔細查看使用FTS虛擬表的其他方式:

附加說明

+1

實際上,如果您按照指定的方式使用fts表(從非fts表中選擇其中_id包含在由fts表匹配返回的docid集中),則可以使用content =「 」。這將創建全文索引而不復制內容。請參閱[無特色FTS4表格](http://www.sqlite.org/fts3.html#section_6_2_1) – astyanaxas

+0

FTS4內容選項的添加時間不早於SQLite 3.7.9(http://www.sqlite.org/releaselog/ 3_7_11.html),這意味着它在Android API 16之前不可用.SQLiteDatabase將拋出使用嘗試。 – Knuckles

1

不要忘記使用內容來重建fts表。

我這樣做與更新,插入一個觸發器,刪除

+1

具有代碼的詳細信息將有所幫助。 – Suragch

+0

'INSERT INTO foo_fts VALUES(「rebuild」)' –