Найпростіша відповідь
Я використовую звичайний sql нижче, щоб все було якомога чіткіше та читабельніше. У своєму проекті ви можете використовувати зручні методи Android. db
Об'єкт , який використовується нижче , є екземпляром SQLiteDatabase .
Створіть таблицю FTS
db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");
Це може піти у onCreate()
методі вашого розширеного SQLiteOpenHelper
класу.
Таблиця заповнення FTS
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 # insert або підготовлені оператори, ніж execSQL
.
Запит таблиці FTS
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);
Ви також можете використовувати метод запиту SQLiteDatabase # . Зверніть увагу на MATCH
ключове слово.
Повна відповідь
У віртуальній таблиці FTS, наведеній вище, є проблема. Кожен стовпець індексується, але це втрата місця та ресурсів, якщо деякі стовпці не потрібно індексувати. Єдиний стовпець , який необхідний індекс FTS, ймовірно text_column
.
Для вирішення цієї проблеми ми будемо використовувати комбінацію звичайної таблиці та віртуальної таблиці FTS. Таблиця FTS буде містити індекс, але жодних фактичних даних із звичайної таблиці. Натомість у ньому буде посилання на вміст звичайної таблиці. Це називається зовнішньою таблицею вмісту .
Створіть таблиці
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. FTS4 не підтримується в Android до версії 11. API. Ви можете (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 (щоб вона могла оновити індекс) кожного разу, коли ви вносите зміни (ВСТАВИТИ, ВИДАЛИТИ, ОНОВИТИ) до зовнішньої таблиці вмісту. Це може стати громіздким. Якщо ви створюєте лише попередньо заповнену базу даних, ви можете це зробити
db.execSQL("INSERT INTO fts_example_table(fts_example_table) VALUES('rebuild')");
який відновить цілу таблицю. Однак це може бути повільним, тому це не те, що ви хочете робити після кожної маленької зміни. Ви зробили б це після закінчення всіх вставок у зовнішній таблиці вмісту. Якщо вам потрібно автоматично синхронізувати бази даних, ви можете використовувати тригери . Ідіть сюди і прокрутіть трохи вниз, щоб знайти напрямки.
Запит до баз даних
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_example_table WHERE fts_example_table MATCH ?", selectionArgs);
Це те саме, що і раніше, за винятком цього разу ви маєте доступ лише до text_column
(і docid
). Що робити, якщо вам потрібно отримати дані з інших стовпців у зовнішній таблиці вмісту? Оскільки docid
таблиця FTS відповідає rowid
(і в даному випадку _id
) зовнішній таблиці вмісту, ви можете використовувати об'єднання. (Дякую за цю відповідь за допомогу в цьому.)
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:
додаткові нотатки
- Оператори встановлення (І, АБО, НЕ) у запитах SQLite FTS мають стандартний синтаксис запитів та розширений синтаксис запитів . На жаль, Android, очевидно, не підтримує посилений синтаксис запитів (див. Тут , тут , тут і тут ). Це означає, що змішування І та АБО стає важким ( здається, вимагає використання
UNION
або перевірки PRAGMA compile_options
). Дуже прикро. Будь ласка, додайте коментар, якщо в цій області оновлення.