Not quite sure where the best place is to define MongoDB indexes via MongoMapper in a Rails app… My progression has been:
- as part of the key definition in the model class
- in a rails initializer
- hybrid between initializer and model
- rake task invoking model methods
Define Indexes on the Keys
This works fine during development.
class Account include MongoMapper::Document ... # Attributes :::::::::::::::::::::::::::::::::::::::::::::::::::::: key :login, String, :unique => true, :index => true key :msid, String, :index => true key :doctor_num, String, :index => true ... end
Define Indexes in an Initializer
When I wanted to trigger a new index creation, I would add it here. Only problem is that restarting a production server with tons of data gets held up by the create index task.
# Rails.root/config/initializers/mongo_config.rb Account.ensure_index(:last_name) Group.ensure_index(:name) Group.ensure_index(:group_num) ...
Define Indexes in a Class Method, Invoke in Initializer
A small tweak to putting indexes into an initializer was to place the knowledge of the indexes back into the model classes themselves. Then, all you needed to do was invoke the model class method to create it’s own indexes.
The Initializer Code
# Rails.root/config/initializers/mongo_config.rb Event.create_indexes Encounter.create_indexes Setting.create_indexes
The Model(s) Code
class Setting include MongoMapper::Document # Attributes :::::::::::::::::::::::::::::::::::::::::::::::::::::: # What the user sees as a label key :label, String # How we reference it in code key :identifier, String, :required => true ... # Indexes ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: def self.create_indexes self.ensure_index(:identifier, :unique => true) self.ensure_index(:label, :unique => true) end ... end
Enter the Rake!
Of course, you could also invoke the index creation code in a rake task, as pointed out here.
The beauty behind a rake task as best I can tell is this:
- You can run it at any time to update the indexes
- You do not bring a deploy to a screeching halt because you are waiting for index creation
I was already standardizing on how I was creating indexes inside each model class — where better to keep on top of what the indexes for a class should be than in the class itself!
# app/models/setting.rb class Setting ... def self.create_indexes self.ensure_index(:identifier, :unique => true) self.ensure_index(:label, :unique => true) end ... end
I created a new class in the model directory (so that it is close to where the models are defined) that simply loops through each model class to generate the proper indexes:
# app/models/create_indexes.rb class CreateIndexes def self.all puts "*"*15 + " GENERATING INDEXES" + "*"*15 MongoMapper.database.collection_names.each do |coll| # Avoid "system.indexes" next if coll.index(".") model = coll.singularize.camelize.constantize model.create_indexes if model.respond_to?(:create_indexes) model.show_indexes if model.respond_to?(:show_indexes) end end end
You can invoke it easily from the Rails console: CreateIndexes.all
Next I created a rake task (in lib/tasks/indexes.rake
) that invoked the ruby code to do the indexing mojo.
namespace :db do namespace :mongo do desc "Create mongo_mapper indexes" task :index => :environment do CreateIndexes.all end end end
Any tips/comments/insights appreciated…
PS: self.show_indexes Mix-in
I created a mix-in for the “show_indexes()” class method for each model. I could not add it directly to the MongoMapper::Document class unfortunately — I ran into errors and finally gave up. Here’s the mix-in that I defined in lib/mongo_utils.rb
:
module MongoMapper module IndexUtils puts "Customizing #{self.inspect}" module ClassMethods def show_indexes puts "%s #{self.name} INDEXES %s" % ["*"*12, "*"*12] self.collection.index_information.collect do |index| puts " #{index[0]}#{index[1].has_key?("unique") ? " (unique)":"x"}" end end end def self.included(base) #puts "#{base} is being extended'" base.extend(ClassMethods) end end end
And you use it as follows:
require 'mongo_mapper' require 'mongo_utils' class Setting include MongoMapper::Document include MongoMapper::IndexUtils ...
Pingback: MongoDB Index Performance » Technical Debt