Building Apps With


Nate Abele
IPC — 1 June 2010
Berlin
X
Me


Former lead developer, CakePHP
Lead developer, Lithium
Twitter: @nateabele
“Alternative” databases?
What’s the point?

 Relational databases » relational algebra » set theory
 ORM “impedance mismatch”
 Failed alternatives reinforced the status quo
What’s a document database?


Composed of documents ...duh
Each document is heterogenous, and may have a
completely unique structure compared to other
documents
...That’s pretty much it
Why Mongo?

                   .org :

“The best features of document databases,
key-value stores, and relational databases.”
Mongo is...


 Fast
 Smart
 Scalable
Terminology


Database   »   Database

Table      »   Collection

Row        »   Document
Common Tasks
       tags
  id
                        MySQL
  name


   posts_tags
  id                    posts    comments
  post_id       id              id
  tag_id        title           post_id
                slug            author
                body            email
                published       body
                created         created
                updated
Common Tasks       id
                           posts

                   title
                   slug
         MongoDB   body
                   published
                   created
                   updated

                           comments
                            author
                            email
                            body
                            created


                           tags
Common Tasks
MongoDB
{
    "_id" : ObjectId("4c03e856e258c2701930c091"),
    "title" : "Welcome to MongoDB",
    "slug" : "welcome-to-mongodb",
    "body" : "Today, we're gonna totally rock your world...",
    "published" : true,
    "created" : "Mon May 31 2010 12:48:22 GMT-0400 (EDT)",
    "updated" : "Mon May 31 2010 12:48:22 GMT-0400 (EDT)",
    "comments" : [
        {
            "author" : "Bob",
            "email" : "bob@example.com",
            "body" : "My mind has been totally blown!",
            "created" : "Mon May 31 2010 12:48:22 GMT-0400 (EDT)"
        }
    ],
    "tags" : [
        "databases", "MongoDB", "awesome"
    ]
}
[...frantic hand-wringing ensues...]
[...pause, deep breath...]
Doing the “normal db” stuff
How do I...

 ...do pattern-matching?
 db.posts.find({ "title" : /mongodb/i })


 ...find all posts tagged ‘MongoDB’?
 db.posts.find({ "tags" : "MongoDB" })


 ...find all Bob’s comments?
 db.posts.find({ "comments.email" : "bob@example.com" })
Doing the “normal db” stuff
How do I...



 ...change Bob’s email?
 db.posts.update(
     { "comments.email": "bob@example.com" },
     { $set : { "comments.$.email" : "robert@example.com" }}
 )
$set?
Querying
$gt     $all
$gte	   $size
$lt	    $exists
$lte	   $type
$ne	    $elemMatch
$in	    $not
$nin	   $where
$mod
Querying
dontTrust = db.people.find({
  “age”: { $gt : 30 }
})
awesome = db.posts.find({ “tags” : {
  $in : [‘MongoDB’, ‘awesome’]
}})
todo = db.tasks.find({
  “status” : { $nin : [
    ‘In Progress’, ‘Completed‘
  ]}
})
Querying
By arbitrary function:
db.posts.find({ $where: function() {
    return this.hits % 2 == 0;
}})

By grouping key:
db.posts.group({
    "key": { "hits": true },
    "initial": { count: 0 },
    "reduce": function(obj, prev) {
       prev.count++;
    }
});
Querying
By grouping function:
db.photos.group({
  keyf: function(o) {
     return {
       hits: Math.round(o.hits / 1000)
     };
  },
  "initial": { count: 0 },
  "reduce": function(obj, prev) {
     prev.count++;
  }
});
Grouping results
[
    { 'hits' => 0, 'count' => 5 },
    { 'hits' => 1, 'count' => 4 },
    { 'hits' => 2, 'count' => 7 },
    ...
]
Map/reduce


...with apologies to John Nunemaker.
Map/reduce
>   db.items.insert({tags:   ['dog', 'cat']})
>   db.items.insert({tags:   ['dog']})
>   db.items.insert({tags:   ['dog', 'mouse']})
>   db.items.insert({tags:   ['dog', 'mouse', 'hippo']})
>   db.items.insert({tags:   ['dog', 'mouse', 'hippo']})
>   db.items.insert({tags:   ['dog', 'hippo']})
Map/reduce
> var map = function() {
    this.tags.forEach(function(t) {
      emit(t, {count: 1});
    });
}
Map/reduce
> var reduce = function(key, val) {
  var count = 0;
  for(var i = 0, len = val.length; i < len; i++) {
    count += val[i].count;
  }
  return { count: count };
}
Map/reduce


> var result = db.items.mapReduce(map, reduce);
Map/reduce
{
    "ok" : 1,
    "timeMillis" : 86,
    "result" : "tmp.mr.mapreduce_1273861517_683",
    "counts" : {
       "input" : 6,
       "emit" : 13,
       "output" : 4
    }
}
Map/reduce
db["tmp.mr.mapreduce_1273861517_683"].find()


{   "_id"   :   "cat",	"value" :   {   "count" :   1   }   }
{   "_id"   :   "dog",	"value" :   {   "count" :   6   }   }
{   "_id"   :   "hippo", "value"   :   { "count"   :   3   } }
{   "_id"   :   "mouse", "value"   :   { "count"   :   3   } }
Atomic Operations

Incrementing & decrementing: $inc:
  db.posts.update(
    { _id : new ObjectId("4c041e...30c093") },
    { $inc : { "hits" : 1 }}
  )

  db.posts.update(
    { _id : new ObjectId("4c041e...30c093") },
    { $inc : { "hits" : -1 }}
  )
Atomic Operations


Adding, updating & removing: $set & $unset:
 db.posts.update({}, { $set : { "hits" : 0 }})

 db.posts.update({}, { $unset : { "hits" : 1 }})
Atomic Operations

Array operations: $push[All], $pull[All],
$addToSet & $pop:

 db.posts.update({ "tags": "MongoDB" }, {
    $push: { "tags" : "awesome" }
 })

 db.posts.update({ "tags": "MySQL" }, {
    $pull: { "tags" : "awesome" }
 })
Atomic Operations
Array operations: $push[All], $pull[All],
$addToSet & $pop:

 db.queues.update(
   { _id : new ObjectId("4c041e...30c093") },
   { $pop: { "operations" : 1 }}
 )

 db.todos.update(
    { _id : new ObjectId("4c041e...30c093") },
    { $pop: { "listItems" : -1 }}
 )
Indexes


Slows write performance, but greatly improves reads
For best results, index on what you query on
Mongo likes to fit its indexes in memory
Indexes

db.users.ensureIndex({ “email”: 1 })
db.posts.ensureIndex({ “tags”: 1 })
db.posts.ensureIndex({ “created”: -1 })
db.posts.ensureIndex({
   “tags”: 1,
   “created”: -1
})
Indexes

“unique” / “dropDups”: ensure uniqueness
“safe”: Make sure it worked. Else, panic.
“name”: Mostly for troubleshooting
“background”:
 Best. Performance. Panic. Button. Ever.
Indexes
When in doubt, explain()
{   "cursor" : "BtreeCursor hits_1 multi",
    "indexBounds" : [
       [{ "hits" : 0 }, { "hits" : 0 }],
       [{ "hits" : 1 }, { "hits" : 1 }]
    ],
    "nscanned" : 1,
    "nscannedObjects" : 1,
    "n" : 1,
    "millis" : 35,
    "allPlans" : [
       {
          "cursor" : "BtreeCursor hits_1 multi",
          "indexBounds" : [
             [{ "hits" : 0 }, { "hits" : 0 }],
             [{ "hits" : 1 }, { "hits" : 1 }]
          ]
       }
    ]
}
Location, location, location
Location
MongoDB makes location-aware apps stupid-simple
First, add an index:

db.places.ensureIndex({ location: “2d” })
Go to town:

db.places.find({
  location: { $near : [
    40.79870933724115, -73.7656099560547
  ]}
})
Location
Location

db.places.find({
   location: { $within : { $box : {
   [
     [40.800788494123154,
      -73.85556051757814],
     [40.68008976560161,
      -74.04232809570314]
   ]
});
Easy to get started
 Up and running on most systems in a half-dozen
 commands or less fewer
 http://try.mongodb.org/
Coming to a platform near you
Drupal for MongoDB
http://drupal.org/project/mongodb

D7: mongodb_cache: Store cache items in mongodb

D7: mongodb_field_storage: Store the fields in mongodb

D7: mongodb_session: Store sessions in mongodb

D6/D7: mongodb_watchdog: Store the watchdog messages in mongodb

D6/D7: mongodb: support library for the other modules

D7: mongodb_block: Store block information in mongodb. Very close to the core
block API

D7: mongodb_queue: DrupalQueueInterface implementation using mongodb

http://sf2010.drupal.org/conference/sessions/mongodb-humongous-drupal
Even MORE Drupal

Work to get listing API into core:
http://drupal.org/node/780154
Experimental goodies to play with:
http://drupalcode.org/viewvc/drupal/contributions/
sandbox/chx/dbtng_mongo_experimental/
Joomla!


MongoDB helper library for Joomla!
Branch of 1.6 development for alternative query builder
Full MongoDB support most likely in 2.0
Lithium PHP framework

MongoDB native support since 0.2
http://rad-dev.org/lithium/wiki
Projects demonstrating MongoDB support:
  http://rad-dev.org/lithium_mongo/source
  http://rad-dev.org/lithium_blog/source
CakePHP framework

MongoDB datasource
http://github.com/ichikaway/mongoDB-Datasource
Example article
http://mark-story.com/posts/view/using-mongodb-
with-cakephp
MongoDB Language Center
http://www.mongodb.org/display/DOCS/Drivers
Community Resources
http://www.mongodb.org/display/DOCS/
Community
Development Tracker
http://jira.mongodb.org
MongoDB Cookbook
http://cookbook.mongodb.org/
Thanks!
{
    email:    “nate.abele@gmail.com”,
    twitter: “@nateabele”,
    web:      “http://lithify.me/”,
    slides:   “http://www.slideshare.net/nateabele”
}

Building Apps with MongoDB