Ruby on Rails and NoSQL, adding fields

I just dive into Mongodb and MongoID with Rails, and I find it awesome. One thing that NoSQL helps is when I can add extra fields to my model without any extra effort whenever I want:

class Page include Mongoid::Document include Mongoid::MultiParameterAttributes field :title, :type => String field :body, :type => String field :excerpt, :type => String #Added later field :location, :type => String #Added later field :published_at, :type => Time validates :title, :presence => true validates :body, :presence => true validates :excerpt, :presence => true end 

And it works just as it should. But my question is: (sorry, if this is trivial), the existing entries are empty and do not have a specific value for the newly added field. For example, in the sample blog application, after I posted two posts, I decided to add a clipping and location field to my database (see code above). Any blog post that is published after adding these new fields can be entered in the excerpt field. But messages posted before these two new fields were added have null values ​​(which is understandable why), which I cannot confirm. Is there an elegant solution for this?

Thanks.

+4
source share
1 answer

There are three main options:

  • Update everything inside MongoDB to enable excerpt.
  • Use the after_initialize hook to add a default excerpt to existing objects when you pull them out of MongoDB.
  • Lock your validation logic to only check for excerpts from new objects.

(1) it takes a (possibly big) hit in time when you make a change, but this is just one time, and you don't need to worry about it after that. You pull each page out of MongoDB, do page.excerpt = 'some default excerpt' , and then save it back to MongoDB. If you have many pages, you will want to process them in pieces, say, 100 at a time. If you do this, you can search in excerpts without worrying about what you should do with null s. You can also do this inside MongoDB by sending a JavaScript fragment to MongoDB :

 connection.eval(%q{ db.pages.find({}, { _id: true }).forEach(function(p) { db.pages.update( { _id: p._id }, { $set: { excerpt: 'some default excerpt' } } ); }); }) 

(2) would look something like this:

 after_initialize :add_default_excerpt, :unless => :new_record? #... private def add_default_excerpt self.excerpt = 'some default excerpt' unless self.excerpt.present? end 

You can move unless self.excerpt to :unless , if you don't mind using lambda:

 after_initialize :add_default_excerpt, :unless => ->{ |o| o.new_record? || o.excerpt.present? } #... private def add_default_excerpt self.excerpt = 'some default excerpt' end 

This should be pretty quick and easy to set up, but there are also disadvantages. First of all, in your MongoDB you will have a bunch of null , which you may have to handle specifically during the search. In addition, you will be carrying a bunch of code and logic to process old data, but this baggage will be used less and less over time. In addition, the after_initialize call after_initialize not come for free.

(3) requires that you skip checking the availability of excerpts for non-new pages ( :unless => :new_record? ), Or you will have to find a way to distinguish new objects from old ones, as well as correctly process changes, both new and new and old pages. You can also get people to provide an excerpt when they change the page and leave your check as is; including :default => '' on your field :excerpt , will take care of any nil problems in the views, etc.


If possible, I will go with (1). If the update takes too much time and you want the site to work and work while you are fixing MongoDB, you can add :default => '' when updating, and then remove the :default option, restart and manually fix all the deviations that were received through.

+4
source

Source: https://habr.com/ru/post/1393079/


All Articles