views:

564

answers:

2

I'm trying my first application with mongodb on Rails using mongo_mapper and I'm weighing my options on an STI model like below.

It works fine, and I will of course add to this in more ways than I can currently count, I just curious if I wouldn't be better off with Embedded Documents or some such.

I'd like my models to share as much as possible, IE since they all inherit certain attributes, a shared form partial on property/_form.html.erb... in addition to their own unique form elements etc. I know the views will differ but I'm not sure on the controllers yet, as I could use property controller I assume for most things? And I'm sure it will get more complex as I go along.

Any pointers resources and/or wisdom (pain saving tips) would be greatly appreciated

property.rb

class Property
      include MongoMapper::Document

      key :name, String, :required => true
      key :_type, String, :required => true
      key :location_id, Integer, :required => true
      key :description, String
      key :phone, String
      key :address, String
      key :url, String
      key :lat, Numeric
      key :lng, Numeric
      key :user_id, Integer, :required => true
      timestamps!

    end

restaurant

class Restaurant < Property
  key :cuisine_types, Array, :required => true

end

bar

class Bar < Property
  key :beers_on_tap, Array

end
+2  A: 

Don't be afraid of more models, the idea of OO is to be able to cut up your concerns into tiny pieces and then treat each of them in the way they need to be treated.

For example, your Property model seems to be doing a whole lot. Why not split out the geo stuff you've got going on into an EmbeddedDocument (lat, lng, address, etc)? That way your code will remain simpler and more readable.

I use this sort of STI myself and I find it makes my code much simpler and more useable. One of the beauties of using a DB like Mongo is that you can do very complex STI like this and still have a manageable collection of data.

Regarding your cuisine_types and beers_on_tap etc, I think those are fine concepts. It might be useful to have Cuisine and Beer models too, so your database remains more normalized (a concept that is easy to lose in Mongo). e.g.:

class Bar < Property
  key :beer_ids, Array
  many :beers, :in => :beer_ids
end
class Beer
  include MongoMapper:Document
  key :name, String
end
joshsz
Thanks for the input, its very helpful. Also, what do you think about routes? I would like to use the same controller for properties and her children, but I'm not sure how to work that in terms of still using routes... ie similar Restaurants route still uses property controller but specific view partials for each subclass... etc. Is this feasible?
holden
Sure, what I do is (basically) convert the class name of the specific instance into a string and render that partial, using this helper method:( converts TabContainerNode to tab_container )`def partial_for(node) node.class.to_s.underscore.gsub(/_node$/,"")end`The gsub is optional of course, I just don't like having _node on all my node partials.So then you have the partial name and you can render it in the view:<%= render :partial => partial_for(node), :object => node %>
joshsz
But, also how can I use a route like /restaurants which is a subclass of Property, and pass this info to the controller? map.resources :restaurants, :controller => :properties makes the route work, but I'm not able to ascertain what url brought them there to display restaurants and not all properties?
holden
Hmm, I'm not sure offhand, but `rake routes` always helps me puzzle out how the router is figuring things out.
joshsz
A: 

Do you expect to return both Restaurants and Bars in the same query?

If not, you might want to reconsider having them derive from a base type.

By default, Mongo_Mapper is going to put both Restaurants and Bars in a single collection. This could hamper performance and make things harder to scale in the future.

Looking through some of the Mongo_Mapper code, it looks like you might be able to set this on the fly with set_collection_name.

Scott Watermasysk