Использование COLLATE в Android SQLite - локали игнорируются в инструкции LIKE

При создании моей базы данных SQLite в Android я устанавливаю локаль базы данных - db.setLocale(новый язык ( "cz_CZ" )). Это чешский язык.

Оператор SELECT работает и учитывает локаль, например:

SELECT * from table WHERE name='sctzy' COLLATE LOCALIZED 

Найдет запись "ščťžý".

Но использование LIKE не будет выполнено:

SELECT * from table WHERE name LIKE '%sctzy%' COLLATE LOCALIZED 

Никакая строка не возвращается.

BTW. В Android нет класса java.text.Normalized. Я думал, что могу сделать второй столбец с нормализованным текстом, лишенный специальных символов, который будет использоваться для поиска - но мне не хватает класса или способа нормализации строки.

Ответ 1

Вы просмотрели документацию SQLite для LIKE? В нем появилась информация о символах без символов ASCII и об ошибке. Возможно, у Android установлена ​​более старая версия SQLite, где это проблема.

Я думаю, что второй нормализованный столбец может быть вашим лучшим вариантом, к сожалению.

Ответ 2

Создание второго нормализованного столбца можно использовать для ограничения ограничений (как кратко сказано в других ответах).

Это означает, что на практике вам нужно создать другой (теневой) столбец первой, где хранятся одни и те же данные в фиксированном случае (например, все верхние символы). Нечувствительные к регистру запросы (в том числе запросы) могут быть сделаны в этом новом столбце со значениями поиска в том же случае.

Если первый столбец "a" содержит

AAA
AAA
Bbb
äää
ééé

Второй столбец a_shadow будет содержать для тех же строк

AAA
AAA
Bbb
ÄÄÄ
ÉÉÉ

и ваш исходный запрос (пример) "выберите a from mytable, где a = 'äää'"
будет заменен на "выберите a из моей таблицы, где A =" ÄÄÄ "

Ваш код необходимо обновить, чтобы заполнить преобразованный теневой контент при добавлении основного контента. Если столбец добавлен после создания или вы не можете изменить код, существующие значения могут потребоваться преобразовать с помощью запроса обновления. Пример:

UPDATE mytable SET a_shadow=UPPER(a);

Ответ 3

Может потребоваться много времени, но вы можете использовать java.text.Normalizer, как здесь.

Преобразование символов, акцентированных букв на английский алфавит

Как не является частью java-подмножества Android, вы можете попытаться найти его в коде java, например Normalizer.java С помощью Javadoc найдено здесь:

И скопируйте часть кода, необходимого в вашем проекте.

Надеюсь, что это сработает!

Ответ 4

Просто сегодня у меня было точно такое же задание, как и у тебя. И в моей ситуации создание дополнительных теневых столбцов - это не тот случай, потому что мне приходится искать более одного столбца. Поэтому я пришел к такому решению, которое тестируется в реальном проекте. В моем случае я обрабатываю только строчные буквы, но вы также можете расширить функцию заглавными буквами.

db.setLocale(Locale("cz", "CZ"))
val query = "SELECT * FROM table WHERE name GLOB ${getExpr(str)} ORDER BY name COLLATE LOCALIZED ASC"

private fun getExpr(input: String) : String{
    var expr = ""
    for(lettter in input){
        expr += when(lettter){
            's','š' -> "[sš]"
            'a','á' -> "[aá]"
            'e','ě','é' -> "[eěé]"
            'i','í' -> "[ií]"
            'z','ž' -> "[zž]"
            'c','č' -> "[cč]"
            'y','ý' -> "[yý]"
            'r','ř' -> "[rř]"
            'u','ů','ú' -> "[uůú]"
            'o','ó' -> "[oó]"
            'n','ň' -> "[nň]"
            'd','ď' -> "[dď]"
            't','ť' -> "[tť]"
            else -> lettter
        }
     }
     return "'*${expr}*'"
}

Ответ 5

В 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, * и ? , с "сбежавшими" версиями.