MongoDB and Ruby on Rails

Preview:

Citation preview

Using MongoDB with Ruby on Rails

presented by Ryan Fischer, Founder of 20spokes

What is covered

• Why we use MongoDB and Ruby on Rails

• Choices made for Object Mapper

• Simple to get started!

Using MongoDB with Ruby on Rails

• Fast in-place updates with atomic modifiers

• Ruby on Rails is productive!

• Object Mappers available for MongoDB

• Mongo Ruby Driver

Mobile / Web

• Ruby on Rails makes REST easy, making APIs easier.

• Render responses as text, JSON, or XML

• Geospatial indexing for location based queries.

• Location-centric websites and mobile applications.

Why we chose MongoDB

• Cowrite - collaborative writing web application

• Versioning needed

• Originally using GridFS

• Travel720 - Gift registry web site

• Self contained relations can take advantage of embedding documents

Mongo Object Mappers for Ruby

• MongoMapper

• Mongoid

• Mongo ODM

• MongoModel

Why we chose Mongoid

• Excellent documentation

• Active community

• Compatibility with other projects/gems

• Similar API to ActiveRecord

• Uses ActiveValidation

• Mongoid Extras: Caching, Paranoid Documents, Versioning, Timestamping, Composite Keys

Compatible Gems with Mongoid

• Devise - Authentication solution for Rails based on Warden. Supports Mongoid out of box.

• Carrierwave - simple and flexible way to upload files from Ruby Applications. Supports grid_fs.

• Geocoder - complete geocoding solution for Rails. Adds geocoding by street or IP address, reverse geocoding, and distance queries.

• Mongoid-rspec - RSpec matchers and macros for Mongoid.

Getting Started

gem "mongoid", "~> 2.3"gem "bson_ext", "~> 1.4"

Include in Gem file

Run the install for Mongoid

rails generate mongoid:config

That’s it!

mongoid.ymldevelopment: host: localhost database: geekbusters_development

test: host: localhost database: geekbusters_test

production: host: <%= ENV['MONGOID_HOST'] %> port: <%= ENV['MONGOID_PORT'] %> username: <%= ENV['MONGOID_USERNAME'] %> password: <%= ENV['MONGOID_PASSWORD'] %> database: <%= ENV['MONGOID_DATABASE'] %> # slaves: # - host: slave1.local # port: 27018 # - host: slave2.local # port: 27019

Developing with MongoDB/Mongoid

• Generating models is the same using the console as with ActiveRecord.

• rails generate model Team name:string city:string location:array

• No migrations needed!

• Take advantage of embedded documents in models where applicable for increased performance.

• Store large files using GridFS

Fields available for Mongoid

• Array

• BigDecimal (Stored as a String)

• Boolean

• Date

• DateTime

• Float

• Hash

• Integer

• Range

• String

• Symbol

• Time

Mongoid Document

class Team include Mongoid::Document include Mongoid::Timestamps field :name, type: String field :city, type: String field :location, :type => Array validates :name, :city, :presence => true end

Persisting in the Controller

def create Team.create(:city => "New York", :name => "Giants")end

def update @team = Team.find(params[:id]) @team = Team.update_attributes(params[:team])end

Indexing

class Team include Mongoid::Document field :name, type: String index :name, unique: trueend

index( [ [ :name, MONGO::ASCENDING ] [ :city, MONGO::ASCENDING ] ] )

Indexing on multiple fields -

Indexing

To create indexes in the database use the rake task

rake db:mongoid:create_indexes

Or configure to autocreate in mongoid.yml (not recommended)

defaults: &defaults autocreate_indexes: true

Relations in Models

• Associations between models can be embedded or referenced

• NO JOINS!

• Objects that are referenced involve a separate query

• Embedded documents can be very efficient with reducing queries to one while managing the size of the document

• One to One, One to Many, and Many to Many relations available

Relations in Mongoid - Embedded

• Embedded Relations - stored inside other documents in the database.class Blog include Mongoid::Document embeds_many :postsend

class Post include Mongoid::Document embedded_in :blogend

Embedded Posts

{ "_id" : ObjectId("4e9ba47fcdffba523f000004"), "name" : "Geekbusters Blog", "posts" : [ {"content" : "Slimer is right behind you.", _id" : ObjectId("4e9ba47fcdffba523f000005")} ]}

Polymorphic Behaviorclass Image include Mongoid::Document embeds_many :comments, as: :commentableend

class Document include Mongoid::Document embeds_many :comments, as: :commentableend

class Comment include Mongoid::Document embedded_in :commentable, polymorphic: trueend

Relations in Mongoid - Referenced

class Blog include Mongoid::Document has_many :posts, dependent: :deleteend

class Post include Mongoid::Document belongs_to :blogend

Referenced Relations - stores reference to a document in another collection, typically an id

Many to Many Relationship

class Tag include Mongoid::Document has_and_belongs_to_many :postsend

class Post include Mongoid::Document has_and_belongs_to_many :tagsend

Querying

• Queries are of type Criteria, which is a chainable and lazily evaluated wrapper to a MongoDB dynamic query.

• Chainable queries include:all_in all_ofalso_in and any_ofascdescdistinctexcludes

includeslimitnearnot_inonlyorder_byskipwherewithout

Query Examples

Team.near(location: [20.70, 38.15]).limit(20)

Team.any_in(names: ["Giants", "Bears", "Lions"])

Team.find_or_create_by(name: "Giants")

Team.where(name: "Cowboys").destroy_all

Versioning with Mongoid

• Embeds a version of the object on each save.

• Can skip versioning and also set the max versions.

• Add to model -

include Mongoid::Versioning

Versioning Example{"_id" : ObjectId("4e9ba7fdcdffba52d6000004"), "content" : "Impossible. I am too loud to fall asleep to.", "created_at" : ISODate("2011-10-17T03:58:53Z"), "title" : "Impossible to sleep to - this guy.", "updated_at" : ISODate("2011-10-17T03:58:53Z"), "version" : 3, "versions" : [ {"title" : "Who is asleep?", "content" : "Wake up the guy next to you if they are asleep. Thanks.", "version" : 1, "created_at" : ISODate("2011-10-17T03:58:53Z")}, {"title" : "Who is asleep?", "content" : "Impossible. I am too loud to fall asleep to.", "created_at" : ISODate("2011-10-17T03:58:53Z"), "version" : 2 } ]}

And More

• Identity Map

• Callbacks

• Scopes

• Dirty Tracking

Testing Rails with MongoDB

• RSpec-Mongoid provides test matchers

• RSpec does not refresh the database with each test run.

• Database Cleaner to the rescue. It be setup to truncate a database before running tests. Add below after installing the DatabaseCleaner Gem.

config.before(:suite) do DatabaseCleaner.strategy = :truncation DatabaseCleaner.orm = "mongoid" end

Hosting Options

• Heroku is a widely used cloud hosting for Ruby on Rails

• MongoHQ and MongoLab both have add on options

• Options and pricing are similar

Thanks!!

Email: ryan.fischer@20spokes.com

Twitter: @ryanfischer20

Check out Geekbusters on Github