Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Runtime changing model with mongodb/mongoid

I've to add several fields in a mongoid model, I know there is not migration with MongoDB but if I go on without dropping the DB, making rails to "regenerate" the DB entirely, it doesn't display or use the new fields at all !

What's the best way to go here ? Is there something softer than drop/reopen mongodb ?

Thanks in advance luca

like image 631
Luca G. Soave Avatar asked Jul 17 '11 14:07

Luca G. Soave


1 Answers

In general it should be possible to update old documents with the new fields at runtime. There is no need for migrations in MongoDB.

You maybe want to write rake tasks to update your old documents with the new fields and default values.

You could find out these documents by checking those new fields which have per default a nil value.


Update

Easy style:

If you define a new field with a default value, this value should always be used as long as you set a new one:

app/models/my_model.rb

class MyModel
  include Mongoid::Document
  field :name, type: String
  field :data, type: String
  # NEW FIELD
  field :note, type: String, default: "no note given so far!"
end

If you query your database you should get your default value for documents which haven't this field before your extension:

(rails console)

MyModel.first
#=> #<MyModel …other fields…, note: "no note given so far!">

I tested this with a fresh rails stack with a current mongoid on Ruby 1.9.2 - should work with other stacks, too.

More complicated/complex style:

If you didn't set a default value, you'll get nil for this new field.

app/models/my_model.rb

class MyModel
  include Mongoid::Document
  field :name, type: String
  field :data, type: String
  # NEW FIELD
  field :note, type: String
end

(rails console)

MyModel.first
#=> #<MyModel …other fields…, note: nil>

Then you could set up a rake task and migration file like in this example:

lib/tasks/my_model_migration.rake:

namespace :mymodel do
  desc "MyModel migration task"
  task :migrate => :environment do
    require "./db/migrate.rb"
  end
end

db/migrate.rb:

olds = MyModel.where(note: nil)
# Enumerator of documents without a valid :note field (= nil)
olds.each do |doc|
  doc.note = "(migration) no note given yet"
  # or whatever your desired default value should be
  doc.save! rescue puts "Could not modify doc #{doc.id}/#{doc.name}"
  # the rescue is only a failsafe statement if something goes wrong
end

Run this migration with rake mymodel:migrate.

This is only a starting point and you can extend this to a full mongoid migration engine.

The task :migrate => :environment do … is necessary, otherwise rake won't load models.

like image 185
asaaki Avatar answered Sep 19 '22 22:09

asaaki