MongoMapper
          Kerry Buckley
IPRUG Lightning talk, 2 August 2011
What is it?
What is it?
Why we switched
Why we switched

• Modelling complex object hierarchy
• Painfully-complex polymorphic associations
 • (and about to get worse)
• Data seemed to suit a document DB
Document

   class User
 include MongoMapper::Document

 key :username, String
 key :real_name, String
 key :date_of_birth, Date
end
Supported types
• Array      • Object    • Date

• Float      • String    • ObjectId

• Hash       • Time      • Set

• Integer    • Binary

• NilClass   • Boolean
Custom types
  module MongoMapper::BigDecimal
 def to_mongo value
  value.to_s
 end

 def from_mongo value
  value.present? ? new(value.to_s) : nil
 end
end
BigDecimal.send :extend, MongoMapper::BigDecimal

class Person
 include MongoMapper::Document
 key :height, BigDecimal
end
Database migrations
Database migrations


  This page intentionally left blank
Time and user stamps
   class Article
 include MongoMapper::Document

 key :title, String
 key :body, String
 timestamps!
 userstamps!
end

class User
 include MongoMapper::Document

 key :name, String
end
Simple associations
   class Owner
 include MongoMapper::Document
 many :pets
end

class Pet
 include MongoMapper::Document
 belongs_to :owner
 one :bed
end

class Bed
 include MongoMapper::Document
 belongs_to :pet
end
Embedded document
   class Order
 include MongoMapper::Document

 many :line_items
end

class LineItem
 include MongoMapper::EmbeddedDocument

 key :name, String
 key :quantity, Integer
end
Many-to-many
   class Book
 include MongoMapper::Document

 key :title
 key :author_ids, Array
 many :authors, :in => :author_ids
end

class Author
 include MongoMapper::Document

 key :name
end
Basic polymorphism
   class BlogPost
 include MongoMapper::Document
 key :body
 many :comments, :as => :commentable
end

class HomePage
 include MongoMapper::Document
 key :content
 many :comments, :as => :commentable
end

class Comment
 include MongoMapper::Document
 key :text
 belongs_to :commentable, :polymorphic => true
SCI polymorphism
class Commentable
 include MongoMapper::Document
 many :comments
end

class BlogPost < Commentable
 key :body
end

class HomePage < Commentable
 key :content
end

class Comment
 include MongoMapper::Document
 key :text
 belongs_to :commentable
end
Reverse polymorphism
   class Site
 include MongoMapper::Document
 many :pages
end

class Page
 include MongoMapper::Document
 belongs_to :site
end

class HomePage < Page
 key :content
end

class BlogPost < Page
 key :body
M2M polymorphism
   class Book
 include MongoMapper::Document
 key :author_ids, Array
 many :authors, :in => :author_ids
end

class Novel < Book
end

class Textbook < Book
end

class Author
 include MongoMapper::Document
end

class Editor < Author
end
Validations
   class Member
 include MongoMapper::Document

 key :name, String
 key :age, Integer
 key :sex, String

 validates_uniqueness_of :name
 validates_presence_of :name
 validates_numericality_of :age
 validates_inclusion_of :sex, :in => %w{male female}

 validate :custom_validation

 def custom_validation
  ...
 end
end
Validation shorthand
   class Member
 include MongoMapper::Document

 key :name, String, :required => true, :unique => true
 key :age, Integer, :numeric => true
 key :sex, String, :in => %w{male female}

 validate :custom_validation

 def custom_validation
  ...
 end
end
Querying
   class User
 include MongoMapper::Document
 key :first_name, String
 key :last_name, String
 scope :smiths, where(:last_name => "Smith")
end

User.find "4e0c70aea7a6c32ab5000003"

User.find_by_first_name "Kerry"

User.find_by_first_name! "Kerry"

User.find_by_first_name_and_last_name "Kerry", "Buckley"

User.find_all_by_last_name "Buckley"

User.smiths.find_by_first_name "John"
Modifiers
   class Page
 include MongoMapper::Document

 key :title,   String
 key :hit_count, Integer, :default => 0
 key :tags,     Array
end

@page.increment :hit_count => 1
Page.increment {:title => "Home"}, :hit_count => 1

Page.set {:title => "Home"}, :title => "New Home"

Page.unset {:title => "Home"}, :title

Page.push {:title => "Home"}, :tags => "foo"
Gotchas
Gotchas
• No transactions
• find versus find!
• Writes are fire-and-forget by default
• Uniqueness is per class, not per collection
• Formtastic needs hints of field type
Reflections
Reflections

• Longish but fairly painless switch
• Acceptance/integration tests invaluable
• Specs/unit tests a pain to migrate
• Decouple behaviour from persistence!
Fin.

MongoMapper lightning talk

  • 1.
    MongoMapper Kerry Buckley IPRUG Lightning talk, 2 August 2011
  • 2.
  • 3.
  • 4.
  • 5.
    Why we switched •Modelling complex object hierarchy • Painfully-complex polymorphic associations • (and about to get worse) • Data seemed to suit a document DB
  • 6.
    Document class User include MongoMapper::Document key :username, String key :real_name, String key :date_of_birth, Date end
  • 7.
    Supported types • Array • Object • Date • Float • String • ObjectId • Hash • Time • Set • Integer • Binary • NilClass • Boolean
  • 8.
    Custom types module MongoMapper::BigDecimal def to_mongo value value.to_s end def from_mongo value value.present? ? new(value.to_s) : nil end end BigDecimal.send :extend, MongoMapper::BigDecimal class Person include MongoMapper::Document key :height, BigDecimal end
  • 9.
  • 10.
    Database migrations This page intentionally left blank
  • 11.
    Time and userstamps class Article include MongoMapper::Document key :title, String key :body, String timestamps! userstamps! end class User include MongoMapper::Document key :name, String end
  • 12.
    Simple associations class Owner include MongoMapper::Document many :pets end class Pet include MongoMapper::Document belongs_to :owner one :bed end class Bed include MongoMapper::Document belongs_to :pet end
  • 13.
    Embedded document class Order include MongoMapper::Document many :line_items end class LineItem include MongoMapper::EmbeddedDocument key :name, String key :quantity, Integer end
  • 14.
    Many-to-many class Book include MongoMapper::Document key :title key :author_ids, Array many :authors, :in => :author_ids end class Author include MongoMapper::Document key :name end
  • 15.
    Basic polymorphism class BlogPost include MongoMapper::Document key :body many :comments, :as => :commentable end class HomePage include MongoMapper::Document key :content many :comments, :as => :commentable end class Comment include MongoMapper::Document key :text belongs_to :commentable, :polymorphic => true
  • 16.
    SCI polymorphism class Commentable include MongoMapper::Document many :comments end class BlogPost < Commentable key :body end class HomePage < Commentable key :content end class Comment include MongoMapper::Document key :text belongs_to :commentable end
  • 17.
    Reverse polymorphism class Site include MongoMapper::Document many :pages end class Page include MongoMapper::Document belongs_to :site end class HomePage < Page key :content end class BlogPost < Page key :body
  • 18.
    M2M polymorphism class Book include MongoMapper::Document key :author_ids, Array many :authors, :in => :author_ids end class Novel < Book end class Textbook < Book end class Author include MongoMapper::Document end class Editor < Author end
  • 19.
    Validations class Member include MongoMapper::Document key :name, String key :age, Integer key :sex, String validates_uniqueness_of :name validates_presence_of :name validates_numericality_of :age validates_inclusion_of :sex, :in => %w{male female} validate :custom_validation def custom_validation ... end end
  • 20.
    Validation shorthand class Member include MongoMapper::Document key :name, String, :required => true, :unique => true key :age, Integer, :numeric => true key :sex, String, :in => %w{male female} validate :custom_validation def custom_validation ... end end
  • 21.
    Querying class User include MongoMapper::Document key :first_name, String key :last_name, String scope :smiths, where(:last_name => "Smith") end User.find "4e0c70aea7a6c32ab5000003" User.find_by_first_name "Kerry" User.find_by_first_name! "Kerry" User.find_by_first_name_and_last_name "Kerry", "Buckley" User.find_all_by_last_name "Buckley" User.smiths.find_by_first_name "John"
  • 22.
    Modifiers class Page include MongoMapper::Document key :title, String key :hit_count, Integer, :default => 0 key :tags, Array end @page.increment :hit_count => 1 Page.increment {:title => "Home"}, :hit_count => 1 Page.set {:title => "Home"}, :title => "New Home" Page.unset {:title => "Home"}, :title Page.push {:title => "Home"}, :tags => "foo"
  • 23.
  • 24.
    Gotchas • No transactions •find versus find! • Writes are fire-and-forget by default • Uniqueness is per class, not per collection • Formtastic needs hints of field type
  • 25.
  • 26.
    Reflections • Longish butfairly painless switch • Acceptance/integration tests invaluable • Specs/unit tests a pain to migrate • Decouple behaviour from persistence!
  • 27.