mirror of
https://github.com/meineerde/redmine.git
synced 2026-02-01 03:57:15 +00:00
"contains any of" operator for text filters to perform OR search of multiple terms (#38435).
Patch by Go MAEDA. git-svn-id: https://svn.redmine.org/redmine/trunk@22188 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
c54070cf18
commit
404a5b1de0
@ -791,8 +791,14 @@ class IssueQuery < Query
|
||||
projects = nil
|
||||
end
|
||||
|
||||
is_all_words =
|
||||
case operator
|
||||
when '~' then true
|
||||
when '|~', '!~' then false
|
||||
end
|
||||
|
||||
fetcher = Redmine::Search::Fetcher.new(
|
||||
question, User.current, ['issue'], projects, all_words: (operator != '!~'), attachments: '0'
|
||||
question, User.current, ['issue'], projects, all_words: is_all_words, attachments: '0'
|
||||
)
|
||||
ids = fetcher.result_ids.map(&:last)
|
||||
if ids.present?
|
||||
|
||||
@ -306,6 +306,7 @@ class Query < ActiveRecord::Base
|
||||
"t-" => :label_ago,
|
||||
"~" => :label_contains,
|
||||
"!~" => :label_not_contains,
|
||||
"|~" => :label_contains_any_of,
|
||||
"^" => :label_starts_with,
|
||||
"$" => :label_ends_with,
|
||||
"=p" => :label_any_issues_in_project,
|
||||
@ -323,9 +324,9 @@ class Query < ActiveRecord::Base
|
||||
:list_subprojects => [ "*", "!*", "=", "!" ],
|
||||
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ],
|
||||
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ],
|
||||
:string => [ "~", "=", "!~", "!", "^", "$", "!*", "*" ],
|
||||
:text => [ "~", "!~", "^", "$", "!*", "*" ],
|
||||
:search => [ "~", "!~" ],
|
||||
:string => [ "~", "|~", "=", "!~", "!", "^", "$", "!*", "*" ],
|
||||
:text => [ "~", "|~", "!~", "^", "$", "!*", "*" ],
|
||||
:search => [ "~", "|~", "!~" ],
|
||||
:integer => [ "=", ">=", "<=", "><", "!*", "*" ],
|
||||
:float => [ "=", ">=", "<=", "><", "!*", "*" ],
|
||||
:relation => ["=", "!", "=p", "=!p", "!p", "*o", "!o", "!*", "*"],
|
||||
@ -1431,6 +1432,8 @@ class Query < ActiveRecord::Base
|
||||
sql = sql_contains("#{db_table}.#{db_field}", value.first)
|
||||
when "!~"
|
||||
sql = sql_contains("#{db_table}.#{db_field}", value.first, :match => false)
|
||||
when "|~"
|
||||
sql = sql_contains("#{db_table}.#{db_field}", value.first, :all_words => false)
|
||||
when "^"
|
||||
sql = sql_contains("#{db_table}.#{db_field}", value.first, :starts_with => true)
|
||||
when "$"
|
||||
@ -1443,6 +1446,12 @@ class Query < ActiveRecord::Base
|
||||
end
|
||||
|
||||
# Returns a SQL LIKE statement with wildcards
|
||||
#
|
||||
# valid options:
|
||||
# * :match - use NOT LIKE if false
|
||||
# * :starts_with - use LIKE 'value%' if true
|
||||
# * :ends_with - use LIKE '%value' if true
|
||||
# * :all_words - use OR instead of AND if false
|
||||
def sql_contains(db_field, value, options={})
|
||||
options = {} unless options.is_a?(Hash)
|
||||
options.symbolize_keys!
|
||||
@ -1465,10 +1474,11 @@ class Query < ActiveRecord::Base
|
||||
def self.tokenized_like_conditions(db_field, value, **options)
|
||||
tokens = Redmine::Search::Tokenizer.new(value).tokens
|
||||
tokens = [value] unless tokens.present?
|
||||
logical_opr = options.delete(:all_words) == false ? ' OR ' : ' AND '
|
||||
sql, values = tokens.map do |token|
|
||||
[Redmine::Database.like(db_field, '?', options), "%#{sanitize_sql_like token}%"]
|
||||
end.transpose
|
||||
[sql.join(" AND "), *values]
|
||||
[sql.join(logical_opr), *values]
|
||||
end
|
||||
# rubocop:enable Lint/IneffectiveAccessModifier
|
||||
|
||||
|
||||
@ -810,6 +810,7 @@ en:
|
||||
label_more_than_ago: more than days ago
|
||||
label_ago: days ago
|
||||
label_contains: contains
|
||||
label_contains_any_of: contains any of
|
||||
label_not_contains: doesn't contain
|
||||
label_starts_with: starts with
|
||||
label_ends_with: ends with
|
||||
|
||||
@ -736,6 +736,37 @@ class QueryTest < ActiveSupport::TestCase
|
||||
assert_not_include issue, result
|
||||
end
|
||||
|
||||
def test_operator_contains_any_of
|
||||
User.current = User.find(1)
|
||||
query = IssueQuery.new(
|
||||
:name => '_',
|
||||
:filters => {
|
||||
'subject' => {
|
||||
:operator => '|~',
|
||||
:values => ['close block']
|
||||
}
|
||||
}
|
||||
)
|
||||
result = find_issues_with_query(query)
|
||||
assert_equal [8, 9, 10, 11, 12], result.map(&:id).sort
|
||||
result.each {|issue| assert issue.subject =~ /(close|block)/i}
|
||||
end
|
||||
|
||||
def test_operator_contains_any_of_with_any_searchable_text
|
||||
User.current = User.find(1)
|
||||
query = IssueQuery.new(
|
||||
:name => '_',
|
||||
:filters => {
|
||||
'any_searchable' => {
|
||||
:operator => '|~',
|
||||
:values => ['recipe categories']
|
||||
}
|
||||
}
|
||||
)
|
||||
result = find_issues_with_query(query)
|
||||
assert_equal [1, 2, 3], result.map(&:id).sort
|
||||
end
|
||||
|
||||
def test_range_for_this_week_with_week_starting_on_monday
|
||||
I18n.locale = :fr
|
||||
assert_equal '1', I18n.t(:general_first_day_of_week)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user