class ObjectFramework::Persistence::Query
Immutable query shared by storage adapters. Filters and ordering produce new queries; rows use id as a deterministic final ordering key.
Query.new.where(status: "ready").order(:id).limit(20).offset(40)
Constants
- OPERATORS
Attributes
Public Class Methods
Source
# File lib/object_framework/persistence/query.rb, line 15 def self.identifier(name) raise ConfigurationError, "Invalid storage identifier: #{name.inspect}" unless /\A[a-zA-Z_][a-zA-Z0-9_]*\z/.match?(name.to_s) name.to_sym end
Source
# File lib/object_framework/persistence/query.rb, line 20 def initialize(table: nil, criteria: [], ordering: [], row_limit: nil, row_offset: 0) @table = self.class.identifier(table) if table @criteria, @ordering = criteria.freeze, ordering.freeze @row_limit, @row_offset = row_limit, row_offset freeze end
Public Instance Methods
Source
# File lib/object_framework/persistence/query.rb, line 61 def apply(rows) selected = rows.select { |row| criteria.all? { |criterion| matches?(row, criterion) } } ordering = @ordering.any? { |field, _| field == :id } ? @ordering : @ordering + [[:id, :asc]] selected.sort! do |left, right| ordering.each do |field, direction| a, b = left[field], right[field] comparison = if a.nil? then b.nil? ? 0 : -1 elsif b.nil? then 1 else a <=> b end raise InvalidInput, "Incomparable query values" unless comparison break(direction == :desc ? -comparison : comparison) unless comparison.zero? end.then { |result| result.is_a?(Integer) ? result : 0 } end selected = selected.drop(row_offset) row_limit ? selected.take(row_limit) : selected end
Source
# File lib/object_framework/persistence/query.rb, line 38 def compare(field, operator, value) raise ConfigurationError, "Unsupported query operator" unless OPERATORS.include?(operator) raise ConfigurationError, "IN requires an array" if operator == :in && !value.is_a?(Array) criterion = Criterion.new(field: self.class.identifier(field), operator:, value: Values.detach(value)) copy(criteria: criteria + [criterion]) end
Adds :eq, :ne, :gt, :gte, :lt, :lte, or :in. :in requires an Array. Values are detached on construction.
Source
# File lib/object_framework/persistence/query.rb, line 27 def for_table(table) copy(table:) end
Source
# File lib/object_framework/persistence/query.rb, line 52 def limit(count) copy(row_limit: nonnegative(count)) end
Returns a query limited to a nonnegative integer number of rows.
Source
# File lib/object_framework/persistence/query.rb, line 57 def offset(count) copy(row_offset: nonnegative(count)) end
Returns a query skipping a nonnegative integer number of rows.
Source
# File lib/object_framework/persistence/query.rb, line 46 def order(field, direction = :asc) raise ConfigurationError, "Invalid ordering direction" unless %i[asc desc].include?(direction) copy(ordering: ordering + [[self.class.identifier(field), direction].freeze]) end
Appends an ascending or descending ordering using :asc or :desc.
Source
# File lib/object_framework/persistence/query.rb, line 32 def where(**attributes) attributes.reduce(self) { |query, (field, value)| query.compare(field, :eq, value) } end
Adds equality criteria for every supplied field. Returns a new query.