OSCON 2012 MongoDB Tutorial

  • 45,138 views
Uploaded on

This tutorial will introduce the features of MongoDB by building a simple location-based application using MongoDB. The tutorial will cover the basics of MongoDB’s document model, query language, …

This tutorial will introduce the features of MongoDB by building a simple location-based application using MongoDB. The tutorial will cover the basics of MongoDB’s document model, query language, map-reduce framework and deployment architecture.

The tutorial will be divided into 5 sections:

Data modeling with MongoDB: documents, collections and databases
Querying your data: simple queries, geospatial queries, and text-searching
Writes and updates: using MongoDB’s atomic update modifiers
Trending and analytics: Using mapreduce and MongoDB’s aggregation framework
Deploying the sample application
Besides the knowledge to start building their own applications with MongoDB, attendees will finish the session with a working application they use to check into locations around Portland from any HTML5 enabled phone!

TUTORIAL PREREQUISITES
Each attendee should have a running version of MongoDB. Preferably the latest unstable release 2.1.x, but any install after 2.0 should be fine. You can dowload MongoDB at http://www.mongodb.org/downloads.

Instructions for installing MongoDB are at http://docs.mongodb.org/manual/installation/.

Additionally we will be building an app in Ruby. Ruby 1.9.3+ is required for this. The current latest version of ruby is 1.9.3-p194.

For windows download the http://rubyinstaller.org/
For OSX download http://unfiniti.com/software/mac/jewelrybox/
For linux most users should know how to for their own distributions.
We will be using the following GEMs and they MUST BE installed ahead of time so you can be ahead of the game and safe in the event that the Internet isn’t accommodating.

bson (1.6.4)
bson_ext (1.6.4)
haml (3.1.4)
mongo (1.6.4)
rack (1.4.1)
rack-protection (1.2.0)
rack shotgun (0.9)
sinatra (1.3.2)
tilt (1.3.3)
Prior ruby experience isn’t required for this. We will NOT be using rails for this app.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
45,138
On Slideshare
0
From Embeds
0
Number of Embeds
13

Actions

Shares
Downloads
820
Comments
7
Likes
45

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
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • Intel's improved microprocessor chip is introduced April 1, 1974, the 8080 becomes a standard in the computer industry.\n\nThis predates Microsoft, Apple \n
  • Remember in 1995 there were around 10,000 websites. Mosiac, Lynx, Mozilla (pre netscape) and IE 2.0 were the only web browsers. \nApache (Dec ’95), Java (’96), PHP (June ’95), and .net didn’t exist yet. Linux just barely (1.0 in ’94)\n
  • Remember in 1995 there were around 10,000 websites. Mosiac, Lynx, Mozilla (pre netscape) and IE 2.0 were the only web browsers. \nApache (Dec ’95), Java (’96), PHP (June ’95), and .net didn’t exist yet. Linux just barely (1.0 in ’94)\n
  • Remember in 1995 there were around 10,000 websites. Mosiac, Lynx, Mozilla (pre netscape) and IE 2.0 were the only web browsers. \nApache (Dec ’95), Java (’96), PHP (June ’95), and .net didn’t exist yet. Linux just barely (1.0 in ’94)\n
  • \n
  • \n
  • Galaxy III released in May\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • By reducing transactional semantics the db provides, one can still solve an interesting set of problems where performance is very important, and horizontal scaling then becomes easier.\n\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • After this I’ll jump to my terminal and demo a few things.\n
  • wget http://c.spf13.com/OSCON/venuesImport.json\nmongoimport -d milieu -c venues venuesImport.json\n\n
  • After this I’ll jump to my terminal and demo a few things.\n
  • After this I’ll jump to my terminal and demo a few things.\n
  • After this I’ll jump to my terminal and demo a few things.\n
  • After this I’ll jump to my terminal and demo a few things.\n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n
  • \n

Transcript

  • 1. MongoDB
  • 2. Today’s AgendaMongoDB IntroMongoDB FundamentalsRunning MongoDBBuilding your first appDeploying your first app to Heroku
  • 3. @spf13 AKASteve Francia15+ years buildingthe internet Father, husband, skateboarderChief Solutions Architect @responsible for drivers,integrations, web & docs
  • 4. Company behind MongoDBHQ in NYC & Palo Alto,Offices in Sydney, London & DublinSupport, consulting, training
  • 5. Introduction toMongoD
  • 6. A bit ofhistory
  • 7. 1974The relational database is created
  • 8. 1979
  • 9. 1979 1994
  • 10. 1979 1994 1995
  • 11. Computers in 1995100 mhz Pentium10 base T16 MB ram200 MB HD
  • 12. Cloud in 1995(Windows 95 cloud wallpaper)
  • 13. Cell Phones inQuad core 1.4Ghz802.11n (300+ Mbps)2 GB ram64 GB Solid State
  • 14. The world is vastlydifferent than theone the relational database was created for
  • 15. What do we want in an ideal world?
  • 16. What do we want in an ideal world?
  • 17. What do we want in an ideal world?•Horizontal scaling •cloud compatible •works with standard servers
  • 18. What do we want in an ideal world?•Horizontal scaling •cloud compatible •works with standard servers•Fast
  • 19. What do we want in an ideal world?•Horizontal scaling •cloud compatible •works with standard servers•Fast•Easy Development •Features •The Right Data Model •Schema Agility
  • 20. MongoDB philosophy Keep functionality when we can (key/value stores are great, but we need more) Non-relational (no joins) makes scaling horizontally practical Document data models are good Database technology should run anywhere virtualized, cloud, metal, etc
  • 21. Under the hoodWritten in C++Runs nearly everywhereData serialized to BSONExtensive use of memory-mapped filesi.e. read-through write-throughmemory caching.
  • 22. DatabaseScalability & Performance Memcached MongoDB RDBMS Depth of Functionality
  • 23. “MongoDB has the bestfeatures of key/valuestores, documentdatabases andrelational databasesin one. John Nunemaker
  • 24. Relational made normalized data look like this Category • Name • Url Article User • Name Tag • Name • Slug • Name • Email Address • Publish date • Url • Text Comment • Comment • Date • Author
  • 25. Document databases makenormalized data look like this Article • Name • Slug • Publish date User • Text • Name • Author • Email Address Comment[] • Comment • Date • Author Tag[] • Value Category[] • Value
  • 26. But we’ve been usinga relational database for 40 years!
  • 27. How do people storedocuments in real life?
  • 28. Think about adoctors office There’s two ways theycould organize their files
  • 29. Each document type in it’s own drawerMRIs X-rays Lab Invoices Index 1 1 1 1 1 1 1 1 History Medications Lab Forms
  • 30. Each document type in it’s own drawerMRIs X-rays Lab Invoices Index 1 1 1 1 1 1 1 1 History Medications Lab Forms
  • 31. Each document type in it’s own drawerMRIs X-rays Lab Invoices Index 1 1 1 1 1 1 1 1 History Medications Lab Forms
  • 32. Group related records Patient 1 Patient 2 Patient 3 ... Vendor 1 Vendor 2 Vendor 3
  • 33. Group related records Patient 1 Patient 3 ... Patient 2 Vendor 1 Vendor 2 Vendor 3
  • 34. Databases work the same way Relation Docum Patient 1 Vendor 1 Article Category • Name • Name • Slug • Url • Publish User date • Text • Name • Author • Email Address Article User Tag • Name Comment[]• Name • Name• Email • Slug • Url • Comment Address • Publish date • Date • Author Comment Tag[] • Comment • Value • Date • Author Category[] • Value
  • 35. Terminology RDBMS MongoTable, View ➜ CollectionRow ➜ DocumentIndex ➜ IndexJoin ➜ EmbeddedForeign Key ➜ Document ReferencePartition ➜ Shard
  • 36. MongoDBUse Cases
  • 37. CMS / BlogNeeds:• Business needed modern data store for rapid development and scaleSolution:• Use PHP & MongoDBResults:• Real time statistics• All data, images, etc stored together easy access, easy deployment, easy high availability• No need for complex migrations• Enabled very rapid development and growth
  • 38. Photo Meta-Problem:• Business needed more flexibility than Oracle could deliverSolution:• Use MongoDB instead of OracleResults:• Developed application in one sprint cycle• 500% cost reduction compared to Oracle• 900% performance improvement compared to Oracle
  • 39. Customer AnalyticsProblem:• Deal with massive data volume across all customer sitesSolution:• Use MongoDB to replace Google Analytics / Omniture optionsResults:• Less than one week to build prototype and prove business case• Rapid deployment of new features
  • 40. ArchivingWhy MongoDB:• Existing application built on MySQL• Lots of friction with RDBMS based archive storage• Needed more scalable archive storage backendSolution:• Keep MySQL for active data (100mil)• MongoDB for archive (2+ billion)Results:• No more alter table statements taking over 2 months to run• Sharding fixed vertical scale problem• Very happily looking at other places to use MongoDB
  • 41. OnlineProblem:• MySQL could not scale to handle their 5B+ documentsSolution:• Switched from MySQL to MongoDBResults:• Massive simplification of code base• Eliminated need for external caching system• 20x performance improvement over MySQL
  • 42. E-commerceProblem:• Multi-vertical E-commerce impossible to model (efficiently) in RDBMSSolution:• Switched from MySQL to MongoDBResults:• Massive simplification of code base• Rapidly build, halving time to market (and cost)• Eliminated need for external caching system• 50x+ performance improvement over MySQL
  • 43. Tons more MongoDB casts a wide net people keep coming up with new and brilliant ways to use it
  • 44. In Good and 1000s more
  • 45. MongoD B
  • 46. Start with an (or array, hash, dict, eplace1 = { name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", tags : [ "business", "awesome" ]}
  • 47. Inserting the record Initial Data Load > db.places.insert(place1)> db.places.insert(place1)
  • 48. Querying> db.places.findOne({ zip: "10011", tags: "awesome" })> db.places.find({tags: "business" }){ name : "10gen HQ", address : "134 5th Avenue 3rd Floor", city : "New York", zip : "10011", tags : [ "business", "awesome" ]}
  • 49. Nested Documents{ _id : ObjectId("4c4ba5c0672c685e5e8aabf3"), name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", tags : [ "business", "awesome" ], comments : [ { author : "Fred", date : "Sat Apr 25 2010 20:51:03", text : "Best Place Ever!" }]}
  • 50. Object ID> db.places.insert(place1)object(MongoId)#4 (1) { ["$id"]=> string(24) "4e9cc76a4a1817fd21000000"} 4e9cc76a4a1817fd21000000 |------||----||--||----| ts mac pid inc
  • 51. Updating> db.places.update( {name : "10gen HQ"}, { $push : { comments : { author : "steve", date : 6/26/2012, text : "Office hours are great!" } } })
  • 52. Atomic$set $unset $rename $push $pop $pull $addToSet $in
  • 53. Index nested documents// Index nested documents> db.posts.ensureIndex({ "comments.author":1 })> db.posts.find({comments.author:Fred}){ _id : ObjectId("4c4ba5c0672c685e5e8aabf3"), name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", comments : [ { author : "Fred", date : "Sat Apr 25 2010 20:51:03", text : "Best Place Ever!" }]}
  • 54. Regular Expressions// Regular Expressions> db.posts.find({comments.author: /^Fr/}){ _id : ObjectId("4c4ba5c0672c685e5e8aabf3"), name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", comments : [ { author : "Fred", date : "Sat Apr 25 2010 20:51:03", text : "Best Place Ever!" }]}
  • 55. Index multiple values// Index on tags (multi-key index)> db.posts.ensureIndex({ tags: 1})> db.posts.find( { tags: tech } ){ _id : ObjectId("4c4ba5c0672c685e5e8aabf3"), name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", tags : [ "business", "awesome", "tech" ],}
  • 56. Geospatial// geospatial index> db.posts.ensureIndex({ "location": "2d" })> db.posts.find({"location":{$near:[22,42]}}){ _id : ObjectId("4c4ba5c0672c685e5e8aabf3"), name : "10gen HQ", address : "578 Broadway 7th Floor", city : "New York", zip : "10011", location : [ 22, 42 ],}
  • 57. Cursors$cursor = $c->find(array("foo" => "bar"));foreach ($cursor as $id => $value) { echo "$id: "; var_dump( $value );}$a = iterator_to_array($cursor);
  • 58. Pagingpage_num = 3;results_per_page = 10;cursor = db.collection.find() .sort({ "ts" : -1 }) .skip((page_num - 1) * results_per_page) .limit(results_per_page);
  • 59. RunningMongoDB
  • 60. Fire up a mongomongo mongod mongoexportmongoimport mongos mongostatmongo_console mongodump mongofilesmongorestore mongosniff mongotop
  • 61. Start Mongod mongod Note the dmongod --help for help and startup optionsThu Jul 12 23:19:49 [initandlisten] MongoDB starting : pid=32237 port=27017 dbpath=/data/db/ 64-bit host=Steves-MacBook-Air.localThu Jul 12 23:19:49 [initandlisten] db version v2.0.6, pdfile version 4.5Thu Jul 12 23:19:49 [initandlisten] git version:e1c0cbc25863f6356aa4e31375add7bb49fb05bcThu Jul 12 23:19:49 [initandlisten] build info: Darwin erh2.10gen.cc 9.8.0 Darwin KernelVersion 9.8.0: Wed Jul 15 16:55:01 PDT 2009; root:xnu-1228.15.4~1/RELEASE_I386 i386BOOST_LIB_VERSION=1_40Thu Jul 12 23:19:49 [initandlisten] options: {}Thu Jul 12 23:19:49 [initandlisten] journal dir=/data/db/journalThu Jul 12 23:19:49 [initandlisten] recover : no journal files present, no recovery neededThu Jul 12 23:19:49 [websvr] admin web console waiting for connections on port 28017Thu Jul 12 23:19:49 [initandlisten] waiting for connections on port 27017
  • 62. Download & Import the venues curl -L http://j.mp/OSCONvenues | mongoimport -d milieu -c venues Database Collectionwget http://c.spf13.com/OSCON/venuesImport.jsonmongoimport -d milieu -c venues venuesImport.json
  • 63. Start the Mongo shell mongo> help Note no ddb.help() help on db methodsdb.mycoll.help() help on collection methodsrs.help() help on replica set methodshelp admin administrative helphelp connect connecting to a db helphelp keys key shortcutshelp misc misc things to knowhelp mr mapreduceshow dbs show database namesshow collections show collections in current databaseshow users show users in current databaseshow profile show most recent system.profile
  • 64. Let’s look at the venues> use milieu Databaseswitched to db milieu> db.venues.count()50
  • 65. Let’s look at the venues> db.venues.findOne(){ "_id" : ObjectId("4ff73cb568840767e50d4e9c"), "location" : { "address" : "777 NE ML King Blvd.", "cc" : "US", "city" : "Portland", "country" : "United States", "distance" : 18, "geo" : [ -122.66293287277222, 45.52829837051139 ], "postalCode" : "97232", "state" : "OR" },"name" : "Agent Reboot Portland","stats" : { "checkinsCount" : 0, "usersCount" : 0 }}
  • 66. Creating a Geo index> db.venues.ensureIndex({ location.geo : 2d})> db.venues.getIndexes()[ { "v" : 1, "key" : { "_id" : 1 }, "ns" : "milieu.venues", "name" : "_id_" }, { "v" : 1, "key" : { "location.geo" : "2d" }, "ns" : "milieu.venues", "name" : "location.geo_" }]
  • 67. ChallengesCreate a new DB & CollectionInsert a new record into this collection ... with a nested fieldInsert another recordCreate an index (on a common field)Query on the field and confirm index usedUpdate multiple records
  • 68. Building aMongoDB app
  • 69. MongoDB DriversOfficial Support for 12 languagesCommunity drivers for tons moreDrivers connect to mongo serversDrivers translate BSON into native typesmongo shell is not a driver, but works likeone in some ways
  • 70. Building an app in Ruby?Had to pick a languageSinatra is very minimal and approachableWanted to focus on MongoDB interactionRuby gems are awesomeWorks well on Windows, OS X & LinuxSeemed like a good idea at the time
  • 71. A crashcourse inSinatra & Ruby
  • 72. 1st Ruby
  • 73. Everything is an object1.class # => Fixnuma.class # => String:z.class # => Symbolclass Foo # => ClassendFoo.class # => FooFoo.new.class
  • 74. Structuredef do_stuff(thing) Method thing.do_the_stuffendclass TheThing Class def do_the_stuff puts "Stuff was done!" endenddo_stuff(TheThing.new) Invocation
  • 75. Stringsname = World # => "World""Hello, #{name}" # => "Hello, World"Hello, #{name} # => "Hello, #{name}"
  • 76. Numbers1 + 1 # => 21 + 1.1 # => 2.16 * 7 # => 426 ** 7 # => 279936Math.sqrt(65536) # => 256.01.class # => Fixnum(2 ** 42).class # => Fixnum(2 ** 64).class # => Bignum1.1.class # => Float
  • 77. ArraysArray.new # => []Array.new(3) # => [nil, nil, nil][] # => []a = [1,2,3] # => [1, 2, 3]a[0] = one # => "one"a # => ["one", 2, 3]a[-1] # => 3a[1..2] # => [2, 3]
  • 78. HashesHash.new # => {}{} # => {}h = {1 => "one", 2 => "two"}h[1] # => "one"h["1"] # => nilh[:one] = "einz" # => "einz"h[:one] # => "einz"h.keys # => [1, 2, :one]h.values # => ["one", "two", "einz"]
  • 79. Variables & NamesCamelCased # Classes, moduleswith_underscores # methods, local variables@instance_variable@@class_variable$GLOBAL_VARIABLE
  • 80. Control Structures if condition # ... elsif other_condition # ... end unless condition # ... end while # ... end
  • 81. Sinatra is...not Railsnot a frameworka DSL for quickly creatingweb applications in Rubywith minimal effort
  • 82. Hello World # myapp.rbrequire sinatraget / do Hello world!end
  • 83. HTTP Actions In Sinatra, a route is an HTTP method paired with a URL-matching pattern.Each route is associated with a block:get / do .. show something ..endpost / do .. create something ..endput / do .. replace something ..enddelete / do .. annihilate something ..end
  • 84. Routes Routes are matched in the order they are defined. The first route that matches the request is invoked.Route patterns may include named parameters,accessible via the params hash:get /hello/:name do # matches "GET /hello/foo" and "GET /hello/bar" # params[:name] is foo or bar "Hello #{params[:name]}!"end#You can also access named parameters via block parameters:get /hello/:name do |n| "Hello #{n}!"end
  • 85. Splat Route patterns may also include splat (or wildcard) parameters, accessible via the params[:splat] array:get /say/*/to/* do # matches /say/hello/to/world params[:splat] # => ["hello", "world"]endget /download/*.* do # matches /download/path/to/file.xml params[:splat] # => ["path/to/file", "xml"]end
  • 86. Introducing the milieu app
  • 87. Start with a skeleton /Users/steve/Code/milieu/app/ layout.haml*▸ config/ login.haml*▸ helpers/ navbar.haml*▾ model/ register.haml* mongodb.rb user_dashboard.haml* mongoModule.rb user_profile.haml* user.rb venue.haml*▾ public/ venues.haml* ▸ bootstrap/ app.rb* ▾ css/ config.ru* styles.css* Gemfile* ▸ images/ Gemfile.lock*▾ views/ Rakefile* footer.haml* README* index.haml*
  • 88. Download & Install deps mkdir milieu cd milieu wget http://c.spf13.com/OSCON/gettingStarted.tgz tar zxvf gettingStarted.tgz cd app bundle installUsing bson (1.6.4)Using bson_ext (1.6.4)Using googlestaticmap (1.1.3)Using haml (3.1.4)Using mongo (1.6.4)Using rack (1.4.1)Using rack-protection (1.2.0)Using shotgun (0.9)Using tilt (1.3.3)Using sinatra (1.3.2)Using bundler (1.1.4)Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
  • 89. Run app shotgun== Shotgun/WEBrick on http://127.0.0.1:9393/[2012-07-14 11:37:34] INFO WEBrick 1.3.1[2012-07-14 11:37:34] INFO ruby 1.9.3 (2012-04-20) [x86_64-darwin11.4.0][2012-07-14 11:37:34] INFO WEBrick::HTTPServer#start: pid=42185port=9393
  • 90. Open Browser to localhost:9393
  • 91. Connecting to MongoDB model/mongodb.rbrequire mongorequire ./model/mongoModulerequire ./model/userCONNECTION = Mongo::Connection.new("localhost")DB = CONNECTION.db(milieu)USERS = DB[users]VENUES = DB[venues]CHECKINS = DB[checkins]
  • 92. Listing Venues app.rbget /venues do @venues = VENUES.find haml :venuesend
  • 93. Listing Venues.container .content views/venues.haml %h2 Venues %table.table.table-striped %thead %tr %th Name %th Address %th Longitude %th Latitude %tbody ~@venues.each do |venue| %tr %td %a{:href => /venue/ << venue[_id].to_s}= venue[name] %td= venue[location][address] ? venue[location][address] : &nbsp %td= venue[location][geo][0].round(2) %td= venue[location][geo][1].round(2)
  • 94. Listing Venues localhost:9393/venues
  • 95. Paginating Venues app.rbget /venues/?:page? do @page = params.fetch(page, 1).to_i pp = 10 @venues = VENUES.find.skip( ( @page - 1 ) * pp).limit(pp) @total_pages = (VENUES.count.to_i / pp).ceil haml :venuesend
  • 96. Listing Venues localhost:9393/venues
  • 97. Creating UsersUsers are a bit special in our appNot just dataSpecial considerations for securepassword handlingNot complicated on MongoDB side, butslightly complicated on Ruby side
  • 98. Creating Usersclass User model/user.rbattr_accessor :_id, :name, :email, :email_hash, :salt, :hashed_password, :collection,:updated_at def initialize self.collection = users end def password=(pass) self.salt = random_string(10) unless self.salt self.hashed_password = User.encrypt(pass, self.salt) end def save col = DB[self.collection] self.updated_at = Time.now col.save(self.to_hash) endend
  • 99. Creating Userspost /register do app.rb u = User.new u.email = params[:email] u.password = params[:password] u.name = params[:name] if u.save() flash("User created") session[:user] = User.auth( params["email"], params["password"]) redirect /user/ << session[:user].email.to_s << "/dashboard" else tmp = [] u.errors.each do |e| tmp << (e.join("<br/>")) end flash(tmp) redirect /create endend
  • 100. Logging inconfigure do app.rb enable :sessionsendbefore do unless session[:user] == nil @suser = session[:user] endendget /user/:email/dashboard do haml :user_dashboardend
  • 101. User Dashboard.container views/user_dashboard.haml .content .page-header -unless @suser == nil? %h2="Dashboard" %br %image{src: "http://www.gravatar.com/avatar/" <<@suser.email_hash.to_s << .png} %h3= @suser.name.to_s -else redirect / %small %a{href: "/user/" << @suser.email.to_s << "/profile"} profile .container#main-topic-nav
  • 102. Dashboard localhost:9393/dashboard
  • 103. Showing a Venue app.rbget /venue/:_id do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one( { :_id => object_id }) haml :venueend
  • 104. Showing a Venue .row-fluid views/venue.haml .span4 %h2= @venue[name].to_s %p =@venue[location][address].to_s %br= @venue[location][city].to_s + + @venue[location][state].to_s + + @venue[location][postalCode].to_s .span8 %image{:src => << gmap_url(@venue, {:height =>300, :width => 450}) }
  • 105. A Venue localhost:9393/venue/{id}
  • 106. Nearby Venues app.rbget /venue/:_id do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) @nearby_venues = VENUES.find( { :location.geo => { :$near => [ @venue[location][geo][0], @venue[location][geo][1]] } }).limit(4).skip(1) haml :venueend
  • 107. Listing Nearby Venues... views/venue.haml .row-fluid - @nearby_venues.each do |nearby| .span3 %h2 %a{:href => /venue/ + nearby[_id].to_s}=nearby[name].to_s %p =nearby[location][address].to_s %br= nearby[location][city].to_s + + nearby[location][state].to_s + + nearby[location][postalCode].to_s %a{:href => /venue/ + nearby[_id].to_s} %image{:src => << gmap_url(nearby, {:height =>150, :width => 150, :zoom => 17}) }
  • 108. Nearby Venues localhost:9393/venue/{id}
  • 109. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 110. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 111. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 112. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 113. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 114. Checking in app.rbget /venue/:_id/checkin do object_id = BSON::ObjectId.from_string(params[:_id]) @venue = VENUES.find_one({ :_id => object_id }) user = USERS.find_and_modify(:query => { :_id => @suser._id}, :update => {:$inc => { "venues." << object_id.to_s => 1 } }, :new => 1) if user[venues][params[:_id]] == 1 VENUES.update({ :_id => @venue[_id]}, { :$inc => { :stats.usersCount => 1, :stats.checkinsCount => 1}}) else VENUES.update({ _id: @venue[_id]}, { :$inc => { :stats.checkinsCount => 1}}) end flash(Thanks for checking in) redirect /venue/ + params[:_id]end
  • 115. Checkin In views/venue.haml%p %a.btn.btn-primary.btn-large{:href => /venue/ + @venue[_id].to_s + /checkin} Check In Here%p =@venue[stats][usersCount].ceil.to_s + users have checked in here + @venue[stats][checkinsCount].ceil.to_s + times%p=user_times_at
  • 116. You’ve been here def user_times_at helpers/milieu.rb if logged_in? times = You have checked in here if !@user.venues.nil? && !@user.venues[params[:_id]].nil? times << @user.venues[params[:_id]].to_s else times << 0 end times << times else times = Please <a href=/login>login</a> to jointhem. end end
  • 117. A Venue localhost:9393/venue/{id}
  • 118. Deployingto Heroku
  • 119. Installing the Dependencies gem install herokuFetching: excon-0.14.3.gem (100%)Fetching: heroku-api-0.2.8.gem (100%)Fetching: netrc-0.7.5.gem (100%)Fetching: mime-types-1.19.gem (100%)Fetching: rest-client-1.6.7.gem (100%)Fetching: launchy-2.1.0.gem (100%)Fetching: rubyzip-0.9.9.gem (100%)Fetching: heroku-2.28.12.gem (100%)Successfully installed excon-0.14.3Successfully installed heroku-api-0.2.8Successfully installed netrc-0.7.5Successfully installed mime-types-1.19Successfully installed rest-client-1.6.7Successfully installed launchy-2.1.0Successfully installed rubyzip-0.9.9Successfully installed heroku-2.28.128 gems installed
  • 120. Signup for Heroku
  • 121. Create app on Heroku heroku create app_nameEnter your Heroku credentials.Email: Your_Email@Your_Domain.comPassword (typing will be hidden):Found the following SSH public keys:1) id_rsa.pubWhich would you like to use with your Heroku account? 1Uploading SSH public key /Users/steve/.ssh/id_rsa.pub... doneCreating milieu... done, stack is cedarhttp://milieu.herokuapp.com/ | git@heroku.com:milieu.gitGit remote heroku added
  • 122. Pushing to Heroku git push heroku masterWarning: Permanently added the RSA host key for IP address 50.19.85.132 to thelist of known hosts.Counting objects: 67, done.Delta compression using up to 4 threads.Compressing objects: 100% (65/65), done.Writing objects: 100% (67/67), 206.04 KiB, done.Total 67 (delta 9), reused 0 (delta 0)-----> Heroku receiving push-----> Removing .DS_Store files-----> Ruby/Rack app detected-----> Installing dependencies using Bundler version 1.2.0.pre Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment Fetching gem metadata from http://rubygems.org/....... Installing bson (1.6.4) Installing bson_ext (1.6.4) with native extensions Installing googlestaticmap (1.1.3) Installing haml (3.1.4)
  • 123. Fetching gem metadata from http://rubygems.org/.......Pushing to Heroku Installing bson (1.6.4) Installing bson_ext (1.6.4) with native extensions Installing googlestaticmap (1.1.3) Installing haml (3.1.4) Installing mongo (1.6.4) Installing rack (1.4.1) Installing rack-protection (1.2.0) Installing shotgun (0.9) Installing tilt (1.3.3) Installing sinatra (1.3.2) Using bundler (1.2.0.pre) Your bundle is complete! It was installed into ./vendor/bundle Cleaning up the bundler cache.-----> Writing config/database.yml to read from DATABASE_URL-----> Discovering process types Procfile declares types -> (none) Default types for Ruby/Rack -> console, rake, web-----> Compiled slug size is 1.4MB-----> Launching... done, v3 http://milieu.herokuapp.com deployed to HerokuTo git@heroku.com:milieu.git * [new branch] master -> master
  • 124. Adding MongoHQ/Lab heroku addons:add mongohqAdding mongohq to milieu... done, v4 (free)Use `heroku addons:docs mongohq` to view documentation
  • 125. Adjusting Connection model/mongodb.rbif ENV[RACK_ENV] == production db = URI.parse(ENV[MONGOHQ_URL]) db_name = db.path.gsub(/^//, ) DB = Mongo::Connection.new( db.host, db.port).db(db_name) DB.authenticate(db.user, db.password) unless (db.user.nil? || db.user.nil?)else DB = Mongo::Connection.new( "localhost", 27017).db(milieu)end
  • 126. Config MongoHQ/Lab heroku config=== Config Vars for milieuGEM_PATH: vendor/bundle/ruby/1.9.1LANG: en_US.UTF-8MONGOHQ_URL: mongodb://heroku:3vw...xv12@staff.mongohq.com:100XX/appXXPATH: bin:vendor/bundle/ruby/1.9.1/bin:/usr/local/bin:/usr/bin:/binRACK_ENV: production Password Port Username Server DB
  • 127. Connecting to the shellmongo -u USER -p PASSWORD SERVER:PORT/DB_NAME
  • 128. Importing the Venues mongoimport -u heroku -p PASSWORD -h staff.mongohq.com --port PORT -d DB_NAME -c venues venuesImport.json
  • 129. Debugging heroku logs2012-07-14T03:24:31+00:00 app[web.1]: /app/vendor/bundle/ruby/1.9.1/gems/mongo-1.6.4/lib/mongo/connection.rb:420:in `connect: Failed to connect to a master node at localhost:27017 (Mongo::ConnectionFailure)2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/mongo-1.6.4/lib/mongo/connection.rb:594:in `setup2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/mongo-1.6.4/lib/mongo/connection.rb:130:in `initialize2012-07-14T03:24:31+00:00 app[web.1]: from /app/model/mongodb.rb:5:in `new2012-07-14T03:24:31+00:00 app[web.1]: from /app/model/mongodb.rb:5:in `<top (required)>2012-07-14T03:24:31+00:00 app[web.1]: from /app/app.rb:4:in `<top (required)>2012-07-14T03:24:31+00:00 app[web.1]: from /app/config.ru:6:in `require2012-07-14T03:24:31+00:00 app[web.1]: from /app/app.rb:4:in `require2012-07-14T03:24:31+00:00 app[web.1]: from /app/config.ru:6:in `block in <main>2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:51:in `instance_eval2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:51:in `initialize2012-07-14T03:24:31+00:00 app[web.1]: from /app/config.ru:1:in `new2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:40:in `eval2012-07-14T03:24:31+00:00 app[web.1]: from /app/config.ru:1:in `<main>2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/builder.rb:40:in `parse_file2012-07-14T03:24:31+00:00 app[web.1]: from /app/vendor/bundle/ruby/1.9.1/gems/rack-1.4.1/lib/rack/server.rb:200:in `app
  • 130. Deploying new versions git push herokuCounting objects: 7, done.Delta compression using up to 4 threads.Compressing objects: 100% (4/4), done.Writing objects: 100% (4/4), 424 bytes, done.Total 4 (delta 2), reused 0 (delta 0)-----> Heroku receiving push-----> Removing .DS_Store files-----> Ruby/Rack app detected-----> Installing dependencies using Bundler version 1.2.0.pre Running: bundle install --without development:test --path vendor/bundle --binstubs bin/ --deployment Using bson (1.6.4) Using bson_ext (1.6.4) Using googlestaticmap (1.1.3) Using haml (3.1.4) Using mongo (1.6.4) Using rack (1.4.1) Using rack-protection (1.2.0)
  • 131. Using googlestaticmap (1.1.3)Deploying new versions Using haml (3.1.4) Using mongo (1.6.4) Using rack (1.4.1) Using rack-protection (1.2.0) Using shotgun (0.9) Using tilt (1.3.3) Using sinatra (1.3.2) Using bundler (1.2.0.pre) Your bundle is complete! It was installed into ./vendor/bundle Cleaning up the bundler cache.-----> Writing config/database.yml to read from DATABASE_URL-----> Discovering process types Procfile declares types -> (none) Default types for Ruby/Rack -> console, rake, web-----> Compiled slug size is 1.4MB-----> Launching... done, v6 http://milieu.herokuapp.com deployed to HerokuTo git@heroku.com:milieu.git 9d5d688..ad894be master -> master
  • 132. Checkout your deployed app
  • 133. Next ?
  • 134. It’s all on
  • 135. Programmin g Challenge
  • 136. Some Ideas‣Createdifferentuser levels ‣Timestamped(admin) Checkins‣Createinterface ‣Amore completeto add venues dashboard with stats‣Connecttofoursquare ‣Badgesor Categories‣Login w/twitter
  • 137. One More Thing...
  • 138. http://spf13.com http://github.com/s @spf13Question download at mongodb.orgWe’re hiring!! Contact us at jobs@10gen.com