Post on 08-Sep-2014
description
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
postsidtitleslugbodypublishedcreatedupdated
commentsidpost_idauthoremailbodycreated
posts_tagsidpost_idtag_id
tagsidname
MySQL
Common Tasks
MongoDB
postsidtitleslugbodypublishedcreatedupdated
commentsauthoremailbodycreated
tags
Common TasksMongoDB
{ "_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
...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" })
How do I...
Doing the “normal db” stuff
...change Bob’s email?db.posts.update(
{ "comments.email": "bob@example.com" },
{ $set : { "comments.$.email" : "robert@example.com" }})
How do I...
$set?
Querying$gt $all$gte $size$lt $exists $lte $type$ne $elemMatch $in $not$nin $where$mod
QueryingdontTrust = db.people.find({ “age”: { $gt : 30 }})
awesome = db.posts.find({ “tags” : { $in : [‘MongoDB’, ‘awesome’]}})
todo = db.tasks.find({ “status” : { $nin : [ ‘In Progress’, ‘Completed‘ ]}})
QueryingBy 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++; }});
QueryingBy 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.
IndexesWhen 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 startedUp 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 MongoDBhttp://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 datasourcehttp://github.com/ichikaway/mongoDB-Datasource
Example articlehttp://mark-story.com/posts/view/using-mongodb-with-cakephp
MongoDB Language Centerhttp://www.mongodb.org/display/DOCS/Drivers
Community Resourceshttp://www.mongodb.org/display/DOCS/Community
MongoDB Cookbookhttp://cookbook.mongodb.org/
Thanks!
{
email: “nate.abele@gmail.com”,
twitter: “@nateabele”,
web: “http://lithify.me/”,
slides: “http://www.slideshare.net/nateabele”
}