Как игнорировать акцент в запросе SQLite (Android)
Я новичок в Android, и я работаю над запросом в SQLite. Моя проблема в том, что когда я использую ударение в строках, например
Если я сделаю:
SELECT * FROM TB_MOVIE WHERE MOVIE_NAME LIKE '%a%' ORDER BY MOVIE_NAME;
Это возвращение:
- AAA
- ааа (игнорируя остальных)
Но если я сделаю:
SELECT * FROM TB_MOVIE WHERE MOVIE_NAME LIKE '%à%' ORDER BY MOVIE_NAME;
Это возвращение:
- ааа (игнорируя заголовок "ААА")
Я хочу выбрать строки в БД SQLite, не обращая внимания на акценты и регистр. Пожалуйста помоги.
Ответы
Ответ 1
Как правило, сравнение строк в SQL контролируется правилами столбца или выражения COLLATE
. В Android только три последовательности сортировки предопределены: BINARY (по умолчанию), LOCALIZED и UNICODE. Ни один из них не идеален для вашего варианта использования, и API C для установки новых функций сортировки, к сожалению, не отображается в Java API.
Чтобы обойти это:
- Добавьте еще один столбец в таблицу, например
MOVIE_NAME_ASCII
-
Сохраняйте значения в этом столбце с удалением отметок. Вы можете удалять акценты, нормализуя свои строки в Unicode Normal Form D (NFD) и удаляя точки кода, отличные от ASCII, поскольку NFD представляет акцентированные символы примерно как обычный ASCII +, комбинирующий маркеры:
String asciiName = Normalizer.normalize(unicodeName, Normalizer.Form.NFD)
.replaceAll("[^\\p{ASCII}]", "");
-
Выполняет ли поиск текста в этом столбце с нормализацией ASCII, но отображает данные из исходного столбца Юникода.
Ответ 2
Вы можете использовать Android NDK для перекомпиляции источника SQLite, включая желаемый ICU (Международные компоненты для Unicode).
Объясняется здесь:
http://habrahabr.ru/post/122408/
Процесс компиляции SQLilte с источником с ICU объясняется здесь:
Как скомпилировать sqlite с ICU?
К сожалению, у вас будут разные APK для разных процессоров.
Ответ 3
В Android sqlite LIKE
и GLOB
игнорируют как COLLATE LOCALIZED
и COLLATE UNICODE
(они работают только для ORDER BY
). Однако есть решение без добавления дополнительных столбцов в таблицу. Как объясняет @asat в этом ответе, вы можете использовать GLOB
с шаблоном, который заменит каждую букву всеми доступными альтернативами этой буквы. В Java:
public static String addTildeOptions(String searchText) {
return searchText.toLowerCase()
.replaceAll("[aáàäâã]", "\\[aáàäâã\\]")
.replaceAll("[eéèëê]", "\\[eéèëê\\]")
.replaceAll("[iíìî]", "\\[iíìî\\]")
.replaceAll("[oóòöôõ]", "\\[oóòöôõ\\]")
.replaceAll("[uúùüû]", "\\[uúùüû\\]")
.replace("*", "[*]")
.replace("?", "[?]");
}
И потом (не буквально так, конечно):
SELECT * from table WHERE lower(column) GLOB "*addTildeOptions(searchText)*"
Таким образом, например, на испанском языке пользователь, выполняющий поиск по mas или más, преобразует результаты поиска в m [aáàäâã], возвращая оба результата.
Важно отметить, что GLOB
игнорирует COLLATE NOCASE
, поэтому я преобразовал все в нижний регистр как в функции, так и в запросе. Также обратите внимание, что функция lower()
в sqlite не работает с не-ASCII-символами, но, вероятно, это те, которые вы уже заменяете!
Функция также заменяет подстановочные знаки GLOB
, *
и ?
, с "сбежавшими" версиями.
Ответ 4
Вам нужно посмотреть на них, а не на акцентированные символы, а на совершенно разные персонажи. Вы могли бы также искать a, b или c. При этом я бы попытался использовать для этого регулярное выражение. Это выглядело бы так:
SELECT * from TB_MOVIE WHERE MOVIE_NAME REGEXP '.*[aAàÀ].*' ORDER BY MOVIE_NAME;