4 comments
This article is not an introduction to MongoDB or MongoMapper.
It's a summary of a few things I learned while I was working on my first real project based on MongoDB.
You will find here some tips that will (I hope) help you during your MongoMapper learning phase.
I've been working recently on a project for a client who wanted to sell second-hand vehicles on his website.
It's a classic story : visitors can register and post announces describing the vehicle they'd like to sell.
Various vehicles types are available : cars, bikes, trucks and more.
Each announce is characterized by various attributes depending on the selected vehicle type.
You can find general information (brand, mileage, ...), information on the equipment (GPS? Radio?), the contact person, some pictures... and more.
Of course, the equipment of a truck and a bike will be very different!
I've been thinking for a while about the best way to structure my SQL schema but I was never satisfied with the results.
It was either too complicated to implement or to maintain and I also had the impression that the solution was not pretty enough.
Being addicted to new technologies, I directly took this opportunity to jump into the NoSQL world and start learning MongoDB, a documents oriented database more and more popular in the Rails community.
When I think about it, it's so much more beautiful to represent announces as documents, containing various subdocuments (general info, equipment, pictures...) instead of a couple of rows in SQL tables.
Notice that I'm using ActiveRecord (AR) an MongoMapper (MM) in the same project (because I installed my CMS engine) and it's working perfectly!
MongoMapper is very easy to learn because it mainly respects the ActiveRecord API. You will not be lost in a unknown world because you will be able to manipulate your documents as you are currently doing with your AR records. You can persist documents using well-known methods like save, create and update_attributes. You can also select the data you need using finders and conditions. Associations, callbacks and validation are available as well. In fact, the views and the controllers generated by a scaffold can directly be used as they are with MongoMapper.
That being said, don't think that learning MM will be as easy as pie. This is because your brain is currently SQL formated!
During the development of your project you will ask yourself a few questions and you'll have to search to find the answer.
For example, how can you execute a search on text fields? With SQL, you know that you have to use LIKE. But what is the equivalent with MongoDB?
MongoMapper is a young project and it is still under heavy development. There is currently no documentation, no books and only a few people are blogging about it.
The best place to get help is the Google group.
Once familiarized with the beast, you'll probably not want to come back to ActiveRecord.
After two full months of Mongo I had to get back to ActiveRecord and this is probably only at that time that I really realized the awesomeness of MongoMapper.
It was hard to remember migrations syntax and rake tasks to execute them... With MongoMapper, these painful migrations are gone!
The biggest disadvantage of the MongoMapper-ActiveRecord switch is that you loose most of your plugins. I'm for example a fan of Authlogic and I had to drop it. This is probably a matter of time because I know that his author is working on a MM compatible version. Some plugins will still work, like the excellent will_paginate for example.
Also, some very used features of ActiveRecord like named scopes aren't available yet.
MongoDB allows you to create embedded documents. In my personal case, an Announce can contain multiple pictures :
At the beginning it maybe difficult to determine whether or not you should use classic or embedded documents.
To help you in your choice, I will tell you when not to use embedded documents.
It's important to know that an EmbeddedDocument has a very limited set of methods.
For example, you cannot call the find method on a such object.
Following instructions are not valid :
So if you want to be able to manipulate a document independently of his parent, you should not use an embedded document.
These documents are mostly manipulated using classic Ruby code.
To create a new embedded document :
To select a document :
a.pictures returns a simple Ruby Array, the select method is a classical Ruby method, it has nothing to do with MongoMapper.
If you want to edit a document :
Note that you have to call the save method on the main document (the announce in this case).
Finally, here is the code to delete an embedded document :
You will find many examples on the Internet using the String type to declare "foregin keys".
But this is deprecated and should not be used anymore with new versions of MM.
Use ObjectID instead :
At your first tries you will also probably try to find an equivalent of the ActiveRecord has_one association.
We don't need that with MongoMapper because keys are not limited to basic types.
If you want to specify that a user has one profile, you just have to define your key like this :
The Profile class can for example be an EmbeddedDocument.
Let's come back to one of my first questions : how can you execute a search on text fields (equivalent of LIKE in SQL)?
By using regular expressions!
All users containing "thony" in their email address will be returned.
The /i allows us to specify that we want to ignore the case.
As you understood, you'll be able to perform very precise research if you refresh your skills in Javascript regular expressions.
What if I want to find all users created today? You can use $gt (greater than) and $lt (lower than) :
And finally, how do you find articles matching the searched keyword in their title OR description?
We'd like to generate a OR instead of a AND :
I use Machinist a lot to generate fake data in my specs.
Happily, an adapter exists for MongoMapper and you can find it on GitHub.
Just install the gem (gem install machinist_mongomapper) then edit your blueprints.rb file to load the new adapter (require 'machinist/mongomapper').
As you probably noticed, Mongo IDs are not simple numbers.
If you have to generate fake IDs in your tests, the following method is for you : Mongo::ObjectID.new
I'll finish this article by promoting two plugins I recently developed :
3 comments
If like me you are used to work with existing database schemas, the reverse_scaffold script should be interesting for you.
Just download it from GitHub and copy the file into the "script" folder of your Ruby on Rails application.
You can then execute it from your terminal :
$ ruby script/reverse_scaffold users user
For this example I presume you have an existing "users" table in your database described by the following columns :
In this case, the script will execute the command :
ruby script/generate scaffold user --skip-migration id:integer pseudo:string email:string password:string
As you understood, the first argument represents the name of the table and the second one is the name of the model to generate.
You can use the -V option if you don't want to execute the scaffold but just display the generated command.
If you are an RSpec adept, you can specify the -r option and rspec_scaffold will then be used.
For more information :
$ ruby script/reverse_scaffold -h
1 comment
0 comment
belongs_to :company, :touch => true
class Module
attr_accessor :_setup_block
attr_accessor :_dependencies
def setup(&blk)
@_setup_block = blk
end
def use(mod)
return if self < mod
(mod._dependencies || []).each do |dep|
use dep
end
# raise "Circular dependencies" if self < mod
include mod
extend mod.const_get("ClassMethods") if mod.const_defined?("ClassMethods")
class_eval(&mod._setup_block) if mod._setup_block
end
def depends_on(mod)
return if self < mod
@_dependencies ||= []
@_dependencies << mod
end
end
92 comments