]> source.dussan.org Git - redmine.git/commitdiff
OR search with multiple terms for "starts with" and "ends with" filter operators...
authorGo MAEDA <maeda@farend.jp>
Mon, 17 Apr 2023 23:25:14 +0000 (23:25 +0000)
committerGo MAEDA <maeda@farend.jp>
Mon, 17 Apr 2023 23:25:14 +0000 (23:25 +0000)
Patch by Go MAEDA.

git-svn-id: https://svn.redmine.org/redmine/trunk@22202 e93f8b46-1217-0410-a6f0-8f06a7374b81

app/models/query.rb
test/unit/query_test.rb

index 7764fede1c18be0f2c824827b6e5314999496aa6..00d99c24f6cf3144b3b5273604879478a5a04c10 100644 (file)
@@ -1452,31 +1452,33 @@ class Query < ActiveRecord::Base
   # * :starts_with - use LIKE 'value%' if true
   # * :ends_with - use LIKE '%value' if true
   # * :all_words - use OR instead of AND if false
+  #   (ignored if :starts_with or :ends_with is true)
   def sql_contains(db_field, value, options={})
     options = {} unless options.is_a?(Hash)
     options.symbolize_keys!
-    prefix = suffix = nil
-    prefix = '%' if options[:ends_with]
-    suffix = '%' if options[:starts_with]
-    if prefix || suffix
-      value = queried_class.sanitize_sql_like value
-      queried_class.sanitize_sql_for_conditions(
-        [Redmine::Database.like(db_field, '?', :match => options[:match]), "#{prefix}#{value}#{suffix}"]
-      )
-    else
-      queried_class.sanitize_sql_for_conditions(
-        ::Query.tokenized_like_conditions(db_field, value, **options)
-      )
-    end
+    queried_class.sanitize_sql_for_conditions(
+      ::Query.tokenized_like_conditions(db_field, value, **options)
+    )
   end
 
   # rubocop:disable Lint/IneffectiveAccessModifier
   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 '
+
+    if options[:starts_with]
+      prefix, suffix = nil, '%'
+      logical_opr = ' OR '
+    elsif options[:ends_with]
+      prefix, suffix = '%', nil
+      logical_opr = ' OR '
+    else
+      prefix = suffix = '%'
+      logical_opr = options[:all_words] == false ? ' OR ' : ' AND '
+    end
+
     sql, values = tokens.map do |token|
-      [Redmine::Database.like(db_field, '?', options), "%#{sanitize_sql_like token}%"]
+      [Redmine::Database.like(db_field, '?', options), "#{prefix}#{sanitize_sql_like token}#{suffix}"]
     end.transpose
     [sql.join(logical_opr), *values]
   end
index 164a7d304974e446ac5239da093d4686a7b7699c..638ab5b175ea29d325192014553c310c688b8abe 100644 (file)
@@ -3086,6 +3086,34 @@ class QueryTest < ActiveSupport::TestCase
     assert_equal 1, query.issue_count
   end
 
+  def test_sql_contains_should_tokenize_for_starts_with
+    query = IssueQuery.new(
+      :project => nil, :name => '_',
+      :filters => {
+        'subject' => {:operator => '^', :values => ['issue closed']}
+      }
+    )
+
+    assert_equal 4, query.issue_count
+    query.issues.each do |issue|
+      assert_match /^(issue|closed)/i, issue.subject
+    end
+  end
+
+  def test_sql_contains_should_tokenize_for_ends_with
+    query = IssueQuery.new(
+      :project => nil, :name => '_',
+      :filters => {
+        'subject' => {:operator => '$', :values => ['version issue']}
+      }
+    )
+
+    assert_equal 4, query.issue_count
+    query.issues.each do |issue|
+      assert_match /(version|issue)$/i, issue.subject
+    end
+  end
+
   def test_display_type_should_accept_known_types
     query = ProjectQuery.new(:name => '_')
     query.display_type = 'list'