Project

General

Profile

Patch #43917 » 0001-Add-default-any_searchable-filter.patch

Mizuki ISHIKAWA, 2026-03-31 07:43

View differences:

app/models/issue_query.rb
103 103

  
104 104
  def initialize(attributes=nil, *args)
105 105
    super(attributes)
106
    self.filters ||= {'status_id' => {:operator => "o", :values => [""]}}
106
    self.filters ||= {
107
      'status_id' => {:operator => "o", :values => [""]},
108
      'any_searchable' => {:operator => "~", :values => [""]}
109
    }
107 110
  end
108 111

  
109 112
  def draw_relations
app/models/query.rb
347 347
    :tree => ["=", "~", "!*", "*"]
348 348
  }
349 349

  
350
  NO_VALUE_REQUIRED_OPERATORS = %w[o c !* * nd t ld nw w lw l2w nm m lm y *o !o].freeze
351

  
350 352
  class_attribute :available_columns
351 353
  self.available_columns = []
352 354

  
......
526 528
          # filter requires one or more values
527 529
          (values_for(field) and values_for(field).first.present?) or
528 530
          # filter doesn't require any value
529
          ["o", "c", "!*", "*", "nd", "t", "ld", "nw", "w", "lw", "l2w", "nm", "m", "lm", "y", "*o", "!o"].include? operator_for(field)
531
          NO_VALUE_REQUIRED_OPERATORS.include?(operator_for(field)) or
532
          # filter with blank value is skipped only for :search filters
533
          skip_filter_for_blank_value?(field, operator_for(field), values_for(field))
530 534
    end if filters
531 535
  end
532 536

  
......
993 997
      next unless v and !v.empty?
994 998

  
995 999
      operator = operator_for(field)
1000
      next if skip_filter_for_blank_value?(field, operator, v)
996 1001

  
997 1002
      # "me" value substitution
998 1003
      if %w(assigned_to_id author_id user_id watcher_id updated_by last_updated_by).include?(field)
......
1099 1104

  
1100 1105
  private
1101 1106

  
1107
  def skip_filter_for_blank_value?(field, operator, values)
1108
    return false unless type_for(field) == :search
1109
    return false if NO_VALUE_REQUIRED_OPERATORS.include?(operator)
1110

  
1111
    values = Array(values)
1112
    values.none?(&:present?)
1113
  end
1114

  
1102 1115
  def grouped_query(&)
1103 1116
    r = nil
1104 1117
    if grouped?
test/functional/issues_controller_test.rb
157 157
    assert_response :success
158 158

  
159 159
    # default filter
160
    assert_query_filters [['status_id', 'o', '']]
160
    assert_query_filters [
161
      ['status_id', 'o', ''],
162
      ['any_searchable', '~', '']
163
    ]
161 164
  end
162 165

  
163 166
  def test_index_with_project_and_filter
......
237 240
        '*' => {:op => '*', :values => ['']}
238 241
      }
239 242
    }
240
    default_filter = {'status_id' => {:operator => 'o', :values => ['']}}
243
    default_filter = {
244
      'status_id' => {:operator => 'o', :values => ['']},
245
      'any_searchable' => {:operator => '~', :values => ['']}
246
    }
241 247
    to_test.each do |field, expression_and_expected|
242 248
      expression_and_expected.each do |filter_expression, expected|
243 249
        get(:index, :params => {:set_filter => 1, field => filter_expression})
test/unit/query_test.rb
1010 1010
    assert_equal [1, 3], find_issues_with_query(query).map(&:id).sort
1011 1011
  end
1012 1012

  
1013
  def test_filter_that_does_not_require_value_should_not_be_skipped
1014
    open_issue = Issue.generate!(:status => IssueStatus.where(:is_closed => false).first)
1015
    closed_issue = Issue.generate!(:status => IssueStatus.where(:is_closed => true).first)
1016

  
1017
    query = IssueQuery.new(
1018
      :name => '_',
1019
      :filters => {
1020
        'status_id' => {:operator => 'o', :values => ['']}
1021
      }
1022
    )
1023

  
1024
    assert query.valid?
1025
    assert_includes query.issues, open_issue
1026
    assert_not_includes query.issues, closed_issue
1027
  end
1028

  
1013 1029
  def test_filter_any_searchable
1014 1030
    User.current = User.find(1)
1015 1031
    query = IssueQuery.new(
......
1025 1041
    assert_equal [1, 2, 3], result.map(&:id).sort
1026 1042
  end
1027 1043

  
1044
  def test_filter_any_searchable_with_blank_value_should_be_noop
1045
    User.current = User.find(1)
1046

  
1047
    base_query = IssueQuery.new(
1048
      :name => '_',
1049
      :filters => {
1050
        'status_id' => {:operator => 'o', :values => ['']}
1051
      }
1052
    )
1053
    query = IssueQuery.new(
1054
      :name => '_',
1055
      :filters => {
1056
        'status_id' => {:operator => 'o', :values => ['']},
1057
        'any_searchable' => {:operator => '~', :values => ['']}
1058
      }
1059
    )
1060

  
1061
    assert query.valid?
1062
    assert_equal base_query.statement, query.statement
1063
    assert_equal base_query.issues.pluck(:id).sort, query.issues.pluck(:id).sort
1064
  end
1065

  
1066
  def test_filter_any_searchable_with_partially_blank_value_should_be_invalid
1067
    set_language_if_valid 'en'
1068
    User.current = User.find(1)
1069

  
1070
    query = IssueQuery.new(
1071
      :name => '_',
1072
      :filters => {
1073
        'status_id' => {:operator => 'o', :values => ['']},
1074
        'any_searchable' => {:operator => '><', :values => ['', 'recipe']}
1075
      }
1076
    )
1077

  
1078
    assert_not query.valid?
1079
    assert_include 'Any searchable text cannot be blank', query.errors.full_messages
1080
  end
1081

  
1028 1082
  def test_filter_any_searchable_with_multiple_words
1029 1083
    User.current = User.find(1)
1030 1084
    query = IssueQuery.new(
(2-2/2)