1
0
mirror of https://github.com/meineerde/redmine.git synced 2026-02-06 09:03:25 +00:00

Use custom SQL for MySQL to behave like others DBMS: case-insensitive search without ignoring accentuation (#18537).

git-svn-id: http://svn.redmine.org/redmine/trunk@13759 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
Jean-Philippe Lang 2014-12-14 13:55:52 +00:00
parent 9c685f5034
commit 24ea953822
2 changed files with 35 additions and 15 deletions

View File

@ -86,7 +86,7 @@ module Redmine
columns = searchable_options[:columns]
columns = columns[0..0] if options[:titles_only]
token_clauses = columns.collect {|column| "(LOWER(#{column}) LIKE LOWER(?))"}
token_clauses = columns.collect {|column| "(#{search_token_match_statement(column)})"}
if !options[:titles_only] && searchable_options[:search_custom_fields]
searchable_custom_fields = CustomField.where(:type => "#{self.name}CustomField", :searchable => true)
@ -97,8 +97,9 @@ module Redmine
fields_by_visibility.each do |visibility, fields|
ids = fields.map(&:id).join(',')
sql = "#{table_name}.id IN (SELECT cfs.customized_id FROM #{CustomValue.table_name} cfs" +
" WHERE cfs.customized_type='#{self.name}' AND cfs.customized_id=#{table_name}.id AND LOWER(cfs.value) LIKE LOWER(?)" +
" WHERE cfs.customized_type='#{self.name}' AND cfs.customized_id=#{table_name}.id" +
" AND cfs.custom_field_id IN (#{ids})" +
" AND #{search_token_match_statement('cfs.value')}" +
" AND #{visibility})"
token_clauses << sql
end
@ -108,6 +109,28 @@ module Redmine
tokens_conditions = [sql, * (tokens.collect {|w| "%#{w}%"} * token_clauses.size).sort]
search_scope(user, projects).
reorder(searchable_options[:date_column] => :desc, :id => :desc).
where(tokens_conditions).
limit(options[:limit]).
uniq.
pluck(searchable_options[:date_column], :id)
end
def search_token_match_statement(column, value='?')
case connection.adapter_name
when /postgresql/i
"#{column} ILIKE #{value}"
when /mysql/i
"LOWER(#{column}) COLLATE utf8_bin LIKE LOWER(#{value})"
else
"#{column} LIKE #{value}"
end
end
private :search_token_match_statement
# Returns the search scope for user and projects
def search_scope(user, projects)
scope = (searchable_options[:scope] || self)
if scope.is_a? Proc
scope = scope.call
@ -120,21 +143,12 @@ module Redmine
scope = scope.where(Project.allowed_to_condition(user, permission))
end
# TODO: use visible scope options instead
if projects
scope = scope.where("#{searchable_options[:project_key]} IN (?)", projects.map(&:id))
end
results = []
results_count = 0
scope.
reorder(searchable_options[:date_column] => :desc, :id => :desc).
where(tokens_conditions).
limit(options[:limit]).
uniq.
pluck(searchable_options[:date_column], :id)
scope
end
private :search_scope
# Returns search results of given ids
def search_results_from_ids(ids)

View File

@ -143,9 +143,15 @@ class SearchTest < ActiveSupport::TestCase
end
def test_search_should_not_use_ruby_downcase
issue = Issue.generate!(:subject => "Special chars: ÖÖ")
issue1 = Issue.generate!(:subject => "Special chars: ÖÖ")
issue2 = Issue.generate!(:subject => "Special chars: Öö")
Issue.generate!(:subject => "Special chars: oo")
Issue.generate!(:subject => "Special chars: OO")
r = Issue.search_results('%ÖÖ%')
assert_include issue, r
assert_include issue1, r
assert_include issue2, r
assert_equal 2, r.size
end
private