Module: TypedEAV::EntityQuery
- Defined in:
- lib/typed_eav/entity_query.rb
Overview
Class-level query orchestration extended onto host AR models by the ‘has_typed_eav` macro. Owns the `UNSET_SCOPE` / `ALL_SCOPES` sentinels and the `resolve_scope` chain; delegates the heavy lifting to `FilterQuery` (multi-filter SQL composition) and `BulkRead` (bulk per-record reads). `bulk_set_typed_eav_values` stays as a 3-line wrapper around the existing `BulkWrite` executor.
Constant Summary collapse
- UNSET_SCOPE =
Sentinel for the ‘scope:` kwarg default. Distinguishes “kwarg not passed -> resolve from ambient” (UNSET_SCOPE) from “explicitly nil -> filter to global-only fields” (preserves prior behavior).
Object.new.freeze
- ALL_SCOPES =
Sentinel returned by ‘resolve_scope` inside an `unscoped { }` block. Signals the caller to skip the scope filter entirely (return fields across all partitions, not just global).
Object.new.freeze
Instance Method Summary collapse
-
#bulk_set_typed_eav_values(records, values_by_field_name, version_grouping: :default) ⇒ Object
Bulk write API.
-
#typed_eav_definitions(scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Returns field definitions for this entity type.
-
#typed_eav_hash_for(records) ⇒ Object
Bulk read API.
-
#where_typed_eav(*filters, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Query by custom field values.
-
#with_field(name, operator_or_value = nil, value = nil, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Shorthand for single-field queries.
Instance Method Details
#bulk_set_typed_eav_values(records, values_by_field_name, version_grouping: :default) ⇒ Object
Bulk write API. Sets the same ‘values_by_field_name` Hash on every record in `records` inside ONE outer ActiveRecord transaction with a SAVEPOINT-PER-RECORD failure-isolation envelope. See `TypedEAV::BulkWrite` for the transaction shape, error-aggregation contract, and the `version_grouping:` semantics.
99 100 101 102 103 104 105 106 |
# File 'lib/typed_eav/entity_query.rb', line 99 def bulk_set_typed_eav_values(records, values_by_field_name, version_grouping: :default) TypedEAV::BulkWrite.execute( host_class: self, records: records, values_by_field_name: values_by_field_name, version_grouping: version_grouping, ) end |
#typed_eav_definitions(scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Returns field definitions for this entity type.
‘scope:` and `parent_scope:` behavior:
- omitted -> resolve from ambient (`with_scope` -> resolver -> raise/nil)
- passed a value -> use verbatim (explicit override; admin/test path)
- passed nil -> filter to global-only on that axis (prior behavior preserved)
75 76 77 78 79 80 81 82 83 |
# File 'lib/typed_eav/entity_query.rb', line 75 def typed_eav_definitions(scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) resolved = resolve_scope(scope, parent_scope) if resolved.equal?(ALL_SCOPES) TypedEAV::Partition.visible_fields(entity_type: name, mode: :all_partitions) else s, ps = resolved TypedEAV::Partition.visible_fields(entity_type: name, scope: s, parent_scope: ps) end end |
#typed_eav_hash_for(records) ⇒ Object
Bulk read API. Returns ‘{ record_id => { field_name => value } }` for an Enumerable of host records — the class-method bulk variant of `HasTypedEAV::InstanceMethods#typed_eav_hash`. N+1-free regardless of record count or field count. See `TypedEAV::BulkRead` for the pipeline and query bound.
90 91 92 |
# File 'lib/typed_eav/entity_query.rb', line 90 def typed_eav_hash_for(records) TypedEAV::BulkRead.new(host_class: self, records: records).to_hash end |
#where_typed_eav(*filters, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Query by custom field values. Accepts an array of filter hashes or a hash of hashes (from form params).
Each filter needs:
:name or :n - the field name
:op or :operator - the operator (default: :eq)
:value or :v - the comparison value
Contact.where_typed_eav(
{ name: "age", op: :gt, value: 21 },
{ name: "city", value: "Portland" } # op defaults to :eq
)
‘scope:` and `parent_scope:` behavior:
- omitted -> resolve from ambient (`with_scope` -> resolver -> raise/nil)
- passed a value -> use verbatim (explicit override; admin/test path)
- passed nil -> filter to global-only on that axis (prior behavior)
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/typed_eav/entity_query.rb', line 38 def where_typed_eav(*filters, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) resolved = resolve_scope(scope, parent_scope) effective_scope, effective_parent = scope_pair(resolved) TypedEAV::FilterQuery.new( model: self, filters: filters, scope: effective_scope, parent_scope: effective_parent, ).to_relation end |
#with_field(name, operator_or_value = nil, value = nil, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) ⇒ Object
Shorthand for single-field queries.
Contact.with_field("age", :gt, 21)
Contact.with_field("active", true) # op defaults to :eq
Contact.with_field("name", :contains, "smith")
Accepts both ‘scope:` and `parent_scope:` kwargs with the same ambient/explicit/nil semantics as `where_typed_eav`. Single-scope callers (no `parent_scope:`) are unaffected.
59 60 61 62 63 64 65 66 67 |
# File 'lib/typed_eav/entity_query.rb', line 59 def with_field(name, operator_or_value = nil, value = nil, scope: UNSET_SCOPE, parent_scope: UNSET_SCOPE) filter = if value.nil? && !operator_or_value.is_a?(Symbol) # Two-arg form: with_field("name", "value") implies :eq { name: name, op: :eq, value: operator_or_value } else { name: name, op: operator_or_value, value: value } end where_typed_eav(filter, scope: scope, parent_scope: parent_scope) end |