Class: PwQuery::Service
- Inherits:
-
Object
- Object
- PwQuery::Service
- Defined in:
- lib/pw_query/service.rb
Constant Summary collapse
- DEFAULT_LIMIT =
50
Instance Method Summary collapse
- #apply_aggregation(query, value) ⇒ Object
- #apply_conditions(query, conditions) ⇒ Object
- #apply_date_range(query, value) ⇒ Object
- #apply_field_selection(query, params) ⇒ Object
- #apply_filters(query, filters) ⇒ Object
- #apply_multi_sort(query, value) ⇒ Object
- #apply_pagination(query, params) ⇒ Object
- #apply_range(query, ranges) ⇒ Object
- #apply_search(query, filters) ⇒ Object
- #apply_sort(query, value) ⇒ Object
- #apply_sorting(query, params) ⇒ Object
- #apply_time_range(query, value) ⇒ Object
- #association_condition(query, key, value) ⇒ Object
- #build_filter_conditions(query, filters) ⇒ Object
- #build_join_chain(query, chain) ⇒ Object
- #create_condition(table, attribute, value) ⇒ Object
- #direct_condition(query, key, value) ⇒ Object
- #escape_like(string) ⇒ Object
- #get_column_type(table, attribute) ⇒ Object
- #include_associations(query, associations) ⇒ Object
-
#initialize(model, params, options = {}, associations = nil) ⇒ Service
constructor
A new instance of Service.
- #parse_date(date_string) ⇒ Object
- #parse_time(time_string) ⇒ Object
- #perform ⇒ Object
Constructor Details
#initialize(model, params, options = {}, associations = nil) ⇒ Service
Returns a new instance of Service.
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
# File 'lib/pw_query/service.rb', line 7 def initialize(model, params, = {}, associations = nil) @model = model @params = params @options = @associations = associations @total = @model.count @limit = @params.fetch(:limit, DEFAULT_LIMIT).to_i @meta = { total: @total, limit: @limit, page: @params.fetch(:page, 1).to_i, page_size: @total, total_pages: (@total.to_f / @limit).ceil } end |
Instance Method Details
#apply_aggregation(query, value) ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 |
# File 'lib/pw_query/service.rb', line 210 def apply_aggregation(query, value) return query if value.nil? || value.empty? aggregation_type, aggregation_field = value.split(':') case aggregation_type when 'count' query.count when 'sum' query.sum(aggregation_field) when 'average' query.average(aggregation_field) when 'max' query.maximum(aggregation_field) when 'min' query.minimum(aggregation_field) else query end end |
#apply_conditions(query, conditions) ⇒ Object
174 175 176 177 178 179 180 |
# File 'lib/pw_query/service.rb', line 174 def apply_conditions(query, conditions) return query unless conditions.any? where_clause = conditions.map(&:first).join(' OR ') where_values = conditions.map(&:last).flatten query.where(where_clause, *where_values) end |
#apply_date_range(query, value) ⇒ Object
87 88 89 90 91 |
# File 'lib/pw_query/service.rb', line 87 def apply_date_range(query, value) start_date = value ? parse_date(value['start']) : 1.month.ago.beginning_of_day end_date = value ? parse_date(value['end']) : Time.current query.where(created_at: start_date...end_date) end |
#apply_field_selection(query, params) ⇒ Object
78 79 80 81 82 83 84 85 |
# File 'lib/pw_query/service.rb', line 78 def apply_field_selection(query, params) if params[:projection] || params[:fields] fields = params[:projection] || params[:fields] query.select(fields.split(',')) else query end end |
#apply_filters(query, filters) ⇒ Object
45 46 47 48 49 50 51 52 53 54 55 56 57 |
# File 'lib/pw_query/service.rb', line 45 def apply_filters(query, filters) return query if filters.blank? filters.each do |key, value| next if value.blank? values = value.to_s.split(',').map(&:strip) condition = values.size > 1 ? values : value query = query.where(key => condition) end query end |
#apply_multi_sort(query, value) ⇒ Object
72 73 74 75 76 |
# File 'lib/pw_query/service.rb', line 72 def apply_multi_sort(query, value) sort_params = value.split(',') sorting_criteria = sort_params.map { |param| param.split(':').join(' ') } query.order(sorting_criteria.join(', ')) end |
#apply_pagination(query, params) ⇒ Object
202 203 204 205 206 207 208 |
# File 'lib/pw_query/service.rb', line 202 def apply_pagination(query, params) page = params.fetch(:page, 1).to_i offset_value = page == 1 ? 0 : (page - 1) * @limit total_pages = (@total.to_f / @limit).ceil page <= total_pages ? query.offset(offset_value).limit(@limit) : query.none end |
#apply_range(query, ranges) ⇒ Object
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/pw_query/service.rb', line 182 def apply_range(query, ranges) return query if ranges.blank? ranges.each do |key, value| range_values = value.split(',') min_value = range_values[0].to_f if range_values[0].present? max_value = range_values[1].to_f if range_values[1].present? if min_value.present? && max_value.present? query = query.where("#{key} BETWEEN ? AND ?", min_value, max_value) elsif min_value.present? query = query.where("#{key} >= ?", min_value) elsif max_value.present? query = query.where("#{key} <= ?", max_value) end end query end |
#apply_search(query, filters) ⇒ Object
108 109 110 111 112 113 114 |
# File 'lib/pw_query/service.rb', line 108 def apply_search(query, filters) return query if filters.blank? filters = filters.to_unsafe_h conditions = build_filter_conditions(query, filters) apply_conditions(query, conditions) end |
#apply_sort(query, value) ⇒ Object
67 68 69 70 |
# File 'lib/pw_query/service.rb', line 67 def apply_sort(query, value) sort_field, sort_order = value.split(':') query.order("#{sort_field} #{sort_order}") end |
#apply_sorting(query, params) ⇒ Object
59 60 61 62 63 64 65 |
# File 'lib/pw_query/service.rb', line 59 def apply_sorting(query, params) if params[:sort_by] apply_multi_sort(query, params[:sort_by]) else apply_sort(query, params[:sort] || 'created_at:DESC') end end |
#apply_time_range(query, value) ⇒ Object
93 94 95 96 97 98 99 100 101 102 103 104 105 106 |
# File 'lib/pw_query/service.rb', line 93 def apply_time_range(query, value) return query if value.blank? start_time = parse_time(value['start'] || '00:00:00') end_time = parse_time(value['end'] || '23:59:59') table_name = query.model.table_name if start_time <= end_time query.where("CAST(#{table_name}.created_at AS time) BETWEEN ? AND ?", start_time, end_time) else query.where("CAST(#{table_name}.created_at AS time) >= ? OR CAST(#{table_name}.created_at AS time) <= ?", start_time, end_time) end end |
#association_condition(query, key, value) ⇒ Object
124 125 126 127 128 129 |
# File 'lib/pw_query/service.rb', line 124 def association_condition(query, key, value) association_chain = key.split('.') attribute = association_chain.pop current_table = build_join_chain(query, association_chain) create_condition(current_table, attribute, value) end |
#build_filter_conditions(query, filters) ⇒ Object
116 117 118 119 120 121 122 |
# File 'lib/pw_query/service.rb', line 116 def build_filter_conditions(query, filters) filters.map do |key, value| next if value.blank? key.include?('.') ? association_condition(query, key, value) : direct_condition(query, key, value) end.compact end |
#build_join_chain(query, chain) ⇒ Object
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/pw_query/service.rb', line 131 def build_join_chain(query, chain) current_model = query.klass current_table = current_model.table_name join_chain = [] chain.each do |assoc| reflection = current_model.reflect_on_association(assoc.to_sym) or raise "Unknown association: #{assoc} for model #{current_model}" table_alias = "#{reflection.klass.table_name}_as_#{current_table}" join_chain << { table: reflection.klass.table_name, alias: table_alias, foreign_key: reflection.foreign_key, parent_table: current_table } current_table = table_alias current_model = reflection.klass end query.joins(join_chain.map do |join| "LEFT JOIN #{join[:table]} AS #{join[:alias]} ON #{join[:alias]}.id = #{join[:parent_table]}.#{join[:foreign_key]}" end.join(' ')) end |
#create_condition(table, attribute, value) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 |
# File 'lib/pw_query/service.rb', line 154 def create_condition(table, attribute, value) values = value.to_s.split(',').map(&:strip) column = get_column_type(table, attribute) case column&.type when :string, :text condition = if values.size > 1 values.map do |_v| "LOWER(#{table}.#{attribute}::text) LIKE LOWER(?)" end.join(' OR ') else "LOWER(#{table}.#{attribute}::text) LIKE LOWER(?)" end values = values.size > 1 ? values.map { |v| "%#{escape_like(v)}%" } : ["%#{escape_like(value)}%"] [condition, *values] else values.size > 1 ? ["#{table}.#{attribute} IN (?)", values] : ["#{table}.#{attribute} = ?", value] end end |
#direct_condition(query, key, value) ⇒ Object
150 151 152 |
# File 'lib/pw_query/service.rb', line 150 def direct_condition(query, key, value) create_condition(query.klass.table_name, key, value) end |
#escape_like(string) ⇒ Object
238 239 240 |
# File 'lib/pw_query/service.rb', line 238 def escape_like(string) string.gsub(/[\\%_]/) { |m| "\\#{m}" } end |
#get_column_type(table, attribute) ⇒ Object
231 232 233 234 235 236 |
# File 'lib/pw_query/service.rb', line 231 def get_column_type(table, attribute) klass = table.classify.constantize klass.columns_hash[attribute.to_s] rescue StandardError nil end |
#include_associations(query, associations) ⇒ Object
41 42 43 |
# File 'lib/pw_query/service.rb', line 41 def include_associations(query, associations) query.includes(associations) end |
#parse_date(date_string) ⇒ Object
248 249 250 251 252 |
# File 'lib/pw_query/service.rb', line 248 def parse_date(date_string) Date.parse(date_string) if date_string.present? rescue ArgumentError nil end |
#parse_time(time_string) ⇒ Object
242 243 244 245 246 |
# File 'lib/pw_query/service.rb', line 242 def parse_time(time_string) Time.parse(time_string).strftime('%H:%M:%S') rescue ArgumentError '00:00:00' end |
#perform ⇒ Object
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/pw_query/service.rb', line 23 def perform query = @model.all query = include_associations(query, @associations) query = apply_filters(query, @params[:filters]) query = apply_sorting(query, @params) query = apply_field_selection(query, @params) query = apply_date_range(query, @params[:date_range]) if @options[:date_scope] query = apply_time_range(query, @params[:time_range]) query = apply_search(query, @params[:search]) query = apply_range(query, @params[:range]) query = apply_pagination(query, @params) query = apply_aggregation(query, @params[:aggregation]) handle_query(query) { meta: @meta, data: query } end |