Module: Familia::Features::Relationships::Indexing::MultiIndexGenerators
- Defined in:
- lib/familia/features/relationships/indexing/multi_index_generators.rb
Overview
Generators for multi-value index (1:many) methods
Multi-value indexes use UnsortedSet DataType for grouping objects by field value. Each field value gets its own set of object identifiers.
Example: multi_index :department, :dept_index, within: Company
Generates on Company (destination):
- company.sample_from_department(dept, count=1)
- company.find_all_by_department(dept)
- company.dept_index_for(dept_value)
- company.rebuild_dept_index
Generates on Employee (self):
- employee.add_to_company_dept_index(company)
- employee.remove_from_company_dept_index(company)
- employee.update_in_company_dept_index(company, old_dept)
Class Method Summary collapse
-
.generate_factory_method(target_class, index_name) ⇒ Object
Generates the factory method ON THE PARENT CLASS (Company when within: Company): - company.index_name_for(field_value) - DataType factory (always needed).
-
.generate_mutation_methods_self(indexed_class, field, target_class, index_name) ⇒ Object
Generates mutation methods ON THE INDEXED CLASS (Employee): - employee.add_to_company_dept_index(company) - employee.remove_from_company_dept_index(company) - employee.update_in_company_dept_index(company, old_dept).
-
.generate_query_methods_destination(indexed_class, field, target_class, index_name) ⇒ Object
Generates query methods ON THE PARENT CLASS (Company when within: Company): - company.sample_from_department(dept, count=1) - random sampling - company.find_all_by_department(dept) - all objects - company.rebuild_dept_index - rebuild index.
-
.setup(indexed_class:, field:, index_name:, within:, query:) ⇒ Object
Main setup method that orchestrates multi-value index creation.
Class Method Details
.generate_factory_method(target_class, index_name) ⇒ Object
Generates the factory method ON THE PARENT CLASS (Company when within: Company):
- company.index_name_for(field_value) - DataType factory (always needed)
This method is required by mutation methods even when query: false
72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/familia/features/relationships/indexing/multi_index_generators.rb', line 72 def generate_factory_method(target_class, index_name) actual_target_class = Familia.resolve_class(target_class) actual_target_class.class_eval do # Helper method to get index set for a specific field value # This acts as a factory for field-value-specific DataTypes define_method(:"#{index_name}_for") do |field_value| # Return properly managed DataType instance with parameterized key index_key = "#{index_name}:#{field_value}" Familia::UnsortedSet.new(index_key, parent: self) end end end |
.generate_mutation_methods_self(indexed_class, field, target_class, index_name) ⇒ Object
Generates mutation methods ON THE INDEXED CLASS (Employee):
- employee.add_to_company_dept_index(company)
- employee.remove_from_company_dept_index(company)
- employee.update_in_company_dept_index(company, old_dept)
138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 |
# File 'lib/familia/features/relationships/indexing/multi_index_generators.rb', line 138 def generate_mutation_methods_self(indexed_class, field, target_class, index_name) target_class_config = target_class.config_name indexed_class.class_eval do method_name = :"add_to_#{target_class_config}_#{index_name}" Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}") define_method(method_name) do |target_instance| return unless target_instance field_value = send(field) return unless field_value # Use helper method on target instance instead of manual instantiation index_set = target_instance.send("#{index_name}_for", field_value) # Use UnsortedSet DataType method (no scoring) index_set.add(identifier) end method_name = :"remove_from_#{target_class_config}_#{index_name}" Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}") define_method(method_name) do |target_instance| return unless target_instance field_value = send(field) return unless field_value # Use helper method on target instance instead of manual instantiation index_set = target_instance.send("#{index_name}_for", field_value) # Remove using UnsortedSet DataType method index_set.remove(identifier) end method_name = :"update_in_#{target_class_config}_#{index_name}" Familia.ld("[MultiIndexGenerators] #{name} method #{method_name}") define_method(method_name) do |target_instance, old_field_value = nil| return unless target_instance new_field_value = send(field) # Use Familia's transaction method for atomicity with DataType abstraction target_instance.transaction do |_tx| # Remove from old index if provided - use helper method if old_field_value old_index_set = target_instance.send("#{index_name}_for", old_field_value) old_index_set.remove(identifier) end # Add to new index if present - use helper method if new_field_value new_index_set = target_instance.send("#{index_name}_for", new_field_value) new_index_set.add(identifier) end end end end end |
.generate_query_methods_destination(indexed_class, field, target_class, index_name) ⇒ Object
Generates query methods ON THE PARENT CLASS (Company when within: Company):
- company.sample_from_department(dept, count=1) - random sampling
- company.find_all_by_department(dept) - all objects
- company.rebuild_dept_index - rebuild index
95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/familia/features/relationships/indexing/multi_index_generators.rb', line 95 def generate_query_methods_destination(indexed_class, field, target_class, index_name) # Resolve target class using Familia pattern actual_target_class = Familia.resolve_class(target_class) # Generate instance sampling method (e.g., company.sample_from_department) actual_target_class.class_eval do define_method(:"sample_from_#{field}") do |field_value, count = 1| index_set = send("#{index_name}_for", field_value) # i.e. UnsortedSet # Get random members efficiently (O(1) via SRANDMEMBER with count) # Returns array even for count=1 for consistent API index_set.sample(count).map do |id| indexed_class.find_by_identifier(id) end end # Generate bulk query method (e.g., company.find_all_by_department) define_method(:"find_all_by_#{field}") do |field_value| index_set = send("#{index_name}_for", field_value) # i.e. UnsortedSet # Get all members from set index_set.members.map { |id| indexed_class.find_by_identifier(id) } end # Generate method to rebuild the index for this parent instance define_method(:"rebuild_#{index_name}") do # This would need to be implemented based on how you track which # objects belong to this parent instance # For now, just a placeholder end end end |
.setup(indexed_class:, field:, index_name:, within:, query:) ⇒ Object
Main setup method that orchestrates multi-value index creation
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
# File 'lib/familia/features/relationships/indexing/multi_index_generators.rb', line 37 def setup(indexed_class:, field:, index_name:, within:, query:) # Multi-index always requires a parent context target_class = within resolved_class = Familia.resolve_class(target_class) # Store metadata for this indexing relationship indexed_class.indexing_relationships << IndexingRelationship.new( field: field, target_class: target_class, index_name: index_name, query: query, cardinality: :multi, ) # Always generate the factory method - required by mutation methods if target_class.is_a?(Class) generate_factory_method(resolved_class, index_name) end # Generate query methods on the parent class (optional) if query && target_class.is_a?(Class) generate_query_methods_destination(indexed_class, field, resolved_class, index_name) end # Generate mutation methods on the indexed class generate_mutation_methods_self(indexed_class, field, resolved_class, index_name) end |