This repository has been archived on 2024-06-24. You can view files and clone it, but cannot push or open issues or pull requests.
ace-of-base/lib/ace_of_base/query.rb

120 lines
2.7 KiB
Ruby
Raw Normal View History

2019-07-10 21:46:42 +12:00
# frozen_string_literal: true
module AceOfBase
# A query builder for querying our data.
class Query
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 = []
@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
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
def filter(*filters)
@filters = filters
2019-07-10 21:46:42 +12:00
self
end
def execute
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
attr_reader :fields, :order_bys, :filters, :storage, :group_bys, :aggregations
2019-07-10 21:46:42 +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
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
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
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