ruby-on-rails - español - heroku postgres tutorial
Postgres no acentuó la búsqueda de LIKE en Rails 3.1 en Heroku (5)
¿Cómo puedo modificar una condición donde / como en una consulta de búsqueda en Rails?
find(:all, :conditions => ["lower(name) LIKE ?", "%#{search.downcase}%"])
¿De modo que los resultados se combinan independientemente de los acentos? (ej. métro = metro). Debido a que estoy usando utf8, no puedo usar "to_ascii". La producción se está ejecutando en Heroku.
La solución del hombre pobre.
Si puedes crear una función, puedes usar esta. Compilé la lista comenzando here y la agregué con el tiempo. Es bastante completo. Es posible que incluso desee eliminar algunos caracteres:
CREATE OR REPLACE FUNCTION lower_unaccent(text)
RETURNS text AS
$func$
SELECT lower(translate($1
, ''¹²³áàâãäåāăąÀÁÂÃÄÅĀĂĄÆćčç©ĆČÇĐÐèéêёëēĕėęěÈÊËЁĒĔĖĘĚ€ğĞıìíîïìĩīĭÌÍÎÏЇÌĨĪĬłŁńňñŃŇÑòóôõöōŏőøÒÓÔÕÖŌŎŐØŒř®ŘšşșߊŞȘùúûüũūŭůÙÚÛÜŨŪŬŮýÿÝŸžżźŽŻŹ''
, ''123aaaaaaaaaaaaaaaaaaacccccccddeeeeeeeeeeeeeeeeeeeeggiiiiiiiiiiiiiiiiiillnnnnnnooooooooooooooooooorrrsssssssuuuuuuuuuuuuuuuuyyyyzzzzzz''
));
$func$ LANGUAGE sql IMMUTABLE;
Tu consulta debería funcionar así:
find(:all, :conditions => ["lower_unaccent(name) LIKE ?", "%#{search.downcase}%"])
Para búsquedas ancladas a la izquierda, puede utilizar un índice en la función para obtener resultados muy rápidos:
CREATE INDEX tbl_name_lower_unaccent_idx
ON fest (lower_unaccent(name) text_pattern_ops);
Para consultas como:
SELECT * FROM tbl WHERE (lower_unaccent(name)) ~~ ''bob%''
Solución adecuada
En PostgreSQL 9.1+ , con los privilegios necesarios, puedes simplemente:
CREATE EXTENSION unaccent;
que proporciona una función unaccent()
, haciendo lo que necesita (excepto por lower()
, simplemente use esa función adicional si es necesario). Lea el manual sobre esta extensión .
También disponible para PostgreSQL 9.0, pero la sintaxis CREATE EXTENSION
es nueva en 9.1.
Más sobre los no acentos y los índices:
Asumiendo que Foo
es el modelo contra el que está buscando y name
es la columna. Combinando la translate Postgres y la translate ActiveSupport. Puedes hacer algo como:
Foo.where(
"translate(
LOWER(name),
''âãäåāăąÁÂÃÄÅĀĂĄèééêëēĕėęěĒĔĖĘĚìíîïìĩīĭÌÍÎÏÌĨĪĬóôõöōŏőÒÓÔÕÖŌŎŐùúûüũūŭůÙÚÛÜŨŪŬŮ'',
''aaaaaaaaaaaaaaaeeeeeeeeeeeeeeeiiiiiiiiiiiiiiiiooooooooooooooouuuuuuuuuuuuuuuu''
)
LIKE ?", "%#{ActiveSupport::Inflector.transliterate("%qué%").downcase}%"
)
En primer lugar, instale postgresql-contrib. Luego te conectas a tu DB y ejecutas:
CREATE EXTENSION unaccent;
para habilitar la extensión para su base de datos.
Dependiendo de su idioma, es posible que necesite crear un nuevo archivo de reglas (en mi caso greek.rules
, ubicado en /usr/share/postgresql/9.1/tsearch_data
), o simplemente adjuntar a las unaccent.rules
existentes (bastante sencillas).
En caso de que cree su propio archivo .rules
, debe hacerlo por defecto:
ALTER TEXT SEARCH DICTIONARY unaccent (RULES=''greek'');
Este cambio es persistente, por lo que no es necesario rehacerlo.
El siguiente paso sería agregar un método a un modelo para hacer uso de esta función.
Una solución simple sería definir una función en el modelo. Por ejemplo:
class Model < ActiveRecord::Base
[...]
def self.unaccent(column,value)
a=self.where(''unaccent(?) LIKE ?'', column, "%value%")
a
end
[...]
end
Entonces, simplemente puedo invocar:
Model.unaccent("name","text")
Invocar el mismo comando sin la definición del modelo sería tan simple como:
Model.where(''unaccent(name) LIKE ?'', "%text%"
Nota: El ejemplo anterior ha sido probado y funciona para postgres9.1, Rails 4.0, Ruby 2.0.
ACTUALIZAR INFORMACIÓN
Se corrigió el posible backdoor SQLi gracias a los comentarios de @Henrik N
Hay 2 preguntas relacionadas con su búsqueda en el StackExchange: https://serverfault.com/questions/266373/postgresql-accent-diacritic-insensitive-search
Pero como usted está en Heroku, dudo que este sea un buen partido (a menos que tenga un plan de base de datos dedicado).
También hay este en SO: Eliminación de acentos / signos diacríticos de la cadena al tiempo que se conservan otros caracteres especiales .
Pero esto supone que sus datos se almacenan sin ningún acento.
Espero que te apunte en la dirección correcta.
Para aquellos que, como yo, que tienen problemas para agregar la extensión unaccent
para PostgreSQL y hacer que funcione con la aplicación Rails, aquí está la migración que necesita crear:
class AddUnaccentExtension < ActiveRecord::Migration
def up
execute "create extension unaccent"
end
def down
execute "drop extension unaccent"
end
end
Y, por supuesto, después de rake db:migrate
, podrá usar la función unaccent
en sus consultas: unaccent(column) similar to ...
o unaccent(lower(column)) ...