Class: TypedEAV::ScalarQuery

Inherits:
Object
  • Object
show all
Defined in:
lib/typed_eav/scalar_query.rb

Overview

SQL-backed operations over one scalar typed field.

Entity-level wrappers resolve ambient/explicit scope and preserve Rails' current relation delegation. This object owns the field-definition lookup, scalar support gate, and correlated value expression so no host or Value records are hydrated merely to order a relation.

Constant Summary collapse

SCALAR_COLUMNS =
i[
  boolean_value
  date_value
  datetime_value
  decimal_value
  integer_value
  string_value
  text_value
].freeze
DIRECTIONS =
i[asc desc].freeze
NULL_PLACEMENTS =
i[first last].freeze
AGGREGATE_OPERATIONS =
i[min max sum].freeze

Instance Method Summary collapse

Constructor Details

#initialize(model:, name:, scope:, parent_scope:, direction: :asc, nulls: :last) ⇒ ScalarQuery

rubocop:disable Metrics/ParameterLists -- the query object receives the resolved public API inputs explicitly.



25
26
27
28
29
30
31
32
# File 'lib/typed_eav/scalar_query.rb', line 25

def initialize(model:, name:, scope:, parent_scope:, direction: :asc, nulls: :last)
  @model = model
  @name = normalize_name(name)
  @direction = normalize_option(direction, DIRECTIONS, "direction")
  @nulls = normalize_option(nulls, NULL_PLACEMENTS, "nulls")
  @scope = scope
  @parent_scope = parent_scope
end

Instance Method Details

#aggregate(operation:) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
102
103
# File 'lib/typed_eav/scalar_query.rb', line 90

def aggregate(operation:)
  raise_all_scopes!

  operation = normalize_option(operation, AGGREGATE_OPERATIONS, "operation")
  field = field_for_name
  column = numeric_column_for(field)
  relation = value_relation(field)

  case operation
  when :min then relation.minimum(column)
  when :max then relation.maximum(column)
  when :sum then relation.sum(column)
  end
end

#count_distinct_valuesObject



62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/typed_eav/scalar_query.rb', line 62

def count_distinct_values
  raise_all_scopes!

  field = field_for_name
  column = scalar_column_for(field)
  distinct_relation = value_relation(field).select(column).distinct
  aliased_relation = TypedEAV::Value.from(
    "(#{distinct_relation.to_sql}) #{quoted_table_name("typed_eav_distinct_values")}",
  )

  aliased_relation.count
end

#distinct_values(limit:) ⇒ Object



51
52
53
54
55
56
57
58
59
60
# File 'lib/typed_eav/scalar_query.rb', line 51

def distinct_values(limit:)
  raise_all_scopes!

  field = field_for_name
  column = scalar_column_for(field)
  relation = value_relation(field).select(column).distinct
  relation = relation.order(Arel.sql("#{quoted_value_column(column)} ASC NULLS LAST"))

  relation.limit(validate_limit(limit)).pluck(column)
end

#order_relationObject

rubocop:enable Metrics/ParameterLists



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/typed_eav/scalar_query.rb', line 35

def order_relation
  raise_all_scopes!

  field = field_for_name
  column = scalar_column_for(field)
  relation = @model.all
  host_table = relation.arel_table
  value_table = TypedEAV::Value.arel_table
  value_query = value_subquery(value_table, host_table, column, field)

  relation.reorder(
    Arel.sql("(#{value_query.to_sql}) #{@direction.to_s.upcase} NULLS #{@nulls.to_s.upcase}"),
    host_table[@model.primary_key].asc,
  )
end

#value_counts(limit:) ⇒ Object



75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/typed_eav/scalar_query.rb', line 75

def value_counts(limit:)
  raise_all_scopes!

  field = field_for_name
  column = scalar_column_for(field)
  count_sql = Arel.sql("COUNT(DISTINCT #{quoted_value_column(:entity_id)})")

  value_relation(field)
    .group(column)
    .order(Arel.sql("#{quoted_value_column(column)} ASC NULLS LAST"))
    .limit(validate_limit(limit))
    .pluck(column, count_sql)
    .to_h
end