How rails ActiveRecord or Nosql ORM like mongoid handle has_many, has_one side of the relationship?

Viewed 20

Lets Say I have User and a Team model.

    class User
      #some fields
      has_one :team
    end

    class Team
       #some fields
       belongs_to :user
    end

Now when i do User.last.update(team: Team.last), it stores user_id: foreign key in team document. Prety common stuff.

I want to know what ORMs do/store on the has_one/has_many models(User) just like they store foreign_key on belongs_to(Team) model so that User.last.team query actually gives me a team. Does it create instance methods on User model like

  def team
    Team.where(user_id: self.id).first
  end

Am I wrong?

1 Answers

Okay I can definitely explain what's going on here. Given the following models:

class User
  include Mongoid::Document
  #some fields
  has_one :team
end

class Team
  include Mongoid::Document
  #some fields
  belongs_to :user
end

We can create the documents as follows:

Team.create!(user: User.create!)

This is equivalent to what you did in the OP. Now, this yields the following documents:

> db.users.find()
{ "_id" : ObjectId("62d9ad683282a400a799c7e7") }
> db.teams.find()
{ "_id" : ObjectId("62d9ad683282a400a799c7e8"), "user_id" : ObjectId("62d9ad683282a400a799c7e7") }

As you can see, the foreign key is stored on the belongs_to side of the relationship. Now, when trying to retrieve the team from the user:

User.last 
# => #<User _id: 62d9adc63282a400c637b234, >
User.last.team

The take method (as of Mongoid 8, before that, the first method was used) is used on the following criteria:

#<Mongoid::Criteria
  selector: {"user_id"=>BSON::ObjectId('62d9adc63282a400c637b234')}
  options:  {}
  class:    Team
  embedded: false>

To put this into layman's terms, we are searching the Team collection for a document with the same value for user_id as our user's _id.

So yes, your idea is almost correct, it's more like:

Team.where(user_id: user._id).take

The difference between take and first is that take doesn't put a sort on _id, while first does. Using take is a little more efficient.

Related