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 »