Мне сложно понять, как использовать полнотекстовый поиск (FTS) с Android. Я прочитал документацию SQLite на расширениях FTS3 и FTS4. И я знаю это можно сделать на Android. Однако мне трудно найти примеры, которые я могу понять.
Таблица базы данных SQLite (с именем example_table
) имеет 4 столбца. Однако есть только один столбец (с именем text_column
), который нужно индексировать для полнотекстового поиска. Каждая строка text_column
содержит текст, изменяющийся по длине от 0 до 1000 слов. Общее количество строк больше 10000.
text_column
?Дополнительные примечания:
example_table
) будет неэффективным для не-FTS-запросов.text_column
в таблице FTS было бы нежелательным. Этот пост предлагает использовать таблицу внешнего контента.Я использую простой sql ниже, чтобы все было максимально понятным и понятным. В вашем проекте вы можете использовать удобные методы Android. Объект db
, используемый ниже, представляет собой экземпляр SQLiteDatabase.
db.execSQL("CREATE VIRTUAL TABLE fts_table USING fts3 ( col_1, col_2, text_column )");
Это может пойти в методе onCreate()
вашего расширенного класса SQLiteOpenHelper
.
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
.
String[] selectionArgs = { searchString };
Cursor cursor = db.rawQuery("SELECT * FROM fts_table WHERE fts_table MATCH ?", selectionArgs);
Вы также можете использовать метод SQLiteDatabase # query. Обратите внимание на ключевое слово 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 до версии API версии 11. Вы можете либо (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')");
который перестроит всю таблицу. Это может быть медленным, но это не то, что вы хотите сделать после каждого небольшого изменения. Вы сделали бы это после завершения всех вставок во внешней таблице содержимого. Если вам необходимо автоматически синхронизировать базы данных, вы можете использовать triggers. Перейдите сюда и немного прокрутите список, чтобы найти маршруты.
Запросить базы данных
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:
UNION
или проверка PRAGMA compile_options
кажется). Очень неудачно. Пожалуйста, добавьте комментарий, если в этой области есть обновление.Не забывайте при использовании содержимого для перестройки таблицы fts.
Я делаю это с триггером при обновлении, вставке, удалении
INSERT INTO foo_fts VALUES("rebuild")