Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

OSCON 2012 MongoDB Tutorial

67,910 views

Published 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, 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.

Published in: Technology

OSCON 2012 MongoDB Tutorial

  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

×