程序化的 Arel 版本
注意:这尚未针对 SQL 注入进行全面测试。
class ApplicationRecord < ActiveRecord::Base
scope :fields_sentence_ilike, -> (*fields, term) {
sanitized_term = connection.quote("%#{term}%")
# InfixOperation.new(operator, left, right) => left operator right => concatenated_fiels ILIKE '%word%'
# NamedFunction.new(name, expression_nodes) => name(node0, node1, ...nodeN) => CONCAT_WS("columnA", "columnB", "columnC")
where(
Arel::Nodes::InfixOperation.new(
Arel::Nodes::SqlLiteral.new('ILIKE'),
Arel::Nodes::NamedFunction.new(
'CONCAT_WS', # CONCAT_WS concatenates strings using the first argument. In this case, an empty space.
[
Arel::Nodes::SqlLiteral.new("' '"), # CONCAT by empty space
*fields.map { |field|
# CONCATING any NULL fields results NULL (like multiplying any number by 0 equals 0). COALESCE to empty string.
Arel::Nodes::NamedFunction.new('COALESCE', [arel_attribute(field), Arel::Nodes::SqlLiteral.new("''")])
}
]
),
Arel::Nodes::SqlLiteral.new(sanitized_term)
)
)
}
end
然后是 Songs 模型的具体实现
class Song < ApplicationRecord
scope :full_name_like, -> (full_name) { fields_sentence_ilike(:artist_name, :name, full_name) }
end
用法
Song.full_name_like('Jack Beats')
.full_name_like('Epidemic')
.full_name_like('Dillon Francis')