Como fazer uma consulta LIKE no Arel e Rails?


115

Eu quero fazer algo como:

SELECT * FROM USER WHERE NAME LIKE '%Smith%';

Minha tentativa em Arel:

# params[:query] = 'Smith'
User.where("name like '%?%'", params[:query]).to_sql

No entanto, isso se torna:

SELECT * FROM USER WHERE NAME LIKE '%'Smith'%';

Arel envolve a string de consulta 'Smith' corretamente, mas como esta é uma instrução LIKE, ela não funciona.

Como alguém faz uma consulta LIKE no Arel?

Bônus PS - Na verdade, estou tentando verificar dois campos da tabela, nome e descrição, para ver se há alguma correspondência com a consulta. Como isso funcionaria?


1
Atualizei a resposta arel para o bônus.
— Pedro Rolo

Respostas:


275

É assim que você executa uma consulta semelhante no arel:

users = User.arel_table
User.where(users[:name].matches("%#{user_name}%"))

PS:

users = User.arel_table
query_string = "%#{params[query]}%"
param_matches_string =  ->(param){ 
  users[param].matches(query_string) 
} 
User.where(param_matches_string.(:name)\
                       .or(param_matches_string.(:description)))

10
Ao contrário do uso where("name like ?", ...), essa abordagem é mais portátil em bancos de dados diferentes. Por exemplo, isso resultaria em ILIKEser usado em uma consulta em um banco de dados Postgres.
— dkobozev

20
isso está protegido contra injeções de SQL?
— sren

7
Isso NÃO protege totalmente contra injeção de SQL. Tente definir user_name para "%". A consulta retornará correspondências
— travis-146

5
Tentei injetar sql usando params diretamente, User.where(users[:name].matches("%#{params[:user_name]}%"))tentei TRUNCATE users;e outras consultas semelhantes e nada aconteceu no lado do sql. Parece seguro para mim.
— Earonrails

5
Use .gsub(/[%_]/, '\\\\\0')para escapar de caracteres curinga do MySql.
— aercolino

116

Experimentar

User.where("name like ?", "%#{params[:query]}%").to_sql

PS.

q = "%#{params[:query]}%"
User.where("name like ? or description like ?", q, q).to_sql

Aaand já faz muito tempo, mas @ cgg5207 adicionou uma modificação (principalmente útil se você for pesquisar parâmetros de nomes longos ou múltiplos ou se você estiver com preguiça de digitar)

q = "%#{params[:query]}%"
User.where("name like :q or description like :q", :q => q).to_sql

ou

User.where("name like :q or description like :q", :q => "%#{params[:query]}%").to_sql

9
Como o Rails sabe que não deve escapar %na string substituída? Parece que se você só quisesse um caractere curinga unilateral, não há nada que impeça o usuário de enviar um valor de consulta que inclua %em ambas as extremidades (eu sei que na prática, Rails impede %de aparecer em uma string de consulta, mas parece que existe deve haver proteção contra isso no nível ActiveRecord).
— Steven

8
Isso não é vulnerável a ataques de injeção de SQL?
— Behrang Saeedzadeh

7
@Behrang no 8) User.where ("nome como% # {params [: query]}% ou descrição como% # {params [: query]}%"). To_sql seria vulnerável, mas, no formato que mostro , Rails escapa dos parâmetros [: query]
— Reuben Mallaby

Desculpe por offtopic. Eu tenho o sql git do método to_sqlou gerenciador arel, como executar o sql no banco de dados?
— Малъ Скрылевъ

Model.where (to_sql_result)
— Pedro Rolo

4

A resposta de Reuben Mallaby pode ser encurtada ainda mais para usar ligações de parâmetro:

User.where("name like :kw or description like :kw", :kw=>"%#{params[:query]}%").to_sql
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.