Feature #38435 » contains-any-of.patch
app/models/issue_query.rb | ||
---|---|---|
791 | 791 |
projects = nil |
792 | 792 |
end |
793 | 793 | |
794 |
is_all_words = |
|
795 |
case operator |
|
796 |
when '~' then true |
|
797 |
when '|~', '!~' then false |
|
798 |
end |
|
799 | ||
794 | 800 |
fetcher = Redmine::Search::Fetcher.new( |
795 |
question, User.current, ['issue'], projects, all_words: (operator != '!~'), attachments: '0'
|
|
801 |
question, User.current, ['issue'], projects, all_words: is_all_words, attachments: '0'
|
|
796 | 802 |
) |
797 | 803 |
ids = fetcher.result_ids.map(&:last) |
798 | 804 |
if ids.present? |
app/models/query.rb | ||
---|---|---|
306 | 306 |
"t-" => :label_ago, |
307 | 307 |
"~" => :label_contains, |
308 | 308 |
"!~" => :label_not_contains, |
309 |
"|~" => :label_contains_any_of, |
|
309 | 310 |
"^" => :label_starts_with, |
310 | 311 |
"$" => :label_ends_with, |
311 | 312 |
"=p" => :label_any_issues_in_project, |
... | ... | |
323 | 324 |
:list_subprojects => [ "*", "!*", "=", "!" ], |
324 | 325 |
:date => [ "=", ">=", "<=", "><", "<t+", ">t+", "><t+", "t+", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", ">t-", "<t-", "><t-", "t-", "!*", "*" ], |
325 | 326 |
:date_past => [ "=", ">=", "<=", "><", ">t-", "<t-", "><t-", "t-", "t", "ld", "w", "lw", "l2w", "m", "lm", "y", "!*", "*" ], |
326 |
:string => [ "~", "=", "!~", "!", "^", "$", "!*", "*" ], |
|
327 |
:text => [ "~", "!~", "^", "$", "!*", "*" ], |
|
328 |
:search => [ "~", "!~" ], |
|
327 |
:string => [ "~", "|~", "=", "!~", "!", "^", "$", "!*", "*" ],
|
|
328 |
:text => [ "~", "|~", "!~", "^", "$", "!*", "*" ],
|
|
329 |
:search => [ "~", "|~", "!~" ],
|
|
329 | 330 |
:integer => [ "=", ">=", "<=", "><", "!*", "*" ], |
330 | 331 |
:float => [ "=", ">=", "<=", "><", "!*", "*" ], |
331 | 332 |
:relation => ["=", "!", "=p", "=!p", "!p", "*o", "!o", "!*", "*"], |
... | ... | |
1431 | 1432 |
sql = sql_contains("#{db_table}.#{db_field}", value.first) |
1432 | 1433 |
when "!~" |
1433 | 1434 |
sql = sql_contains("#{db_table}.#{db_field}", value.first, :match => false) |
1435 |
when "|~" |
|
1436 |
sql = sql_contains("#{db_table}.#{db_field}", value.first, :all_words => false) |
|
1434 | 1437 |
when "^" |
1435 | 1438 |
sql = sql_contains("#{db_table}.#{db_field}", value.first, :starts_with => true) |
1436 | 1439 |
when "$" |
... | ... | |
1443 | 1446 |
end |
1444 | 1447 | |
1445 | 1448 |
# Returns a SQL LIKE statement with wildcards |
1449 |
# |
|
1450 |
# valid options: |
|
1451 |
# * :match - use NOT LIKE if false |
|
1452 |
# * :starts_with - use LIKE 'value%' if true |
|
1453 |
# * :ends_with - use LIKE '%value' if true |
|
1454 |
# * :all_words - use OR instead of AND if false |
|
1446 | 1455 |
def sql_contains(db_field, value, options={}) |
1447 | 1456 |
options = {} unless options.is_a?(Hash) |
1448 | 1457 |
options.symbolize_keys! |
... | ... | |
1465 | 1474 |
def self.tokenized_like_conditions(db_field, value, **options) |
1466 | 1475 |
tokens = Redmine::Search::Tokenizer.new(value).tokens |
1467 | 1476 |
tokens = [value] unless tokens.present? |
1477 |
logical_opr = options.delete(:all_words) == false ? ' OR ' : ' AND ' |
|
1468 | 1478 |
sql, values = tokens.map do |token| |
1469 | 1479 |
[Redmine::Database.like(db_field, '?', options), "%#{sanitize_sql_like token}%"] |
1470 | 1480 |
end.transpose |
1471 |
[sql.join(" AND "), *values]
|
|
1481 |
[sql.join(logical_opr), *values]
|
|
1472 | 1482 |
end |
1473 | 1483 |
# rubocop:enable Lint/IneffectiveAccessModifier |
1474 | 1484 |
config/locales/en.yml | ||
---|---|---|
810 | 810 |
label_more_than_ago: more than days ago |
811 | 811 |
label_ago: days ago |
812 | 812 |
label_contains: contains |
813 |
label_contains_any_of: contains any of |
|
813 | 814 |
label_not_contains: doesn't contain |
814 | 815 |
label_starts_with: starts with |
815 | 816 |
label_ends_with: ends with |
test/unit/query_test.rb | ||
---|---|---|
710 | 710 |
assert_not_include issue, result |
711 | 711 |
end |
712 | 712 | |
713 |
def test_operator_contains_any |
|
714 |
User.current = User.find(1) |
|
715 |
query = IssueQuery.new( |
|
716 |
:name => '_', |
|
717 |
:filters => { |
|
718 |
'subject' => { |
|
719 |
:operator => '|~', |
|
720 |
:values => ['close block'] |
|
721 |
} |
|
722 |
} |
|
723 |
) |
|
724 |
result = find_issues_with_query(query) |
|
725 |
assert_equal [8, 9, 10, 11, 12], result.map(&:id).sort |
|
726 |
result.each {|issue| assert issue.subject =~ /(close|block)/i} |
|
727 |
end |
|
728 | ||
729 |
def test_operator_contains_any_with_any_searchable_text |
|
730 |
User.current = User.find(1) |
|
731 |
query = IssueQuery.new( |
|
732 |
:name => '_', |
|
733 |
:filters => { |
|
734 |
'any_searchable' => { |
|
735 |
:operator => '|~', |
|
736 |
:values => ['recipe categories'] |
|
737 |
} |
|
738 |
} |
|
739 |
) |
|
740 |
result = find_issues_with_query(query) |
|
741 |
assert_equal [1, 2, 3], result.map(&:id).sort |
|
742 |
end |
|
743 | ||
713 | 744 |
def test_range_for_this_week_with_week_starting_on_monday |
714 | 745 |
I18n.locale = :fr |
715 | 746 |
assert_equal '1', I18n.t(:general_first_day_of_week) |
- « Previous
- 1
- 2
- Next »