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

Я новичок в Adnroid, и я работаю над запросом в 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, не обращая внимания на акценты и регистр. Пожалуйста помоги.

3 ответа

Решение

Как правило, сравнения строк в SQL контролируются столбцом или выражением COLLATE правила. В Android предварительно определены только три последовательности сопоставления: BINARY (по умолчанию), LOCALIZED и UNICODE. Ни один из них не подходит для вашего случая использования, и, к сожалению, API C для установки новых функций сопоставления не представлен в API Java.

Чтобы обойти это:

  1. Например, добавьте еще один столбец в таблицу. MOVIE_NAME_ASCII
  2. Сохраните значения в этом столбце с удаленными знаками ударения. Вы можете удалить акценты, нормализуя строки в Unicode Normal Form D (NFD) и удаляя кодовые точки, отличные от ASCII, так как NFD представляет акцентированные символы примерно как простые ASCII + комбинирующие маркеры акцента:

    String asciiName = Normalizer.normalize(unicodeName, Normalizer.Form.NFD)
        .replaceAll("[^\\p{ASCII}]", "");
    
  3. Выполните поиск текста в этом нормализованном ASCII-столбце, но отобразите данные из исходного столбца Unicode.

В 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 подстановочные знаки, * а также ?, с "сбежавшими" версиями.

Вы можете использовать Android NDK для перекомпиляции исходного кода SQLite, включая требуемый ICU (международные компоненты для Unicode). Объяснено на русском языке здесь: http://habrahabr.ru/post/122408/

Процесс компиляции SQLilte с исходным кодом с помощью ICU объясняется здесь:

Как скомпилировать sqlite с ICU?

К сожалению, вы получите разные APK для разных процессоров.

Вы должны смотреть на них не как на акцентированные символы, а как на совершенно других персонажей. Вы можете также искать a, b или c. При этом я бы попробовал использовать для этого регулярное выражение. Это будет выглядеть примерно так:

SELECT * from TB_MOVIE WHERE MOVIE_NAME REGEXP '.*[aAàÀ].*' ORDER BY MOVIE_NAME;
Другие вопросы по тегам