Как игнорировать акцент в запросе SQLite (Android)

Я новичок в Android, и я работаю над запросом в SQLite. Моя проблема в том, что когда я использую ударение в строках, например

  • aaÃ
  • aaÃ
  • aaÃ
  • aaÃ
  • ааа
  • AAA

Если я сделаю:

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;