El uso de clasificado en Android SQLite - Locales se ignora en instrucción LIKE
Pregunta
Al crear mi base de datos SQLite en Android que establece la configuración regional de base de datos - db.setLocale (nueva configuración regional ( "cz_CZ")). Esta es una configuración regional Checa.
Fábrica de instrucción SELECT y toma la configuración regional en cuenta, por ejemplo:
SELECT * from table WHERE name='sctzy' COLLATE LOCALIZED
encontrará la entrada 'šctžý'.
Sin embargo, utilizando como fallará:
SELECT * from table WHERE name LIKE '%sctzy%' COLLATE LOCALIZED
No se devuelve la fila.
Por cierto. No hay ninguna clase java.text.Normalized en Android. Pensé que podría hacer una segunda columna con un texto normalizado, despojado de caracteres especiales, que se utiliza para buscar - pero me falta una clase o forma de cómo normalizar la cadena
.Solución
¿Ha tenido un vistazo a la documentación href="http://www.sqlite.org/lang_expr.html#like" rel="nofollow noreferrer"> ? Se ha llegado información sobre los caracteres no ASCII y un error. Tal Android tiene una versión anterior de SQLite instalado donde esto es un problema.
Creo que la segunda columna normalizada podría ser su mejor opción por desgracia.
Otros consejos
Creación de una segunda columna normalizada se puede usar para ir alrededor de limitaciones (como se ha mencionado brevemente en otras respuestas).
Esto significa en la práctica que hay que crear otra (sombra) de su primera columna, donde se almacenan los mismos datos en un caso fijo (por ejemplo, todos los caracteres superiores). Caso consultas insensibles (incluyendo como consultas) se pueden hacer en esta nueva columna con los valores de búsqueda en el mismo caso.
Si la primera columna "a" contiene
AAA
AAA
Bbb
AAA
eee
La segunda columna a_shadow contendría por las mismas filas
AAA
AAA
acreditación
AAA
eee
y su búsqueda original (ejemplo) "seleccionar una de mitabla donde a = 'AAA'"
sería reemplazado con "seleccione una de mitabla donde A = 'AAA'"
Su código debe ser actualizado para llenar el contenido sombra convertida al agregar el contenido principal. Si se añade la columna después de la creación o no se puede cambiar el código de valores existentes pueden necesitar ser convertidos mediante una consulta de actualización. Ejemplo:
UPDATE mytable SET a_shadow=UPPER(a);
Hoy mismo he tenido exactamente la misma tarea que tenías. Y en mi situación haciendo sombra columnas adicionales que no es un caso porque yo estoy teniendo que buscar más de una columna. Así que llegué a la solución de este tipo, que se probó en proyecto real. En mi caso yo estoy manejando sólo letras minúsculas, pero se puede extender la función con letras mayúsculas también.
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}*'"
}
Podría llevar mucho tiempo, pero se puede utilizar el java.text.Normalizer como aquí
conversión de símbolos, Accent Cartas al alfabeto Inglés
Como no es parte del subconjunto de Java que Android, se puede tratar de buscar que el código de Java, como Normalizer.java Con el Javadoc encontrado aquí :
Y copiar parte del código necesario dentro de su proyecto.
Esperanza funciona!
SQLite En Android, LIKE
y GLOB
ignoran tanto COLLATE LOCALIZED
y COLLATE UNICODE
(sólo trabajo para ORDER BY
). Sin embargo, como se explica en @asat su respuesta , puede utilizar GLOB
con un patrón que reemplazará a cada letra con todos los disponibles alternativas de esa carta. En 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("?", "[?]");
}
Y luego (no literalmente como este, por supuesto):
SELECT * from table WHERE lower(column) GLOB "*addTildeOptions(searchText)*"
De esta manera, por ejemplo, en español, una búsqueda del usuario, ya sea para mas o Más obtendrá la búsqueda convertida en m [aaaaaa] s , volviendo ambos resultados.
Es importante notar que ignora GLOB
COLLATE NOCASE
, por eso me convertí todo a minúsculas tanto en la función y en la consulta. Nótese también que la función lower()
en sqlite no funciona en caracteres no ASCII - pero de nuevo esos son probablemente los que ya se está reemplazando
La función también reemplaza los dos comodines GLOB
, *
y ?
, con versiones "escapado".