Relationship Methods Reference
Complete reference for methods generated by Familia's relationships feature. Each relationship declaration creates predictable, consistently-named methods on both participating classes.
Quick Reference
Basic Participation
When Domain declares participates_in Customer, :domains:
# Target class (Customer) gets:
customer.domains # Collection accessor
customer.add_domains_instance(domain) # Add single
customer.add_domains([d1, d2]) # Add multiple
customer.remove_domains_instance(domain) # Remove
# Participant class (Domain) gets:
domain.add_to_customer_domains(customer) # Add self
domain.remove_from_customer_domains(customer) # Remove self
domain.in_customer_domains?(customer) # Check membership
domain.score_in_customer_domains(customer) # Get score (sorted_set)
# Reverse collection methods (Domain):
domain.customer_instances # Load Customer objects
domain.customer_ids # Just IDs
domain.customer? # Has any?
domain.customer_count # Count
Participation Methods
Target Class Methods
The target class (specified in participates_in) receives collection management methods.
class Domain < Familia::Horreum
participates_in Customer, :domains, score: :priority
end
| Method | Description | Example |
|---|---|---|
domains |
Collection accessor | customer.domains.size |
add_domains_instance(item, score) |
Add single item | customer.add_domains_instance(domain, 100) |
add_domains([items]) |
Bulk add | customer.add_domains([d1, d2, d3]) |
remove_domains_instance(item) |
Remove item | customer.remove_domains_instance(domain) |
Participant Class Methods
The participant class (calling participates_in) receives membership management methods.
| Method | Description | Example |
|---|---|---|
add_to_customer_domains(target, score) |
Add to target's collection | domain.add_to_customer_domains(customer) |
remove_from_customer_domains(target) |
Remove from target's collection | domain.remove_from_customer_domains(customer) |
in_customer_domains?(target) |
Check membership | domain.in_customer_domains?(customer) |
score_in_customer_domains(target) |
Get score (sorted_set only) | domain.score_in_customer_domains(customer) |
position_in_customer_domains(target) |
Get position (list only) | domain.position_in_customer_domains(customer) |
Reverse Collection Methods
Participants also get methods to query all relationships of a given type.
| Method | Description | Returns |
|---|---|---|
customer_instances |
Load all related objects | Array<Customer> |
customer_ids |
Get all related IDs | Array<String> |
customer? |
Check if has any relationships | Boolean |
customer_count |
Count relationships | Integer |
Class-Level Participation
Declaration
class User < Familia::Horreum
class_participates_in :all_users, score: :created_at
class_participates_in :active_users,
score: ->(u) { u.active? ? u.last_activity : 0 }
end
Generated Methods
| Level | Method | Description |
|---|---|---|
| Class | User.all_users |
Collection accessor |
User.add_to_all_users(user, score) |
Manual add | |
User.remove_from_all_users(user) |
Manual remove | |
| Instance | user.add_to_class_all_users(score) |
Add self |
user.remove_from_class_all_users |
Remove self | |
user.in_class_all_users? |
Check membership | |
user.score_in_class_all_users |
Get score |
Indexing Methods
Class-Level Unique Index
class User < Familia::Horreum
unique_index :email, :email_lookup
unique_index :api_key, :api_key_lookup, query: false
end
| Method | Generated When | Description |
|---|---|---|
User.find_by_email(email) |
query: true (default) |
O(1) lookup |
User.index_email_for(user) |
Always | Manual index |
User.unindex_email_for(user) |
Always | Remove from index |
User.reindex_email_for(user) |
Always | Update index |
Instance-Scoped Unique Index
class Employee < Familia::Horreum
unique_index :badge_number, :badge_index, within: Company
end
Methods on scope class (Company):
| Method | Description |
|--------|-------------|
| company.find_by_badge_number(badge) | Find employee |
| company.index_badge_number_for(employee) | Add to index |
| company.unindex_badge_number_for(employee) | Remove from index |
Methods on indexed class (Employee):
| Method | Description |
|--------|-------------|
| employee.add_to_company_badge_index(company) | Add to company's index |
| employee.remove_from_company_badge_index(company) | Remove from index |
| employee.in_company_badge_index?(company) | Check if indexed |
Class-Level Multi-Value Index
class Customer < Familia::Horreum
multi_index :role, :role_index # within: :class is the default
end
Class methods on indexed class (Customer):
| Method | Description |
|--------|-------------|
| Customer.role_index_for(value) | Factory returning UnsortedSet for value |
| Customer.find_all_by_role(role) | Find all with that role |
| Customer.sample_from_role(role, count) | Random sample |
| Customer.rebuild_role_index | Rebuild index from instances |
Instance methods on indexed class (Customer):
| Method | Description |
|--------|-------------|
| customer.add_to_class_role_index | Add to index |
| customer.remove_from_class_role_index | Remove from index |
| customer.update_in_class_role_index(old_value) | Move between indexes |
Instance-Scoped Multi-Value Index
class Employee < Familia::Horreum
multi_index :department, :dept_index, within: Company
end
Methods on scope class (Company):
| Method | Description |
|--------|-------------|
| company.dept_index_for(value) | Factory returning UnsortedSet for value |
| company.find_all_by_department(dept) | Find all in department |
| company.sample_from_department(dept, count) | Random sample |
| company.rebuild_dept_index | Rebuild index from participation |
Methods on indexed class (Employee):
| Method | Description |
|--------|-------------|
| employee.add_to_company_dept_index(company) | Add to index |
| employee.remove_from_company_dept_index(company) | Remove from index |
| employee.update_in_company_dept_index(company, old_dept) | Move between indexes |
Method Naming Patterns
Participation
- Target methods:
{collection},add_{collection}_instance,remove_{collection}_instance - Participant methods:
{action}_to_{target_config_name}_{collection} - Reverse methods:
{target_config_name}_instances,{target_config_name}_ids
Indexing
- Class unique:
find_by_{field},index_{field}_for,unindex_{field}_for - Class multi:
{Class}.find_all_by_{field},{Class}.sample_from_{field},{item}.add_to_class_{index} - Scoped unique:
{scope}.find_by_{field},{item}.add_to_{scope}_{index} - Scoped multi:
{scope}.find_all_by_{field},{scope}.sample_from_{field},{item}.add_to_{scope}_{index}
Common Usage Examples
Establishing Relationships
# Single addition with auto-calculated score
customer.add_domains_instance(domain)
# Single addition with explicit score
customer.add_domains_instance(domain, 100.0)
# Bulk addition
customer.add_domains([domain1, domain2, domain3])
# From participant side
domain.add_to_customer_domains(customer)
Querying Relationships
# Check membership
domain.in_customer_domains?(customer) # => true/false
# Get all relationships
domain.customer_instances # => [customer1, customer2]
domain.customer_ids # => ["cust_123", "cust_456"]
domain.customer_count # => 2
# Direct collection access
customer.domains.size # Count
customer.domains.to_a # All IDs
customer.domains.range(0, 9) # First 10
Working with Indexes
# Automatic class-level unique indexing
user = User.create(email: 'alice@example.com')
User.find_by_email('alice@example.com') # => user
# Class-level multi-value indexing
customer.add_to_class_role_index
Customer.find_all_by_role('admin') # => [customer, ...]
Customer.sample_from_role('admin', 2) # => random sample
# Manual scoped unique indexing
employee.add_to_company_badge_index(company)
company.find_by_badge_number('12345') # => employee
# Scoped multi-value indexing
employee.add_to_company_dept_index(company)
engineers = company.find_all_by_department('engineering')
See Also
- Relationships Overview - Core concepts and patterns
- Participation Guide - Detailed participation docs
- Indexing Guide - Detailed indexing docs