Ruby_Pry
Ruby_Pry

Reputation: 237

Need advice with MongoDB modeling using Rails/Mongoid

I am in the midst of working on a resume templating app. The project is using Rails 4.0, Devise, MongoDB and bootstrap.

This is how my models are structured right now. I am using Polymorphic associations in order to permit more flexibility as the application grows and Users can have more than one resume. I omitted some of the irrelevant model code and some of the smaller models such as educations, skills that don't pertain to my question:

class User
  include Mongoid::Document
  include Mongoid::Timestamps
  has_many :resumes
  has_many :educations, as: :educatable
  has_many :skills, as: :skillable
  has_many :positions, as: :positionable
  has_one  :address, as: :addressable

  field :name,               :type => String
  field :email,              :type => String, :default => ""
  field :encrypted_password, :type => String, :default => ""

  validates_presence_of :name
  validates_uniqueness_of :name, :email

class Resume
  include Mongoid::Document
  include Mongoid::Timestamps

  belongs_to :user
  has_many :educations, as: :educatable
  has_many :skills, as: :skillable
  has_many :positions, as: :positionable
  has_one :address, as: :addressable

  field :objective, :type => String

  validates_presence_of :objective

end

I am implementing the following features to start out:

A friend suggested that MongoDB was meant to enable each model store the data it needs rather than access it through a relationship. In this case, he suggested that I should enable the resume model to store the positions, educations, skills and address in addition to the user model. Having only worked with an RDBMS, I am a bit stuck with how to proceed regarding the 'User can create a resume' feature and am wondering if anyone can suggest how I should model this?

Here is a snapshot of my routes as well:

Tecume::Application.routes.draw do

  devise_for :users, :controllers => { :registrations => "registrations"} 
    resources :users do 
      resources :resumes, only: [:show, :new, :create]
    end

    root 'pages#home'



  get '/ui/:action', controller: 'ui'
end

Upvotes: 0

Views: 286

Answers (1)

mu is too short
mu is too short

Reputation: 434965

Comments are too small so I'll do some hand waving in an answer instead.

Your friend is talking about embedded documents:

A Mongoid object is basically a Hash wrapped up in a class. Of course, an ActiveRecord object is pretty much the same thing. But, MongoDB makes it easy to have Hash fields and Array fields; if you wrap one of those Hash fields in a class then you pretty much have a embeds_one relationship; if you populate an Array field with Hashes and wrap them all up in classes then you pretty much have an embeds_many relationship.

MongoDB doesn't have JOINs so if you need to query two things at once they either you denormalize and copy the data or you embed one thing inside the other. For example, if you want to find all the resumes from people in Whistler and you're using has_one, then you have to go find all the addresses whose city is Whistler:

addr_ids = Address.where(:city => 'Whistler').pluck(:id)

and then do a separate query to find the resumes:

Resume.where(:address_id.in => addr_ids)

If you use an embedded document then you just look inside the embedded documents:

Resume.where('address.city' => 'Whistler')

Similarly for eager loading: embedded documents come out of the database as part of their parent document, non-embedded documents require separate queries.

Rough rules of thumb: If something needs to be an entity unto itself then traditional has_many and has_one relationships make sense, if something only exists in relation to something else then embeds_one or embeds_many make sense.

Upvotes: 1

Related Questions