Module: ForestAdminDatasourceMambuPayments::Plugins::Relations::PivotResolution

Defined in:
lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb

Overview

Shared machinery for the “two-step” relation filters. Forest’s native OneToMany navigation emits a leaf on a virtual foreign key; these filters pre-resolve that key against an intermediate collection and rewrite the predicate as ‘target_field IN [resolved ids]`.

Centralising it here keeps the four concrete filters in sync: the match-nothing sentinel, the EQUAL/IN normalisation, and — crucially —the paginated intermediate read all live in one place. A single un-paginated ‘list` would silently cap resolution at one API page and drop matching records for large relations.

Constant Summary collapse

Operators =
ForestAdminDatasourceToolkit::Components::Query::ConditionTree::Operators
ConditionTreeLeaf =
ForestAdminDatasourceToolkit::Components::Query::ConditionTree::Nodes::ConditionTreeLeaf
ConditionTreeBranch =
ForestAdminDatasourceToolkit::Components::Query::ConditionTree::Nodes::ConditionTreeBranch
Filter =
ForestAdminDatasourceToolkit::Components::Query::Filter
Projection =
ForestAdminDatasourceToolkit::Components::Query::Projection
Page =
ForestAdminDatasourceToolkit::Components::Query::Page
NO_MATCH_SENTINEL =

All-zero UUID: guaranteed not to exist in Numeral, so the native list returns []. Expresses “match nothing” without tripping the empty-IN guard in ConditionTreeTranslator.

'00000000-0000-0000-0000-000000000000'.freeze
SUPPORTED_OPS =

Only EQUAL/IN are rewritten — the operators Forest’s OneToMany navigation actually emits.

[Operators::EQUAL, Operators::IN].freeze
MAX_RESOLVED =

Upper bound on resolved ids. The host collection walks Numeral’s cursor pages under the hood; we ask for one large window so it fetches them all in O(n / page) rather than re-walking per offset. A relation resolving to more than this is logged rather than silently truncated.

10_000

Class Method Summary collapse

Class Method Details

.and_branch(*leaves) ⇒ Object



48
49
50
# File 'lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb', line 48

def and_branch(*leaves)
  ConditionTreeBranch.new('And', leaves)
end

.collect(context, collection_name, condition_tree, field) ⇒ Object

Lists every row of ‘collection_name` matching `condition_tree` and returns the unique non-empty values of `field` (handles both scalar columns and array columns such as InternalAccount.connected_account_ids). One large-window request lets the collection’s cursor pagination fetch all matching rows in a single forward walk.



57
58
59
60
61
62
63
64
65
66
67
68
69
# File 'lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb', line 57

def collect(context, collection_name, condition_tree, field)
  rows = context.datasource.get_collection(collection_name).list(
    Filter.new(condition_tree: condition_tree, page: Page.new(offset: 0, limit: MAX_RESOLVED)),
    Projection.new([field])
  )
  if rows.size >= MAX_RESOLVED
    ForestAdminDatasourceMambuPayments.logger.warn(
      "[forest_admin_datasource_mambu_payments] #{collection_name} relation resolution hit the " \
      "#{MAX_RESOLVED}-row cap on '#{field}'; results may be truncated."
    )
  end
  rows.flat_map { |row| Array(row[field]) }.compact.reject { |v| v.to_s.empty? }.uniq
end

.no_match(target_field) ⇒ Object



44
45
46
# File 'lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb', line 44

def no_match(target_field)
  ConditionTreeLeaf.new(target_field, Operators::EQUAL, NO_MATCH_SENTINEL)
end

.normalize(value, operator) ⇒ Object



39
40
41
42
# File 'lib/forest_admin_datasource_mambu_payments/plugins/relations/pivot_resolution.rb', line 39

def normalize(value, operator)
  values = operator == Operators::IN ? Array(value) : [value]
  values.compact.reject { |v| v.to_s.empty? }.uniq
end