For awhile now my pet project has been running on top of the “noSQL” document database technology, MongoDB, that the folks over at 10Gen have developed. After testing the tech and going to a couple of the New York City meetups, I immediately decided to push myself to utilize this in Type Aloud. But, along with the Rails 3.0 release and Mongoid 2 (the Active Record wrapper that I am using for Mongo) I have had a hard time finding accurate up-to-date information on some less used features. One particular problem has been referencial (foreign) associations and the use of composite keys.
Because the user’s poems and stories in Type Aloud and only unique on the user level I have been unable to use a simple uniqueness validation on the document identifier which up until this point was just the default hash provided by the database. I need the composite, the user identifier and the story identifier, to be unique; I only want the user to only have one story named “Winnie the Pooh.” It may be easier to understand some code.
A standard Story model in Rails (using Mongoid) might look something like this. Notice how I do not need to explicitly provide the key because it is assumed, by default, we will use the database standard hash provided as our primary key for the document. As you see I explicitly specify the slug field as being an index as this is what will most commonly be queried. In this case, the “id” field may contain something along the lines of 4c897698cd44a10f8b000006.
class Story include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia include Mongoid::Versioning # Document fields field :title, :type => String field :slug, :type => String field :description, :type => String field :disabled, :type => Boolean, :default => true # Document associations embeds_many :chapters referenced_in :user # Document indicies and keys index :slug, :background => true, :unique => false # Document validators validates_presence_of :title, :description end
The problem here, and the exact reason why I merely cannot validate uniqueness on the title, is because multiple users can have the same title for a story or poem. The submissions will be presented in a vanity URL format, e.g. http://typealoud.com/johnbellone/the-darwin-prospect/chapter/1, and thus will almost always be queried with the user’s name. My solution to making this unique is quite simple. So now the story identifier will be in the format of the-darwin-prospect-4c892963cd44a108a2000008 where that hash at the end is the user’s identifier.
class Story include Mongoid::Document include Mongoid::Timestamps include Mongoid::Paranoia include Mongoid::Versioning # Document fields field :title, :type => String field :slug, :type => String field :description, :type => String field :disabled, :type => Boolean, :default => true # Document associations embeds_many :chapters referenced_in :user # Document indicies and keys key :title, :user_id # Document validators validates_presence_of :title, :description end
This works as expected and I no longer get duplicate documents. The only issue that I am having right now is Mongoid does not seem to be correctly throwing for a second save which seemingly just creates another version with a timestamp in the future. Merely adding an explicit validator does not seem to help either. Other than that this solution seems to be up to snuff, but as always, I am sure there may be a more elegant way to handle this. Any thoughts?
After work each night for the past couple of months I have been getting my hands dirty with Ruby/Rails development. I can imagine that most developers out there grow to love rake, but for the life of me I literally hate executing a series of commands on a regular basis. The programmer in me loves automation, and while in development I find myself dropping, re-creating and migrating the database constantly. Why should I have to issue three commands?
rake db:drop rake db:create rake db:migrate
After a little bit of searching I found that this can be accomplished in two commands.
rake db:reset rake db:migrate
Welp, that’s not enough for me because I am just that damn lazy. So, a simple rake task to bounce a database.
namespace :db do
desc "Drop, create and migrate the current database"
task :bounce => :environment do
Rake::Task['db:reset'].invoke
Rake::Task['db:migrate'].invoke
end
end
I hope you enjoy it as much as I do.
rake db:bounce