Class: ForestLiana::FiltersParser
- Inherits:
-
Object
- Object
- ForestLiana::FiltersParser
- Defined in:
- app/services/forest_liana/filters_parser.rb
Constant Summary collapse
- AGGREGATOR_OPERATOR =
%w(and or).freeze
Instance Method Summary collapse
- #apply_filters ⇒ Object
- #apply_filters_on_previous_interval(previous_condition) ⇒ Object
- #ensure_valid_aggregation(node) ⇒ Object
- #ensure_valid_condition(condition) ⇒ Object
- #get_association_field_and_resource(field_name) ⇒ Object
- #get_association_name_for_condition(field) ⇒ Object
-
#get_previous_interval_condition ⇒ Object
NOTICE: Look for a previous interval condition matching the following: - If the filter is a simple condition at the root the check is done right away.
-
#initialize(filters, resource, timezone, params = nil) ⇒ FiltersParser
constructor
A new instance of FiltersParser.
- #is_belongs_to(field) ⇒ Object
- #parse_aggregation(node) ⇒ Object
- #parse_aggregation_on_previous_interval(node, previous_condition) ⇒ Object
- #parse_aggregation_operator(aggregator_operator) ⇒ Object
- #parse_condition(condition) ⇒ Object
- #parse_condition_without_smart_field(condition) ⇒ Object
- #parse_field_name(field) ⇒ Object
- #parse_operator(operator) ⇒ Object
- #parse_previous_interval_condition(condition) ⇒ Object
- #parse_value(operator, value) ⇒ Object
- #raise_empty_condition_in_filter_error ⇒ Object
- #raise_unknown_operator_error(operator) ⇒ Object
Constructor Details
#initialize(filters, resource, timezone, params = nil) ⇒ FiltersParser
Returns a new instance of FiltersParser.
5 6 7 8 9 10 11 |
# File 'app/services/forest_liana/filters_parser.rb', line 5 def initialize(filters, resource, timezone, params = nil) @filters = filters @params = params @resource = resource @operator_date_parser = OperatorDateIntervalParser.new(timezone) @joins = [] end |
Instance Method Details
#apply_filters ⇒ Object
13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'app/services/forest_liana/filters_parser.rb', line 13 def apply_filters return @resource unless @filters where_clause = parse_aggregation(@filters) return @resource unless where_clause @joins.each do |join| @resource = @resource.eager_load(join.name) end @resource.where(where_clause) end |
#apply_filters_on_previous_interval(previous_condition) ⇒ Object
231 232 233 234 235 236 |
# File 'app/services/forest_liana/filters_parser.rb', line 231 def apply_filters_on_previous_interval(previous_condition) # Ressource should have already been joined where = parse_aggregation_on_previous_interval(@filters, previous_condition) @resource.where(where) end |
#ensure_valid_aggregation(node) ⇒ Object
276 277 278 279 |
# File 'app/services/forest_liana/filters_parser.rb', line 276 def ensure_valid_aggregation(node) raise ForestLiana::Errors::HTTP422Error.new('Filters cannot be a raw value') unless node.is_a?(Hash) raise_empty_condition_in_filter_error if node.empty? end |
#ensure_valid_condition(condition) ⇒ Object
281 282 283 284 285 286 287 |
# File 'app/services/forest_liana/filters_parser.rb', line 281 def ensure_valid_condition(condition) raise_empty_condition_in_filter_error if condition.empty? raise ForestLiana::Errors::HTTP422Error.new('Condition cannot be a raw value') unless condition.is_a?(Hash) unless condition['field'].is_a?(String) and condition['operator'].is_a?(String) raise ForestLiana::Errors::HTTP422Error.new('Invalid condition format') end end |
#get_association_field_and_resource(field_name) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 |
# File 'app/services/forest_liana/filters_parser.rb', line 62 def get_association_field_and_resource(field_name) if is_belongs_to(field_name) association = field_name.partition(':').first.to_sym association_field = field_name.partition(':').last unless @resource.reflect_on_association(association) raise ForestLiana::Errors::HTTP422Error.new("Association '#{association}' not found") end current_resource = @resource.reflect_on_association(association).klass return association_field, current_resource else return field_name, @resource end end |
#get_association_name_for_condition(field) ⇒ Object
191 192 193 194 195 196 197 198 199 200 |
# File 'app/services/forest_liana/filters_parser.rb', line 191 def get_association_name_for_condition(field) field, subfield = field.split(':') association = @resource.reflect_on_association(field.to_sym) return nil if association.blank? @joins << association unless @joins.include? association association.name end |
#get_previous_interval_condition ⇒ Object
NOTICE: Look for a previous interval condition matching the following:
- If the filter is a simple condition at the root the check is done right away.
- There can't be a previous interval condition if the aggregator is 'or' (no meaning).
- The condition's operator has to be elligible for a previous interval.
- There can't be two previous interval condition.
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'app/services/forest_liana/filters_parser.rb', line 207 def get_previous_interval_condition current_previous_interval = nil # NOTICE: Leaf condition at root unless @filters['aggregator'] return @filters if @operator_date_parser.has_previous_interval?(@filters['operator']) end if @filters['aggregator'] === 'and' @filters['conditions'].each do |condition| # NOTICE: Nested conditions return nil if condition['aggregator'] if @operator_date_parser.has_previous_interval?(condition['operator']) # NOTICE: There can't be two previous_interval. return nil if current_previous_interval current_previous_interval = condition end end end current_previous_interval end |
#is_belongs_to(field) ⇒ Object
187 188 189 |
# File 'app/services/forest_liana/filters_parser.rb', line 187 def is_belongs_to(field) field.include?(':') end |
#parse_aggregation(node) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'app/services/forest_liana/filters_parser.rb', line 26 def parse_aggregation(node) ensure_valid_aggregation(node) return parse_condition(node) unless node['aggregator'] conditions = [] node['conditions'].each do |condition| conditions.push(parse_aggregation(condition)) end operator = parse_aggregation_operator(node['aggregator']) conditions.empty? ? nil : "(#{conditions.join(" #{operator} ")})" end |
#parse_aggregation_on_previous_interval(node, previous_condition) ⇒ Object
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 |
# File 'app/services/forest_liana/filters_parser.rb', line 238 def parse_aggregation_on_previous_interval(node, previous_condition) raise_empty_condition_in_filter_error unless node return parse_previous_interval_condition(node) unless node['aggregator'] conditions = [] node['conditions'].each do |condition| if condition == previous_condition conditions.push(parse_previous_interval_condition(condition)) else conditions.push(parse_aggregation(condition)) end end operator = parse_aggregation_operator(node['aggregator']) conditions.empty? ? nil : "(#{conditions.join(" #{operator} ")})" end |
#parse_aggregation_operator(aggregator_operator) ⇒ Object
107 108 109 110 111 112 113 |
# File 'app/services/forest_liana/filters_parser.rb', line 107 def parse_aggregation_operator(aggregator_operator) unless AGGREGATOR_OPERATOR.include?(aggregator_operator) raise_unknown_operator_error(aggregator_operator) end aggregator_operator.upcase end |
#parse_condition(condition) ⇒ Object
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'app/services/forest_liana/filters_parser.rb', line 41 def parse_condition(condition) where = parse_condition_without_smart_field(condition) field_name = condition['field'] if ForestLiana::SchemaHelper.is_smart_field?(@resource, field_name) schema = ForestLiana.schema_for_resource(@resource) field_schema = schema.fields.find do |field| field[:field].to_s == field_name end unless field_schema.try(:[], :filter) raise ForestLiana::Errors::NotImplementedMethodError.new("method filter on smart field '#{field_name}' not found") end return field_schema[:filter].call(condition, where) end where end |
#parse_condition_without_smart_field(condition) ⇒ Object
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
# File 'app/services/forest_liana/filters_parser.rb', line 79 def parse_condition_without_smart_field(condition) ensure_valid_condition(condition) operator = condition['operator'] value = condition['value'] field_name = condition['field'] if @operator_date_parser.is_date_operator?(operator) condition = @operator_date_parser.get_date_filter(operator, value) return "#{parse_field_name(field_name)} #{condition}" end association_field, current_resource = get_association_field_and_resource(field_name) # NOTICE: Set the integer value instead of a string if "enum" type # NOTICE: Rails 3 do not have a defined_enums method if current_resource.respond_to?(:defined_enums) && current_resource.defined_enums.has_key?(association_field) value = current_resource.defined_enums[association_field][value] end parsed_field = parse_field_name(field_name) parsed_operator = parse_operator(operator) parsed_value = parse_value(operator, value) field_and_operator = "#{parsed_field} #{parsed_operator}" sanitize_condition(field_and_operator, operator, parsed_value) end |
#parse_field_name(field) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 |
# File 'app/services/forest_liana/filters_parser.rb', line 164 def parse_field_name(field) if is_belongs_to(field) current_resource = @resource.reflect_on_association(field.split(':').first.to_sym)&.klass raise ForestLiana::Errors::HTTP422Error.new("Field '#{field}' not found") unless current_resource get_association_name_for_condition(field) quoted_table_name = current_resource.table_name field_name = field.split(':')[1] else quoted_table_name = @resource.quoted_table_name current_resource = @resource field_name = field end quoted_field_name = ActiveRecord::Base.connection.quote_column_name(field_name) column_found = current_resource.columns.find { |column| column.name == field.split(':').last } if column_found.nil? && !ForestLiana::SchemaHelper.is_smart_field?(current_resource, field_name) raise ForestLiana::Errors::HTTP422Error.new("Field '#{field}' not found") end "#{quoted_table_name}.#{quoted_field_name}" end |
#parse_operator(operator) ⇒ Object
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 |
# File 'app/services/forest_liana/filters_parser.rb', line 115 def parse_operator(operator) case operator when 'not' 'NOT' when 'greater_than', 'after' '>' when 'less_than', 'before' '<' when 'contains', 'starts_with', 'ends_with' 'LIKE' when 'not_contains' 'NOT LIKE' when 'not_equal' '!=' when 'equal' '=' when 'blank' 'IS' when 'present' 'IS NOT' when 'in' 'IN' else raise_unknown_operator_error(operator) end end |
#parse_previous_interval_condition(condition) ⇒ Object
257 258 259 260 261 262 263 264 265 266 |
# File 'app/services/forest_liana/filters_parser.rb', line 257 def parse_previous_interval_condition(condition) raise_empty_condition_in_filter_error unless condition parsed_condition = @operator_date_parser.get_date_filter_for_previous_interval( condition['operator'], condition['value'] ) "#{parse_field_name(condition['field'])} #{parsed_condition}" end |
#parse_value(operator, value) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'app/services/forest_liana/filters_parser.rb', line 142 def parse_value(operator, value) case operator when 'not', 'greater_than', 'less_than', 'not_equal', 'equal', 'before', 'after' value when 'contains', 'not_contains' "%#{value}%" when 'starts_with' "#{value}%" when 'ends_with' "%#{value}" when 'in' if value.kind_of?(String) value.split(',').map { |val| val.strip() } else value end when 'present', 'blank' else raise_unknown_operator_error(operator) end end |
#raise_empty_condition_in_filter_error ⇒ Object
272 273 274 |
# File 'app/services/forest_liana/filters_parser.rb', line 272 def raise_empty_condition_in_filter_error raise ForestLiana::Errors::HTTP422Error.new('Empty condition in filter') end |
#raise_unknown_operator_error(operator) ⇒ Object
268 269 270 |
# File 'app/services/forest_liana/filters_parser.rb', line 268 def raise_unknown_operator_error(operator) raise ForestLiana::Errors::HTTP422Error.new("Unknown provided operator '#{operator}'") end |