2762: Fix wrong class used in test case.
[arvados.git] / services / api / lib / record_filters.rb
1 # Mixin module providing a method to convert filters into a list of SQL
2 # fragments suitable to be fed to ActiveRecord #where.
3 #
4 # Expects:
5 #   model_class
6 # Operates on:
7 #   @objects
8 module RecordFilters
9
10   # Input:
11   # +filters+        array of conditions, each being [column, operator, operand]
12   # +ar_table_name+  name of SQL table
13   #
14   # Output:
15   # Hash with two keys:
16   # :cond_out  array of SQL fragments for each filter expression
17   # :param_out  array of values for parameter substitution in cond_out
18   def record_filters filters, ar_table_name
19     cond_out = []
20     param_out = []
21
22     filters.each do |filter|
23       attr, operator, operand = filter
24       if !filter.is_a? Array
25         raise ArgumentError.new("Invalid element in filters array: #{filter.inspect} is not an array")
26       elsif !operator.is_a? String
27         raise ArgumentError.new("Invalid operator '#{operator}' (#{operator.class}) in filter")
28       elsif !model_class.searchable_columns(operator).index attr.to_s
29         raise ArgumentError.new("Invalid attribute '#{attr}' in filter")
30       end
31       case operator.downcase
32       when '=', '<', '<=', '>', '>=', '!=', 'like'
33         if operand.is_a? String
34           if operator == '!='
35             operator = '<>'
36           end
37           cond_out << "#{ar_table_name}.#{attr} #{operator} ?"
38           if (# any operator that operates on value rather than
39               # representation:
40               operator.match(/[<=>]/) and
41               model_class.attribute_column(attr).type == :datetime)
42             operand = Time.parse operand
43           end
44           param_out << operand
45         elsif operand.nil? and operator == '='
46           cond_out << "#{ar_table_name}.#{attr} is null"
47         elsif operand.nil? and operator == '!='
48           cond_out << "#{ar_table_name}.#{attr} is not null"
49         else
50           raise ArgumentError.new("Invalid operand type '#{operand.class}' "\
51                                   "for '#{operator}' operator in filters")
52         end
53       when 'in', 'not in'
54         if operand.is_a? Array
55           cond_out << "#{ar_table_name}.#{attr} #{operator} (?)"
56           param_out << operand
57         else
58           raise ArgumentError.new("Invalid operand type '#{operand.class}' "\
59                                   "for '#{operator}' operator in filters")
60         end
61       when 'is_a'
62         operand = [operand] unless operand.is_a? Array
63         cond = []
64         operand.each do |op|
65           cl = ArvadosModel::kind_class op
66           if cl
67             cond << "#{ar_table_name}.#{attr} like ?"
68             param_out << cl.uuid_like_pattern
69           else
70             cond << "1=0"
71           end
72         end
73         cond_out << cond.join(' OR ')
74       end
75     end
76
77     {:cond_out => cond_out, :param_out => param_out}
78   end
79
80 end