Your SlideShare is downloading. ×
Practical Ruby Projects with MongoDB - MongoSF
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×
Saving this for later? Get the SlideShare app to save on your phone or tablet. Read anywhere, anytime – even offline.
Text the download link to your phone
Standard text messaging rates apply

Practical Ruby Projects with MongoDB - MongoSF

2,337

Published on

Slides for the talk I gave at MongoSF on 4/30/2010.

Slides for the talk I gave at MongoSF on 4/30/2010.

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,337
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
69
Comments
0
Likes
8
Embeds 0
No embeds

Report content
Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
No notes for slide























































































































































  • Transcript

    • 1. Practical Ruby Projects with
    • 2. Who am I? Alex Sharp Lead Developer at OptimisDev @ajsharp alexjsharp.tumblr.com github.com/ajsharp
    • 3. We’re going to talk about two things.
    • 4. 1. Why Mongo makes sense
    • 5. 2. Using MongoDB with Ruby
    • 6. But first...
    • 7. This talk is not about shiny objects
    • 8. It’s not about the new hotness
    • 9. It’s not about speed...
    • 10. Or performance...
    • 11. Or even scalability...
    • 12. Or boring acronyms like...
    • 13. CAP
    • 14. Or ACID
    • 15. It’s about practicality.
    • 16. This talk is about creating software.
    • 17. Migrating to another technology is inefficient
    • 18. Most people are reluctant to move off RDBMS-es
    • 19. Relational DBs are both 1.Antiquated 2.Inefficient
    • 20. The Problems of SQL
    • 21. A brief history of SQL Developed at IBM in early 1970’s. Designed to manipulate and retrieve data stored in relational databases
    • 22. A brief history of SQL Developed at IBM in early 1970’s. Designed to manipulate and retrieve data stored in relational databases this is a problem
    • 23. Why??
    • 24. We don’t work with data...
    • 25. We work with objects
    • 26. We don’t care about storing data
    • 27. We care about persisting state
    • 28. Data != Objects
    • 29. Therefore...
    • 30. Relational DBs are an antiquated tool for our needs
    • 31. Ok, so what? SQL schemas are designed for storing and querying data, not persisting objects.
    • 32. To reconcile this mismatch, we have ORM’s
    • 33. Object Relational Mappers
    • 34. Ok, so what? We need ORMs to bridge the gap (i.e. map) between SQL and native objects (in our case, Ruby objects)
    • 35. We’re forced to create relationships for data when what we really want is properties for our objects.
    • 36. This is NOT efficient
    • 37. @alex = Person.new( :name => "alex", :stalkings => [Friend.new("Jim"), Friend.new("Bob")] )
    • 38. Native ruby object <Person:0x10017d030 @name="alex", @stalkings= [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]>
    • 39. JSON/Mongo Representation @alex.to_json { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] }
    • 40. SQL Schema Representation # in a SQL schema people: - name stalkings: - name - stalkee_id
    • 41. SQL Schema Representation # in a SQL schema people: - name stalkings: Wha Wha!? - name - stalkee_id
    • 42. SQL Schema Representation # in a SQL schema people: - name stalkings: stalkings ain’t got - name no stalkee_id - stalkee_id
    • 43. SQL Schema Representation # in a SQL schema people: - name stalkings: stalkee_id is how we map our - name object to fit in a SQL schema - stalkee_id
    • 44. SQL Schema Representation # in a SQL schema people: - name stalkings: i.e. the “pointless join” - name - stalkee_id
    • 45. Ruby -> JSON -> SQL <Person:0x10017d030 @name="alex", @stalkings= Ruby [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]> @alex.to_json JSON { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } people: - name SQL stalkings: - name - stalkee_id
    • 46. Ruby -> JSON -> SQL <Person:0x10017d030 @name="alex", @stalkings= Ruby [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]> @alex.to_json JSON { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } people: - name Feels like we’re having SQL to work too hard here stalkings: - name - stalkee_id
    • 47. Ruby -> JSON -> SQL <Person:0x10017d030 @name="alex", @stalkings= Ruby [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]> @alex.to_json JSON { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } people: - name SQL stalkings: - name - stalkee_id
    • 48. This may seem trivial for simple object models
    • 49. But consider a tree-type object graph
    • 50. Example: a sentence builder
    • 51. Ok, so what? You’re probably thinking... “Listen GUY, SQL isn’t that bad”
    • 52. Ok, so what? Maybe.
    • 53. Ok, so what? But we can do much, much better.
    • 54. Summary of Problems mysql is both antiquated and inefficient
    • 55. Needs for a persistence layer:
    • 56. Needs for a persistence layer: 1.To persist the native state of our objects
    • 57. Needs for a persistence layer: 1.To persist the native state of our objects 2.Should NOT interfere w/ application development
    • 58. Needs for a persistence layer: 1.To persist the native state of our objects 2.Should NOT interfere w/ application development 3.Provides features necessary to build modern web apps
    • 59. Mongo to the Rescue!
    • 60. Mongo was made for web apps
    • 61. Mongo goes the 80/20 route
    • 62. Document storage
    • 63. Mongo stores everything in binary JSON
    • 64. Simplifying schema design Maps/hashes/associative arrays are arguably among the best object serialization formats
    • 65. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] }
    • 66. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } In Mongo, stalkings is an “embedded document”
    • 67. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } Great for one-to-many associations
    • 68. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } No JOINS required.
    • 69. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } This is a good thing for scalability, because JOINS limit our ability to horizontally scale.
    • 70. Mongo Representation { name: "alex", stalkings: [{ name: "Jim" }, { name: "Bob" }] } But more importantly, this is more intuitive than messing with foreign keys
    • 71. Mongo allows us to store objects in a near-native format
    • 72. This is awesome.
    • 73. Much less schema design.
    • 74. Much less brain pain.
    • 75. Embedded Documents
    • 76. Lots of 1-to-many relationships can be implemented as an embedded doc
    • 77. This results in way fewer “pointless JOINs”
    • 78. Scalability
    • 79. Ok, so I lied.
    • 80. ;)
    • 81. The ability to easily scale out is an important part of the philosophy behind Mongo
    • 82. Also has implications for in how we store our objects
    • 83. If scaling out is easy, who cares about the DB getting “too large”?
    • 84. Just fire up another EC2 instance and be done with it.
    • 85. Auto-sharding is going to make this stupid easy.
    • 86. So stay tuned...
    • 87. Other features necessary for building web apps
    • 88. indexes
    • 89. replication/redundancy
    • 90. rich query syntax
    • 91. Rich query syntax
    • 92. Practical Projects
    • 93. Four Examples 1.Accounting Application 2.Capped Collection Logging 3.Blogging Application 4.Storing binary data w/ GridFS
    • 94. A simple general ledger accounting application
    • 95. The object model ledger * transactions * entries
    • 96. The object model # Credits Debits Transaction { 1 { :account => “Cash”, :amount => 100.00 } { :account => “Notes Pay.”, :amount => 100.00 } Ledger Entries Transaction { 2 { :account => “A/R”, :amount => 25.00 } { :account => “Gross Revenue”, :amount => 25.00 }
    • 97. Object model summary
    • 98. Object model summary Each ledger transaction belongs to a ledger. Each ledger transaction has two ledger entries which must balance.
    • 99. Object Model with ActiveRecord @credit_entry = LedgerEntry.new :account => "Cash", :amount => 100.00, :type => "credit" @debit_entry = LedgerEntry.new :account => "Notes Pay.", :amount => 100.00, :type => "debit" @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [@credit_entry, @debit_entry]
    • 100. Object Model with ActiveRecord @credit_entry = LedgerEntry.new :account => "Cash", :amount => 100.00, :type => "credit" @debit_entry = LedgerEntry.new :account => "Notes Pay.", :amount => 100.00, :type => "debit" @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [@credit_entry, @debit_entry] In a SQL schema, we need a database transaction to ensure atomicity
    • 101. Object Model with Mongo @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [ { :account => 'Cash', :type => "credit", :amount => 100.00 }, { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 } ]
    • 102. Object Model with Mongo @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [ { :account => 'Cash', :type => "credit", :amount => 100.00 }, { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 } ] This is the perfect case for embedded documents.
    • 103. Object Model with Mongo @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [ { :account => 'Cash', :type => "credit", :amount => 100.00 }, { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 } ] We would never have a ledger entry w/o a ledger transaction.
    • 104. Object Model with Mongo @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [ { :account => 'Cash', :type => "credit", :amount => 100.00 }, { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 } ] In this case, we don’t even need database transactions.
    • 105. Object Model with Mongo @ledger_transaction = LedgerTransaction.new :ledger_id => 1, :ledger_entries => [ { :account => 'Cash', :type => "credit", :amount => 100.00 }, { :account => 'Notes Pay.', :type => "debit", :amount => 100.00 } ] Sweet.
    • 106. Logging with Capped Collections
    • 107. Baseless statistic 95% of the time logs are NEVER used.
    • 108. Most logs are only used when something goes wrong.
    • 109. Capped collections Fixed-sized, limited operation, auto age-out collections (kinda like memcached) Fixed insertion order Super fast (faster than normal writes) Ideal for logging and caching
    • 110. Great. What’s that mean? We can log additional pieces of arbitrary data effortlessly due to Mongo’s schemaless nature.
    • 111. This is awesome.
    • 112. Now we have logs we can query, analyze and use!
    • 113. Also a really handy troubleshooting tool
    • 114. Scenario User experiences weird, hard to reproduce error.
    • 115. Scenario Wouldn’t it be useful to see the complete click-path?
    • 116. Bunyan http://github.com/ajsharp/bunyan Thin ruby layer around a MongoDB capped collection
    • 117. require 'bunyan' # put in config/initializers for rails Bunyan::Logger.configure do |c| c.database 'my_bunyan_db' c.collection 'development_log' # == 100.megabytes if using rails c.size 104857600 end
    • 118. class BunyanMiddleware def initialize(app) @app = app end def call(env) @status, @headers, @response = @app.call(env) Bunyan::Logger.insert(prepare_extra_fields) [@status, @headers, @response] end end
    • 119. Bunyan::Logger.find('user.email' => 'ajsharp@gmail.com')
    • 120. Stay tuned for a Sinatra-based client front-end.
    • 121. Mongolytics Drop-in to a rails app http://github.com/tpitale/mongolytics.git
    • 122. Blogging Application
    • 123. Blogging Application Much easier to model with Mongo than a relational database
    • 124. Blogging Application A post has an author
    • 125. Blogging Application A post has an author A post has many tags
    • 126. Blogging Application A post has an author A post has many tags A post has many comments
    • 127. Blogging Application A post has an author A post has many tags A post has many comments Instead of JOINing separate tables, we can use embedded documents.
    • 128. require 'mongo' conn = Mongo::Connection.new.db('bloggery') posts = conn.collection('posts') authors = conn.collection('authors')
    • 129. # returns a Mongo::ObjectID object alex = authors.save :name => "Alex" post = posts.save( :title => 'Post title', :body => 'Massive pontification...', :tags => ['mongosf', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => 'loud@mouth.edu', :body => "Something really ranty..." } ], :author_id => alex )
    • 130. # returns a Mongo::ObjectID object alex = authors.save :name => "Alex" post = posts.save( :title => 'Post title', :body => 'Massive pontification...', :tags => ['mongosf', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => 'loud@mouth.edu', :body => "Something really ranty..." } ], Joins not necessary. Sweet. :author_id => alex )
    • 131. MongoMapper
    • 132. MongoMapper • MongoDB “ORM” developed by John Nunemaker
    • 133. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty
    • 134. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty • Very similar syntax to DataMapper
    • 135. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty • Very similar syntax to DataMapper • Declarative rather than inheritance-based
    • 136. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty • Very similar syntax to DataMapper • Declarative rather than inheritance-based • Very easy to drop into rails
    • 137. MongoMapper class Post include MongoMapper::Document belongs_to :author, :class_name => "User" key :title, String, :required => true key :body, String key :author_id, Integer, :required => true key :published_at, Time key :published, Boolean, :default => false timestamps! many :tags end class Tag include MongoMapper::EmbeddedDocument key :name, String, :required => true end
    • 138. Storing binary data w/ GridFS and Joint
    • 139. Joint 1.MongoMapper plugin for accessing GridFS 2.Built on top of the Ruby driver GridFS API
    • 140. class Bio include MongoMapper::Document plugin Joint key :name, String, :required => true key :title, String key :description, String timestamps! attachment :headshot attachment :video_bio end
    • 141. class Bio include MongoMapper::Document plugin Joint key :name, String, :required => true key :title, String key :description, String timestamps! Can be served directly attachment :headshot from Mongo attachment :video_bio end
    • 142. Bing.
    • 143. Bang.
    • 144. Boom.
    • 145. Using mongo w/ Ruby Ruby mongo driver MongoMapper MongoID MongoRecord (http://github.com/mongodb/mongo- record) MongoDoc (http://github.com/leshill/mongodoc) Many other ORM/ODM’s under active development
    • 146. Questions?
    • 147. Thanks!

    ×