require 'familia'
Familia.configure do |config|
config.uri = ENV.fetch('REDIS_URL', 'redis://localhost:2525/')
end
puts '=== Familia Relationships Basic Example ==='
puts
class Customer < Familia::Horreum
logical_database 15 feature :relationships
identifier_field :custid
field :custid
field :name
field :email
field :plan
set :domains listkey :projects sorted_set :activity
unique_index :email, :email_lookup
multi_index :plan, :plan_lookup
end
class Domain < Familia::Horreum
logical_database 15 feature :relationships
identifier_field :domain_id
field :domain_id
field :name
field :dns_zone
field :status
participates_in Customer, :domains
class_participates_in :active_domains,
score: -> { status == 'active' ? Familia.now.to_i : 0 }
end
class Project < Familia::Horreum
logical_database 15 feature :relationships
identifier_field :project_id
field :project_id
field :name
field :priority
participates_in Customer, :projects, type: :list
end
puts '=== 1. Basic Object Creation ==='
customer = Customer.new(
custid: "cust_#{SecureRandom.hex(4)}",
name: 'Acme Corporation',
email: 'admin@acme.com',
plan: 'enterprise'
)
domain1 = Domain.new(
domain_id: "dom_#{SecureRandom.hex(4)}",
name: 'acme.com',
dns_zone: 'acme.com.',
status: 'active'
)
domain2 = Domain.new(
domain_id: "dom_#{SecureRandom.hex(4)}",
name: 'staging.acme.com',
dns_zone: 'staging.acme.com.',
status: 'active'
)
project = Project.new(
project_id: "proj_#{SecureRandom.hex(4)}",
name: 'Website Redesign',
priority: 'high'
)
puts "✓ Created customer: #{customer.name} (#{customer.custid})"
puts "✓ Created domains: #{domain1.name}, #{domain2.name}"
puts "✓ Created project: #{project.name}"
puts
puts '=== 2. Establishing Relationships ==='
customer.save puts '✓ Customer automatically added to indexes and tracking on save'
customer.domains << domain1 customer.domains << domain2 customer.projects << project
puts '✓ Established domain ownership relationships using << operator'
puts '✓ Established project ownership relationships using << operator'
domain1.save
domain2.save
[domain1, domain2].each do |d|
activation_score = d.status == 'active' ? Familia.now.to_i : 0
Domain.add_to_active_domains(d, activation_score)
end
puts '✓ Domains added to active_domains class collection'
puts
puts '=== 3. Querying Relationships ==='
record = Customer.find_by_email('admin@acme.com')
puts "Email lookup (unique_index): #{record&.custid || 'not found'}"
enterprise_customers = Customer.find_all_by_plan('enterprise')
puts "Plan lookup (multi_index): #{enterprise_customers.size} enterprise customer(s)"
puts
puts "\nDomain membership checks:"
puts " #{domain1.name} belongs to customer? #{domain1.in_customer_domains?(customer)}"
puts " #{domain2.name} belongs to customer? #{domain2.in_customer_domains?(customer)}"
puts "\nCustomer collections:"
puts " Customer has #{customer.domains.size} domains"
puts " Customer has #{customer.projects.size} projects"
puts " Domain IDs: #{customer.domains.members}"
puts " Project IDs: #{customer.projects.members}"
all_customers_count = Customer.instances.size
puts "\nClass-level tracking:"
puts " Total customers in system: #{all_customers_count}"
active_domains_count = Domain.active_domains.size
puts " Active domains in system: #{active_domains_count}"
puts
puts '=== 4. Range Queries ==='
yesterday = (Familia.now - (24 * 3600)).to_i recent_customers = Customer.instances.rangebyscore(yesterday, '+inf')
puts "Recent customers (last 24h): #{recent_customers.size}"
puts 'Active domains with timestamps:'
[domain1, domain2].each do |d|
score = Domain.active_domains.score(d)
next unless score
puts " #{d.name} (#{d.domain_id}): active since #{Time.at(score.to_i)} (score=#{score})"
end
puts
puts '=== 6. Relationship Cleanup ==='
puts 'Cleaning up relationships...'
domain1.remove_from_customer_domains(customer)
puts "✓ Removed #{domain1.name} from customer domains"
Domain.active_domains.remove(domain2)
puts "✓ Removed #{domain2.name} from active domains"
puts "\nAfter cleanup:"
puts " Customer domains: #{customer.domains.size}"
puts " Active domains: #{Domain.active_domains.size}"
puts
puts '=== Cleaning up test data ==='
[domain1, domain2].each { |d| Domain.active_domains.remove(d) }
records = [customer, domain1, domain2, project]
records.each(&:destroy!)
puts "✓ Destroyed #{records.size} example records (no prefix globs)"
puts
puts '=== Example Complete! ==='
puts
puts 'Key takeaways:'
puts '• unique_index: 1:1 class-level index -> find_by_<field> (single record)'
puts '• multi_index: 1:many class-level index -> find_all_by_<field> (array)'
puts '• instances: Built-in per-class timeline, auto-updated on every save'
puts '• unique_index/multi_index: Auto-populated on save'
puts '• class_participates_in: Class-level collections; add explicitly (not auto on save)'
puts '• participates_in: Use << operator for clean Ruby-like collection syntax'
puts '• Pass within: to unique_index/multi_index for relationship-scoped indexes'
puts '• << operator: Works naturally with all collection types (sets, lists, sorted sets)'
puts
puts 'See docs/wiki/Relationships-Guide.md for comprehensive documentation'