Practical Ruby Projects With Mongo Db

9,810 views

Published on

Given at LaRuby meetup on 4/8/2010

Published in: Technology
6 Comments
21 Likes
Statistics
Notes
No Downloads
Views
Total views
9,810
On SlideShare
0
From Embeds
0
Number of Embeds
739
Actions
Shares
0
Downloads
192
Comments
6
Likes
21
Embeds 0
No embeds

No notes for slide

























































































  • Practical Ruby Projects With Mongo Db

    1. 1. Practical Ruby Projects with
    2. 2. Who am I? Alex Sharp Amphibious Code Creature at OptimisDev @ajsharp alexjsharp.tumblr.com
    3. 3. We’re going to talk about two things.
    4. 4. 1. Why/how MongoDB is practical
    5. 5. 2. A few examples of how to use MongoDB with Ruby
    6. 6. So what’s all this “practical” talk?
    7. 7. Imagine if databases were cars...
    8. 8. Any takers for MySQL?
    9. 9. RDBMS (i.e. mysql) =~
    10. 10. In other words...
    11. 11. MySQL is very reliable.
    12. 12. But it may not be a speed demon.
    13. 13. Guesses for MongoDB?
    14. 14. MongoDB =~
    15. 15. Mongo is reliable too, but they’re also go pretty damn fast.
    16. 16. The Practicality of MongoDB
    17. 17. Simplifying schema design Objects weren’t made for SQL schemas
    18. 18. Simplifying schema design Objects weren’t meant to be “mapped” to a SQL schema
    19. 19. Simplifying schema design We’re forced to create “relationships” when what we really want is properties for our objects.
    20. 20. Simplifying schema design Non-relational databases are better suited for objects
    21. 21. Simplifying schema design Maps/hashes/associative arrays are arguably among the best object serialization formats
    22. 22. @alex = Person.new( :name => "alex", :friends => [Friend.new("Jim"), Friend.new("Bob")] )
    23. 23. Native ruby object <Person:0x10017d030 @name="alex", @friends= [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]>
    24. 24. JSON Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
    25. 25. SQL Schema Representation # in a SQL schema people: - name friends: - name - friend_id
    26. 26. Ruby -> JSON -> SQL <Person:0x10017d030 @name="alex", @friends= Ruby [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]> @alex.to_json JSON { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } people: - name SQL friends: - name - friend_id
    27. 27. Ruby -> JSON -> SQL <Person:0x10017d030 @name="alex", @friends= Ruby [#<Friend:0x10017d0a8 @name="Jim">, #<Friend:0x10017d058 @name="Bob"> ]> @alex.to_json JSON { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } people: - name Feels like we’re having SQL to work too hard here friends: - name - friend_id
    28. 28. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] }
    29. 29. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } In Mongo, friends is an “embedded document”
    30. 30. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } Great for one-to-many associations
    31. 31. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } No JOINS required.
    32. 32. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } This is a good thing for scalability, because JOINS limit our ability to horizontally scale.
    33. 33. Mongo Representation @alex.to_json { name: "alex", friends: [{ name: "Jim" }, { name: "Bob" }] } But more importantly, this seems more intuitive than messing with foreign keys
    34. 34. Ok, so what? You’re probably thinking... “Listen GUY, SQL isn’t that bad”
    35. 35. Ok, so what? True.
    36. 36. Ok, so what? SQL is actually really, really good at what it was designed to do.
    37. 37. Ok, so what? But SQL schemas are designed for storing and querying data, not necessarily modeling objects.
    38. 38. Ok, so what? It is called Structured Query Language.
    39. 39. Ok, so what? And to talk to these schemas in software, we use ORMs.
    40. 40. Ok, so what? ORM stands for Object Relational Mapper
    41. 41. Ok, so what? We need ORMs to bridge the gap between SQL and native objects (in our case, Ruby objects)
    42. 42. Ok, so what? I don’t want to map my objects to a schema designed for querying data.
    43. 43. Ok, so what? I want to store my objects in a datastore that was designed for storing objects
    44. 44. Ok, so what? Mongo is great for this b/c it stores objects (documents) as binary JSON
    45. 45. Ok, so what? And as we’ve seen, JSON is great for representing native objects
    46. 46. Ok, so what? This is important because when I’m writing an application, I don’t want to accomodate my objects to my datastore.
    47. 47. Ok, so what? I want something flexible that stays out of my way, but doesn’t sacrifice performance.
    48. 48. In a Nutshell Schema design for humans, not machines i.e. document-oriented is awesome schema-less is for adults Pragmatic balance of performance and functionality Speed/performance of mongo is super awesome Scalability features are really powerful too
    49. 49. Practical Projects
    50. 50. Three Examples 1.Blogging Application 2.Accounting Application 3.Logging
    51. 51. Blogging Application
    52. 52. Blogging Application Much easier to model with Mongo than a relational database
    53. 53. Blogging Application A post has an author
    54. 54. Blogging Application A post has an author A post has many tags
    55. 55. Blogging Application A post has an author A post has many tags A post has many comments
    56. 56. 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.
    57. 57. require 'mongo' conn = Mongo::Connection.new.db('bloggery') posts = conn.collection('posts') authors = conn.collection('authors')
    58. 58. # returns a Mongo::ObjectID object alex = authors.save :name => "Alex" post = posts.save( :title => 'Post title', :body => 'Massive potification...', :tags => ['laruby', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => 'loud@mouth.edu', :body => "Something really ranty..." } ], :author_id => alex )
    59. 59. # returns a Mongo::ObjectID object alex = authors.save :name => "Alex" post = posts.save( :title => 'Post title', :body => 'Massive potification...', :tags => ['laruby', 'omg', 'lolcats'], :comments => [ { :name => "Loudmouth McGee", :email => 'loud@mouth.edu', :body => "Something really ranty..." } ], Joins not necessary. Sweet. :author_id => alex )
    60. 60. Logging with Capped Collections
    61. 61. 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
    62. 62. This is awesome.
    63. 63. Now we have logs we can query (and analyze)
    64. 64. Can also be used for troubleshooting when things go wrong
    65. 65. Bunyan Thin ruby layer around a Mongo capped collection
    66. 66. require 'bunyan' Bunyan::Logger.configure do |c| c.database 'my_bunyan_db' c.collection 'development_log' # == 100.megabytes if using rails c.size 104857600 end Bunyan::Logger.save( :request_method => 'get', :status_code => 200 )
    67. 67. A simple accounting application (maybe)
    68. 68. The object model ledger * transactions * entries
    69. 69. 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 }
    70. 70. Object model summary Each ledger transaction belongs to a ledger. Each ledger transaction has two ledger entries which must balance.
    71. 71. 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]
    72. 72. 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 } ]
    73. 73. 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.
    74. 74. 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 transaction.
    75. 75. Using mongo w/ Ruby Ruby mongo driver MongoMapper MongoID Many other ORM/ODM’s under active development
    76. 76. MongoMapper
    77. 77. MongoMapper • MongoDB “ORM” developed by John Nunemaker
    78. 78. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty
    79. 79. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty • Very similar syntax to DataMapper
    80. 80. MongoMapper • MongoDB “ORM” developed by John Nunemaker • author of HttpParty • Very similar syntax to DataMapper • Declarative rather than inheritance-based
    81. 81. 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
    82. 82. 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
    83. 83. Questions?
    84. 84. Thanks!

    ×