Associations#

Referenced Associations#

CouchbaseOrm supports the has_many, belongs_to and has_and_belongs_to_many associations familiar to ActiveRecord users.

Has Many#

Use the has_many association to declare that the parent has zero or more children stored in a separate collection:

class Band < CouchbaseOrm::Base

  has_many :members
end

The child model must use belongs_to to declare the association with the parent:

class Member < CouchbaseOrm::Base

  belongs_to :band
end

The child documents contain references to their respective parents:

band = Band.create!(members: [Member.new])
# => #<Band _id: 6001166d4896684910b8d1c5, >

band.members
# => [#<Member _id: 6001166d4896684910b8d1c6, band_id: '6001166d4896684910b8d1c5'>]

Use validations to require that at least one child is present:

class Band < CouchbaseOrm::Base

  has_many :members

  validates_presence_of :members
end

Belongs To#

Use the belongs_to macro to associate a child with a parent stored in a separate collection. The _id of the parent (if a parent is associated) is stored in the child.

class Studio < CouchbaseOrm::Base

  belongs_to :band
end

studio = Studio.create!
# => #<Studio _id: 600118184896684987aa884f, band_id: nil>

Although has_many associations require the corresponding belongs_to association to be defined on the child, belongs_to may also be used has_many macro. In this case the child is not accessible from the parent but the parent is accessible from the child:

class Band < CouchbaseOrm::Base
end

class Studio < CouchbaseOrm::Base
  belongs_to :band
end

Has And Belongs To Many#

Use the has_and_belongs_to_many macro to declare a many-to-many association:

class Band < CouchbaseOrm::Base
  has_and_belongs_to_many :tags
end

class Tag < CouchbaseOrm::Base
  has_and_belongs_to_many :bands
end

Both model instances store a list of ids of the associated models, if any:

band = Band.create!(tags: [Tag.create!])
# => #<Band _id: 60011d554896684b8b910a2a, tag_ids: ['60011d554896684b8b910a29']>

band.tags
# => [#<Tag _id: 60011d554896684b8b910a29, band_ids: ['60011d554896684b8b910a2a']>]

Custom Association Names#

You can name your associations whatever you like, but if the class cannot be inferred by CouchbaseOrm from the name, and neither can the opposite side you’ll want to provide the macro with some additional options to tell CouchbaseOrm how to hook them up.

class Car < CouchabseOrm::Base
  belongs_to :engine, class_name: "Motor"
end

class Motor < CouchabseOrm::Base
  has_many :machine, class_name: "Car"
end

Custom Foreign Keys#

The attributes used when looking up associations can be explicitly specified. The default is to use id on the “parent” association and #{association_name}_id on the “child” association, for example with a has_many/belongs_to:

class Company < CouchbaseOrm::Base
  has_many :emails
end

class Email < CouchbaseOrm::Base
  belongs_to :company
end

company = Company.find(id)
# looks up emails where emails.company_id == company.id
company.emails

Specify a different foreign_key to change the attribute name on the “child” association:

class Company < CouchbaseOrm::Base
  attribute :c, type: String
  has_many :emails, foreign_key: 'c_ref'
end

class Email < CouchbaseOrm::Base
  # This definition of c_ref is automatically generated by CouchbaseOrm:
  # attribute :c_ref, type: Object
  # But the type can also be specified:
  attribute :c_ref, type: String
  belongs_to :company, foreign_key: 'c_ref'
end

company = Company.find(id)
# looks up emails where emails.c_ref == company.c
company.emails

Polymorphism#

has_and_belongs_to_many associations support polymorphism, which is having a single association potentially contain objects of different classes. For example, we could model an organization in which departments and teams have managers as follows:

class Department < CouchbaseOrm::Base
  has_and_belongs_to_many :unit, class_name "Manager"
end

class Team < CouchbaseOrm::Base
  has_and_belongs_to_many :unit, class_name "Manager"
end

class Manager < CouchbaseOrm::Base
  belongs_to :unit, polymorphic: true
end

dept = Department.create!
team = Team.create!

alice = Manager.create!(unit: dept)
alice.unit == dept
# => true
dept.manager == alice
# => true

Dependent Behavior#

You can provide dependent options to referenced associations to instruct CouchbaseOrm how to handle situations where one side of the association is deleted, or is attempted to be deleted. The options are as follows:

  • :destroy: Destroy the child document(s) and run all of the model callbacks.

If no :dependent option is provided, deleting the parent document leaves the child document unmodified (in other words, the child document continues to reference the now deleted parent document via the foreign key attribute). The child may become orphaned if it is ordinarily only referenced via the parent.

class Band < CouchbaseOrm::Base
  has_many :albums, dependent: :destroy
  belongs_to :label
end

class Album < CouchbaseOrm::Base
  belongs_to :band
end

class Label < CouchbaseOrm::Base
  has_many :bands
end

Band.first.destroy # Will delete all associated albums.