Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use Mongoid and ActiveRecord in parallel in Rails 3?

I'm using rails 3, and began my application with ActiveRecord. Now, I have many models, and the relations are starting to get complicated, and some could be more simply expressed with a Document-Oriented structure, so I'd like to try migrating to MongoDB and use Mongoid.

I've always heard that you didn't have to eitheer use all MongoDB or nothing, but that you could use the two in parallel while migrating. I don't see how to go about this from the docs though.

For example, I have:

class User < ActiveRecord::Base
   has_many :items
   has_many :products, :through => :items
end

class Product < ActiveRecord::Base
   has_many :items
end

class Item < ActiveRecord::Base
   belongs_to :user
   belongs_to :product

   # alot of data that fits a hierarchical document-oriented structure
end

I'd like to ideally begin by replacing my Item activerecord model with a Mongoid document, so my items are stored in MongoDB, and my Users and Products can stay in my SQL DB

Thing is, I don't see how to do this. Am I going about this the right way?

Perhaps another alternative is to keep a base AR Item

class Item < ActiveRecord::Base
   has_one :mongodb_item  ?? # I know this is wrong
end

class MongodbItem
   include Mongoid::Document
   belongs_to AR_Item ???    # I know this is also wrong
end

Thanks!

like image 443
noli Avatar asked Jun 30 '11 20:06

noli


3 Answers

What I did was just mock the relationship with methods in each the AR model and the Mongoid model like so.

# visit_session.rb
class VisitSession
  include Mongoid::Document
  include Mongoid::Timestamps

  field :user_id, type: Integer
  index({user_id: 1},{name: :user_id_index})

  # Mock a belongs_to relationship with User model
  def user
    User.find(self.user_id)
  end
end

# user.rb
class User < ActiveRecord::Base

  # Mock a has_many relationship with VisitSession Mongoid model
  def visit_sessions
    VisitSession.where(user_id: self.id)
  end
end

Of course you won't have all the AR methods on VisitSession Mongoid model but you'll at least be able to mock the relationship between the two fairly well.

Hope this helps.

like image 126
Marc Avatar answered Nov 18 '22 13:11

Marc


I don't see any reason why you couldn't have both ActiveRecord and Mongoid models in the same application. That being said, I'm almost certain that you'll run into issues if you try to create relationships between your ActiveRecord and Mongoid models.

If your ActiveRecord models are heavily inter-related, but better suited to a document structure, then I would suggest just biting the bullet and converting them all to Mongoid documents. I had to do this recently on a (large-ish) project, and it's significantly less stressful than you would think.

If you have good unit tests for your models, then it should be a total snap. If you don't - write your unit tests first, make sure they pass with ActiveRecord and then start migrating things over to Mongoid.

like image 45
theTRON Avatar answered Nov 18 '22 12:11

theTRON


i created a module for spoofing the relation in active record models.

module MongoRelations
  def belongs_to_mongo(name, options = {})
    id_name = "mongo_#{name}_id".to_sym
    mongo_model = options[:through] || "Mongo::#{name.to_s.camelize}".constantize

    define_method(name) do
      id = send(id_name)
      mongo_model.find(id) if id.present?
    end

    define_method("#{name}=") do |value|
      send("#{id_name}=".to_sym, value.try(:id).to_s)
    end
  end
end

In my SQL table, I name my mongo relations using the convention mongo_XXX_id, instead of XXX_id

I also namespace all my mongo models under Mongo::

in my active record model

class Foo < ActiveRecord::Base
    belongs_to_mongo :XXX
end

which allows

Foo.new.XXX = Mongo.find('123')
Foo.XXX

or

Foo.new.XXX_id = '123'
Foo.XXX
like image 1
David Chan Avatar answered Nov 18 '22 12:11

David Chan