Merge branch '5554-delete-job-log-rows-wip'
[arvados.git] / services / api / lib / record_filters.rb
index 100e9fc8e206d2b2d25d78a99d09a309aa4b9e10..350c3802fc60e606fd0dba703429ca7dc98f847e 100644 (file)
@@ -22,7 +22,7 @@ module RecordFilters
     ar_table_name = model_class.table_name
     filters.each do |filter|
       attrs_in, operator, operand = filter
-      if attrs_in == 'any'
+      if attrs_in == 'any' && operator != '@@'
         attrs = model_class.searchable_columns(operator)
       elsif attrs_in.is_a? Array
         attrs = attrs_in
@@ -34,13 +34,31 @@ module RecordFilters
       elsif !operator.is_a? String
         raise ArgumentError.new("Invalid operator '#{operator}' (#{operator.class}) in filter")
       end
+
       cond_out = []
+
+      if operator == '@@'
+        # Full-text search
+        if attrs_in != 'any'
+          raise ArgumentError.new("Full text search on individual columns is not supported")
+        end
+        if operand.is_a? Array
+          raise ArgumentError.new("Full text search not supported for array operands")
+        end
+
+        # Skip the generic per-column operator loop below
+        attrs = []
+        # Use to_tsquery since plainto_tsquery does not support prefix
+        # search. And, split operand and join the words with ' & '
+        cond_out << model_class.full_text_tsvector+" @@ to_tsquery(?)"
+        param_out << operand.split.join(' & ')
+      end
       attrs.each do |attr|
         if !model_class.searchable_columns(operator).index attr.to_s
           raise ArgumentError.new("Invalid attribute '#{attr}' in filter")
         end
         case operator.downcase
-        when '=', '<', '<=', '>', '>=', '!=', 'like'
+        when '=', '<', '<=', '>', '>=', '!=', 'like', 'ilike'
           attr_type = model_class.attribute_column(attr).type
           operator = '<>' if operator == '!='
           if operand.is_a? String
@@ -59,7 +77,12 @@ module RecordFilters
                                     "boolean attribute '#{attr}'")
               end
             end
-            cond_out << "#{ar_table_name}.#{attr} #{operator} ?"
+            if operator == '<>'
+              # explicitly allow NULL
+              cond_out << "#{ar_table_name}.#{attr} #{operator} ? OR #{ar_table_name}.#{attr} IS NULL"
+            else
+              cond_out << "#{ar_table_name}.#{attr} #{operator} ?"
+            end
             if (# any operator that operates on value rather than
                 # representation:
                 operator.match(/[<=>]/) and (attr_type == :datetime))