2019-07-10 21:46:42 +12:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module AceOfBase
|
|
|
|
# A query builder for querying our data.
|
|
|
|
class Query
|
2019-07-12 09:03:28 +12:00
|
|
|
require 'ace_of_base/query/operator'
|
|
|
|
require 'ace_of_base/query/or'
|
|
|
|
require 'ace_of_base/query/and'
|
|
|
|
require 'ace_of_base/query/eq'
|
|
|
|
|
|
|
|
include ValidField
|
|
|
|
|
|
|
|
INVALID_AGGREGATION_MESSAGE = 'Operation %s is not a valid aggregation'
|
|
|
|
VALID_AGGREGATIONS = %i[min max sum count collect].freeze
|
2019-07-10 21:46:42 +12:00
|
|
|
|
|
|
|
def initialize(storage)
|
|
|
|
@fields = []
|
|
|
|
@order_bys = []
|
|
|
|
@filters = []
|
2019-07-12 09:03:28 +12:00
|
|
|
@aggregations = {}
|
|
|
|
@group_bys = []
|
2019-07-10 21:46:42 +12:00
|
|
|
@storage = storage
|
|
|
|
end
|
|
|
|
|
|
|
|
def select(*fields)
|
|
|
|
ensure_valid_fields!(fields)
|
|
|
|
@fields = fields
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def aggregate(aggregations)
|
|
|
|
ensure_valid_fields!(aggregations.keys)
|
|
|
|
ensure_valid_aggregations!(aggregations.values)
|
|
|
|
@aggregations = aggregations
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def group_by(*fields)
|
|
|
|
ensure_valid_fields!(fields)
|
|
|
|
@group_bys = fields
|
|
|
|
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2019-07-10 21:46:42 +12:00
|
|
|
def order_by(*fields)
|
|
|
|
ensure_valid_fields!(fields)
|
|
|
|
@order_bys = fields
|
|
|
|
self
|
|
|
|
end
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def filter(*filters)
|
|
|
|
@filters = filters
|
2019-07-10 21:46:42 +12:00
|
|
|
self
|
|
|
|
end
|
|
|
|
|
|
|
|
def execute
|
2019-07-12 09:03:28 +12:00
|
|
|
collection = @storage
|
|
|
|
collection = apply_filters(collection)
|
|
|
|
collection = apply_aggregations(collection)
|
|
|
|
collection = apply_sorts(collection)
|
|
|
|
collection = take_fields(collection)
|
|
|
|
collection.to_a
|
2019-07-10 21:46:42 +12:00
|
|
|
end
|
|
|
|
|
|
|
|
private
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
attr_reader :fields, :order_bys, :filters, :storage, :group_bys, :aggregations
|
2019-07-10 21:46:42 +12:00
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def ensure_valid_aggregations!(aggregations)
|
|
|
|
aggregations.each do |op|
|
|
|
|
raise ArgumentError, format(INVALID_AGGREGATION_MESSAGE, op) unless VALID_AGGREGATIONS.include?(op.to_sym)
|
2019-07-10 21:46:42 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def apply_filters(collection)
|
|
|
|
return collection if filters.empty?
|
|
|
|
|
|
|
|
collection.select do |record|
|
|
|
|
filters.all? do |filter|
|
|
|
|
filter.match?(record)
|
|
|
|
end
|
|
|
|
end
|
2019-07-10 21:46:42 +12:00
|
|
|
end
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def apply_sorts(collection)
|
|
|
|
return collection if order_bys.empty?
|
|
|
|
|
|
|
|
collection.sort do |lhs, rhs|
|
|
|
|
order_bys
|
|
|
|
.lazy
|
|
|
|
.map { |attr| lhs.public_send(attr) <=> rhs.public_send(attr) }
|
|
|
|
.find(-> { 0 }, &:nonzero?)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def apply_aggregations(results)
|
|
|
|
return results if group_bys.empty?
|
|
|
|
|
|
|
|
results
|
|
|
|
.group_by { |record| group_bys.map { |attr| record.public_send(attr) } }
|
|
|
|
.values
|
|
|
|
.map { |records| AggregatedRecord.new(records, aggregations) }
|
2019-07-10 21:46:42 +12:00
|
|
|
end
|
|
|
|
|
2019-07-12 09:03:28 +12:00
|
|
|
def take_fields(collection)
|
|
|
|
collection.map do |record|
|
|
|
|
selected_fields.map { |field| record.public_send(field) }
|
2019-07-10 21:46:42 +12:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def selected_fields
|
|
|
|
return Record::ATTRIBUTES if fields.empty?
|
|
|
|
|
|
|
|
fields
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|