by 桂林
All languages used
Browser script: javaS c r ip t

Server script: javaS c r ip t

Database script: javaS c r ip t
All languages used
Browser script: javaS c r ip t

Server script: javaS c r ip t

Database script: javaS c r ip t
Overview of MongoDB

Patterns of mongoskin
Why MongoDB




Agile and Scalable
Overview of MongoDB
MySQL
posts
 id



comments
 id

 post_id



tags
 id

 name



posts_tags
 post_id

 tag_id
Overview of MongoDB
Database >> Database

Table >> Collection

Row >> Document
Overview of MongoDB
M on g oD B
{
    _id: ObjectId(),
    title: 'node and mongodb',
    slug: 'node-and-mongodb',
    body: '...',
    published: true,
    created: new Date('09/01/2011'),
    updated: new Date('09/16/2011'),
    comments: [
      {
        author: 'bob',
        email: 'bob@bob.me',
        body: '...',
        created: new Date('09/17/2011')
      }
    ],
    tags: ['MongoDB', 'database']
}
Overview of MongoDB
Get c ollec t ion
MongoDB shell
posts = db.posts

NodeJS
var mongodb = require('mongodb');
var db = new Db('test', new Server(host, port, {}),
{native_parser:true});
db.open(function(err, db){
    db.collection('posts', function(err, posts){
      // use posts collection here
    });
});
Overview of MongoDB
I n ser t
MongoDB shell
var doc = {title: 'test posts', ...};
posts.insert(doc);

NodeJS
posts.insert(doc, function(err, reply){
    // error or done
});
Overview of MongoDB
Q u er y
MongoDB shell
posts.find({"comments.author", "bob"})

NodeJS
posts.find({"comments.author", "bob"}, function(err, cursor){
    cursor.toArray(function(err, docs){
        // found docs here
    });
});
Overview of MongoDB
Q u er y op er at or
{<field>:{<operator>:<value>}}

db.things.find({j:{$in: [2,4,6]}});

 $gt, $lt, $gte, $lte

 $all, $exists

 $mod, $ne

 $in, $nin

 $nor, $or, $and

 $size, $type, …
Overview of MongoDB
Up d at e
MongoDB shell
posts.update({_id: doc.id}, {$inc: {votes: 1}})

NodeJS
posts.update({_id: doc.id}, {$inc: {votes: 1}}, function(err, count){
});
Overview of MongoDB
M od if ier op er at ion s
 $set – set a particular value

 $unset – delete a particular field (v1.3+)

 $inc – increment a particular value by a certain amount

 $push – append a value to an array

 $pushAll – append several values to an array

 $pull – remove a value(s) from an existing array

 $pullAll – remove several value(s) from an existing array

 $bit – bitwise operations
Overview of MongoDB
R em ove
MongoDB shell
posts.remove({author: 'bob'})

NodeJS
posts.remove({author: 'bob'}, function(err, reply){})
Overview of MongoDB
I n d ex
MongoDB shell
posts.ensureIndex({author:1})
posts.ensureIndex({slug: 1 }, {unique: true});
db.things.ensureIndex(
    {firstname: 1, lastname: 1},
    {unique: true, background:true});

NodeJS
posts.ensureIndex({author:1}, function(err, reply){})
posts.ensureIndex({slug: 1 }, {unique: true}, function(err, reply){});
db.things.ensureIndex(
    {firstname: 1, lastname: 1},
    {unique: true, background:true}, function(err, reply){});
Overview of MongoDB
Geosp at ial
{   loc   :   [   50 , 30 ] } //SUGGESTED OPTION
{   loc   :   {   x : 50 , y : 30 } }
{   loc   :   {   foo : 50 , y : 30 } }
{   loc   :   {   lon : 40.739037, lat: 73.992964 } }

db.places.ensureIndex( { loc : "2d" } )
db.places.find( { loc : { $near : [50,50] , $maxDistance : 5 } } )

box = [[40.73083, -73.99756], [40.741404, -73.988135]]
db.places.find({"loc" : {"$within" : {"$box" : box}}})

center = [50, 50]
radius = 10
db.places.find({"loc" : {"$within" : {"$center" : [center, radius]}}})
Overview of MongoDB
O p t im izat ion
 Don’t create index for every field

 Be careful about single-key indexes with low selectivity.

 Only one index could be used per query.

 Use compound-key index.
 db.places.ensureIndex( { location : “2d” , category : 1 } );
 db.places.find( { location : { $near : [50,50] }, category : ‘coffee’ } );

 Use hint. Use explain. Use the profiler.

 Pay attention to the read/write ratio of your application.
See docs for more information
Overview of MongoDB
Ag g r eg at ion
 posts.count( {author: 'bob'} )
 posts.distinct("author")

SQL group
 select a,b,sum(c) csum from coll where active=1 group by a,b

MongoDB group
 db.coll.group(
        {key: { a:true, b:true },
         cond: { active:1 },
         reduce: function(obj, out) { out.csum += obj.c; },
         initial: { csum: 0 }
       });
Overview of MongoDB
M ap R ed u c e
map = function() {
    for (var i in this.tags) {
        emit(this.tags[i], 1);
    }
}

reduce = function(key, values) {
    var count = 0;
    for (var i in values) {
        count += current[i];
    }
    return count;
}

// 1.8+ must set out collection
db.posts.mapReduce(map, reduce, {out: 'tags_count'})
Overview of MongoDB
M ap R ed u c e
> db.tags_count.find()
{"_id" : "MongoDB", "value" : 4}
{"_id" : "Map/Reduce", "value" : 2}
{"_id" : "Recipe", "value" : 7}
{"_id" : "Group", "value" : 1}
Overview of MongoDB
Gr id F S
Upload image
var gridStore = new GridStore(db, filename, "w");
gridStore.open(function(err, gridStore) {
  gridStore.write(imageData, function(err, gridStore) {
    gridStore.close(function(err, result) {
      console.log(result._id);
      users.update({_id: userId}, {$set: {avatarId: result._id}});
    });
  });
});

HTML
<img src="http://asset.url/gridfs/{{ user.avatarId }}" />

 Use nginx-gridfs
Overview of MongoDB
R ep lic at ion and S h ar d in g
Nice
Nice ,but node
Nest ed c allb ac k s, an d n ot D R Y ?
var database = new mongo.Db('testdb', new mongo.Server('localhost',
27017));
database.open(function(err, db) {
    if(err) return handle(err);
    db.collection('user', function(err, collection) {
        if(err) return handle(err);
        collection.find({}, function(err, cursor) {
            if(err) return handle(err);
            cursor.toArray(function(err, users) {
                if(err) return handle(err);
                doSomething(users);
            });
        });
    });
});
How t o exp or t c ollec t ion
var database = new mongo.Db('testdb', new mongo.Server('localhost',
27017));
database.open(function(err, db){
    db.collection('posts', function(err, posts) {
      // can't export here?
    });
});
exports.posts = ?
How t o sh ar e c ollec t ion ob jec t
controllers/user.js
var database = new mongo.Db('testdb', new mongo.Server('localhost',
27017));
//...
database.open(function(err, db){
    db.collection('user', function(err, userColl){

            userColl.find({}, function(err, cursor){
                if(err) return handle(err);
                cursor.toArray(function(err, users){
                    res.render('/user.html', {users: users});
                })
            });

      });
});
How t o sh ar e c ollec t ion ob jec t
controllers/book.js
var database = new mongo.Db('testdb', new mongo.Server('localhost',
27017));
//...
database.open(function(err, db){
    db.collection('user', function(err, userColl){
        userColl.findOne({_id: book.author_id}, function(err, author){
            res.render('/book.html', {book: book, author: author});
        });
    });
});
Redsign the API
How ab ou t t h is
config.js
exports.db = mongo.db('mongo://localhost:27017/testdb')
An d t h is
controllers/user.js
var db = require('../config').db;
db.collection('user').find({}).toArray(function(err, users){
    if(err) return handle(err);
    res.render('/user.html', {users: users});
});
An d t h is
controllers/book.js
var db = require('../config').db;
db.collection('user').findOne({_id, book.author_id}, function(err,
author){
    if(err) return handle(err);
    res.render('/book.html', {book: book, author: author});
});
It’s MongoSkin
Patterns of mongoskin
var mongoskin = require('mongoskin');

var db = mongoskin.db('mongo://localhost:27017/testdb');

db.bind('users');
db.bind('books');

db.users.find({}).limit(10).sort({name:-1}).toArray(function(err,
users){
});

db.books.update({_id: bookId}, {$inc: {votes: 1}}, function(err,
reply){
});
Patterns of mongoskin
Pr oxy all m et h od s
node-mongoskin
var skindb = mongoskin.db('mongo://localhost:27017/testdb')
function callback(err, reply){}
skindb.addUser('foo', 'bar', callback);

node-mongodb-native
var db = new mongodb.Db('testdb',
    new mongodb.Server('localhost', 27017));

function callback(err, reply){}
db.open(function(err, db){
    if(err) return callback(err);
    db.addUser('foo', 'bar', callback);
});
Patterns of mongoskin
SkinClass hold all parameters to open it
var SkinDb = exports.SkinDb = function(db, username, password) {
  this.db = db;
  this.username = username;
  this.password = password;
  this.state = STATE_CLOSE;
  this.emitter = new events.EventEmitter();
  this._collections = {};
};
Patterns of mongoskin
Proxy all methods inside open
for (var name in Db.prototype) {
  SkinDb.prototype[name] = function() {
    var args = Array.prototype.slice.call(arguments);
    this.open(function(err, db) {
      if (err) {
        return args[args.length - 1](err);//callback(err)
      } else {
        return Db.prototype[name].apply(db, args);
      }
    });
  };
}
Patterns of mongoskin
Open only on c e (pseudo-code)
SkinDb.prototype.open = function(fn) {
  switch (this.state) {

  case STATE_OPEN:
    return fn(null, this.db);

  case STATE_OPENNING:
    // if call 'open' method multi times before opened
    return this.emitter.addListener('open', fn);

  case STATE_CLOSE:
    this.state = STATE_OPENNING;
    var that = this;
    this.db.open(function(err, db){
        that.db = db;
        fn(err, db);
        that.state = STATE_OPEN;
        that.emitter.emit('open', err, db);
    });
 }
}
 };
      Patterns of mongoskin
R et u r n s S k in Collec t ion
 SkinDb.prototype.collection = function(name) {
   var collection = this._collections[name];
   if (!collection) {
     this._collections[name] = collection = new SkinCollection(this,
 name);
   }
   return collection;
 };
Patterns of mongoskin
S k in Collec t ion
 var SkinCollection = exports.SkinCollection = function(skinDb,
 collectionName) {
   this.skinDb = skinDb;
   this.collectionName = collectionName;
   this.collection;
   this.state = STATE_CLOSE;
   this.internalHint;
   var that = this;
   this.__defineGetter__('hint', function() { return this.internalHint;
 });
   this.__defineSetter__('hint', function(value) {
       this.internalHint = value;
       this.open(function(err, collection) {
         collection.hint = value;
         that.internalHint = collection.hint;
       });
   });

     this.emitter = new events.EventEmitter();
 }
}


    Patterns of mongoskin
Pr oxy all m et h od s in sid e S k in Collec t ion . op en
for (var name in Collection.prototype) {
  SkinCollection.prototype[name] = function() {
    var args = Array.prototype.slice.call(arguments);
    this.open(function(err, collection) {
      if (err) {
        args[args.length - 1](err);// callback(err)
      } else {
        Collection.prototype[name].apply(collection, args);
      }
    });
  };
}
Patterns of mongoskin
S k in Collec t ion . op en
 SkinCollection.prototype.open = function(callback) {
   //...
   var that = this;
   this.skinDb.open(function(err, db){
       db.collection(that.collectionName, function(err, collection){
         this.nativeCollection = collection;
         callback(err, collection);
       }
   });
 }
Patterns of mongoskin
R et u r n s S k in Cu r sor if n o c allb ac k
 SkinCollection.prototype.find = function() {
   var args = Array.prototype.slice.call(arguments);
   if (args.length > 0 && typeof(args[args.length - 1]) === 'function')
 {
     this._find.apply(this, args);
   }else {
     return new SkinCursor(null, this, args);
   }
 };

An d so d o w it h S k in Cu r sor
Patterns of mongoskin
Pat t er n s of m on g osk in
 SkinClass contains all parameters to get NativeObject

 Proxy all method inside callback of op en ( )

 Make open() method cache result

 Return SkinClass object to chain execution
Reference
MongoDB The Definitive Guide

MongoDB Docs http://www.mongodb.org/display/DOCS/Home

MongoDB Cookbook http://cookbook.mongodb.org/

Node mongodb https://github.com/christkv/node-mongodb-native

Mongoskin https://github.com/guileen/node-mongoskin
Thank you
   桂糊涂@weibo

 guileen@gmail.com

Mongoskin - Guilin

  • 1.
  • 2.
    All languages used Browserscript: javaS c r ip t Server script: javaS c r ip t Database script: javaS c r ip t
  • 3.
    All languages used Browserscript: javaS c r ip t Server script: javaS c r ip t Database script: javaS c r ip t
  • 4.
  • 5.
  • 6.
    Overview of MongoDB MySQL posts id comments id post_id tags id name posts_tags post_id tag_id
  • 7.
    Overview of MongoDB Database>> Database Table >> Collection Row >> Document
  • 8.
    Overview of MongoDB Mon g oD B { _id: ObjectId(), title: 'node and mongodb', slug: 'node-and-mongodb', body: '...', published: true, created: new Date('09/01/2011'), updated: new Date('09/16/2011'), comments: [ { author: 'bob', email: 'bob@bob.me', body: '...', created: new Date('09/17/2011') } ], tags: ['MongoDB', 'database'] }
  • 9.
    Overview of MongoDB Getc ollec t ion MongoDB shell posts = db.posts NodeJS var mongodb = require('mongodb'); var db = new Db('test', new Server(host, port, {}), {native_parser:true}); db.open(function(err, db){ db.collection('posts', function(err, posts){ // use posts collection here }); });
  • 10.
    Overview of MongoDB In ser t MongoDB shell var doc = {title: 'test posts', ...}; posts.insert(doc); NodeJS posts.insert(doc, function(err, reply){ // error or done });
  • 11.
    Overview of MongoDB Qu er y MongoDB shell posts.find({"comments.author", "bob"}) NodeJS posts.find({"comments.author", "bob"}, function(err, cursor){ cursor.toArray(function(err, docs){ // found docs here }); });
  • 12.
    Overview of MongoDB Qu er y op er at or {<field>:{<operator>:<value>}} db.things.find({j:{$in: [2,4,6]}}); $gt, $lt, $gte, $lte $all, $exists $mod, $ne $in, $nin $nor, $or, $and $size, $type, …
  • 13.
    Overview of MongoDB Upd at e MongoDB shell posts.update({_id: doc.id}, {$inc: {votes: 1}}) NodeJS posts.update({_id: doc.id}, {$inc: {votes: 1}}, function(err, count){ });
  • 14.
    Overview of MongoDB Mod if ier op er at ion s $set – set a particular value $unset – delete a particular field (v1.3+) $inc – increment a particular value by a certain amount $push – append a value to an array $pushAll – append several values to an array $pull – remove a value(s) from an existing array $pullAll – remove several value(s) from an existing array $bit – bitwise operations
  • 15.
    Overview of MongoDB Rem ove MongoDB shell posts.remove({author: 'bob'}) NodeJS posts.remove({author: 'bob'}, function(err, reply){})
  • 16.
    Overview of MongoDB In d ex MongoDB shell posts.ensureIndex({author:1}) posts.ensureIndex({slug: 1 }, {unique: true}); db.things.ensureIndex( {firstname: 1, lastname: 1}, {unique: true, background:true}); NodeJS posts.ensureIndex({author:1}, function(err, reply){}) posts.ensureIndex({slug: 1 }, {unique: true}, function(err, reply){}); db.things.ensureIndex( {firstname: 1, lastname: 1}, {unique: true, background:true}, function(err, reply){});
  • 17.
    Overview of MongoDB Geospat ial { loc : [ 50 , 30 ] } //SUGGESTED OPTION { loc : { x : 50 , y : 30 } } { loc : { foo : 50 , y : 30 } } { loc : { lon : 40.739037, lat: 73.992964 } } db.places.ensureIndex( { loc : "2d" } ) db.places.find( { loc : { $near : [50,50] , $maxDistance : 5 } } ) box = [[40.73083, -73.99756], [40.741404, -73.988135]] db.places.find({"loc" : {"$within" : {"$box" : box}}}) center = [50, 50] radius = 10 db.places.find({"loc" : {"$within" : {"$center" : [center, radius]}}})
  • 18.
    Overview of MongoDB Op t im izat ion Don’t create index for every field Be careful about single-key indexes with low selectivity. Only one index could be used per query. Use compound-key index. db.places.ensureIndex( { location : “2d” , category : 1 } ); db.places.find( { location : { $near : [50,50] }, category : ‘coffee’ } ); Use hint. Use explain. Use the profiler. Pay attention to the read/write ratio of your application. See docs for more information
  • 19.
    Overview of MongoDB Agg r eg at ion posts.count( {author: 'bob'} ) posts.distinct("author") SQL group select a,b,sum(c) csum from coll where active=1 group by a,b MongoDB group db.coll.group( {key: { a:true, b:true }, cond: { active:1 }, reduce: function(obj, out) { out.csum += obj.c; }, initial: { csum: 0 } });
  • 20.
    Overview of MongoDB Map R ed u c e map = function() { for (var i in this.tags) { emit(this.tags[i], 1); } } reduce = function(key, values) { var count = 0; for (var i in values) { count += current[i]; } return count; } // 1.8+ must set out collection db.posts.mapReduce(map, reduce, {out: 'tags_count'})
  • 21.
    Overview of MongoDB Map R ed u c e > db.tags_count.find() {"_id" : "MongoDB", "value" : 4} {"_id" : "Map/Reduce", "value" : 2} {"_id" : "Recipe", "value" : 7} {"_id" : "Group", "value" : 1}
  • 22.
    Overview of MongoDB Grid F S Upload image var gridStore = new GridStore(db, filename, "w"); gridStore.open(function(err, gridStore) { gridStore.write(imageData, function(err, gridStore) { gridStore.close(function(err, result) { console.log(result._id); users.update({_id: userId}, {$set: {avatarId: result._id}}); }); }); }); HTML <img src="http://asset.url/gridfs/{{ user.avatarId }}" /> Use nginx-gridfs
  • 23.
    Overview of MongoDB Rep lic at ion and S h ar d in g
  • 24.
  • 25.
  • 26.
    Nest ed callb ac k s, an d n ot D R Y ? var database = new mongo.Db('testdb', new mongo.Server('localhost', 27017)); database.open(function(err, db) { if(err) return handle(err); db.collection('user', function(err, collection) { if(err) return handle(err); collection.find({}, function(err, cursor) { if(err) return handle(err); cursor.toArray(function(err, users) { if(err) return handle(err); doSomething(users); }); }); }); });
  • 27.
    How t oexp or t c ollec t ion var database = new mongo.Db('testdb', new mongo.Server('localhost', 27017)); database.open(function(err, db){ db.collection('posts', function(err, posts) { // can't export here? }); }); exports.posts = ?
  • 28.
    How t osh ar e c ollec t ion ob jec t controllers/user.js var database = new mongo.Db('testdb', new mongo.Server('localhost', 27017)); //... database.open(function(err, db){ db.collection('user', function(err, userColl){ userColl.find({}, function(err, cursor){ if(err) return handle(err); cursor.toArray(function(err, users){ res.render('/user.html', {users: users}); }) }); }); });
  • 29.
    How t osh ar e c ollec t ion ob jec t controllers/book.js var database = new mongo.Db('testdb', new mongo.Server('localhost', 27017)); //... database.open(function(err, db){ db.collection('user', function(err, userColl){ userColl.findOne({_id: book.author_id}, function(err, author){ res.render('/book.html', {book: book, author: author}); }); }); });
  • 30.
  • 31.
    How ab out t h is config.js exports.db = mongo.db('mongo://localhost:27017/testdb')
  • 32.
    An d th is controllers/user.js var db = require('../config').db; db.collection('user').find({}).toArray(function(err, users){ if(err) return handle(err); res.render('/user.html', {users: users}); });
  • 33.
    An d th is controllers/book.js var db = require('../config').db; db.collection('user').findOne({_id, book.author_id}, function(err, author){ if(err) return handle(err); res.render('/book.html', {book: book, author: author}); });
  • 34.
  • 35.
    Patterns of mongoskin varmongoskin = require('mongoskin'); var db = mongoskin.db('mongo://localhost:27017/testdb'); db.bind('users'); db.bind('books'); db.users.find({}).limit(10).sort({name:-1}).toArray(function(err, users){ }); db.books.update({_id: bookId}, {$inc: {votes: 1}}, function(err, reply){ });
  • 36.
    Patterns of mongoskin Proxy all m et h od s node-mongoskin var skindb = mongoskin.db('mongo://localhost:27017/testdb') function callback(err, reply){} skindb.addUser('foo', 'bar', callback); node-mongodb-native var db = new mongodb.Db('testdb', new mongodb.Server('localhost', 27017)); function callback(err, reply){} db.open(function(err, db){ if(err) return callback(err); db.addUser('foo', 'bar', callback); });
  • 37.
    Patterns of mongoskin SkinClasshold all parameters to open it var SkinDb = exports.SkinDb = function(db, username, password) { this.db = db; this.username = username; this.password = password; this.state = STATE_CLOSE; this.emitter = new events.EventEmitter(); this._collections = {}; };
  • 38.
    Patterns of mongoskin Proxyall methods inside open for (var name in Db.prototype) { SkinDb.prototype[name] = function() { var args = Array.prototype.slice.call(arguments); this.open(function(err, db) { if (err) { return args[args.length - 1](err);//callback(err) } else { return Db.prototype[name].apply(db, args); } }); }; }
  • 39.
    Patterns of mongoskin Openonly on c e (pseudo-code) SkinDb.prototype.open = function(fn) { switch (this.state) { case STATE_OPEN: return fn(null, this.db); case STATE_OPENNING: // if call 'open' method multi times before opened return this.emitter.addListener('open', fn); case STATE_CLOSE: this.state = STATE_OPENNING; var that = this; this.db.open(function(err, db){ that.db = db; fn(err, db); that.state = STATE_OPEN; that.emitter.emit('open', err, db); }); }
  • 40.
    } }; Patterns of mongoskin R et u r n s S k in Collec t ion SkinDb.prototype.collection = function(name) { var collection = this._collections[name]; if (!collection) { this._collections[name] = collection = new SkinCollection(this, name); } return collection; };
  • 41.
    Patterns of mongoskin Sk in Collec t ion var SkinCollection = exports.SkinCollection = function(skinDb, collectionName) { this.skinDb = skinDb; this.collectionName = collectionName; this.collection; this.state = STATE_CLOSE; this.internalHint; var that = this; this.__defineGetter__('hint', function() { return this.internalHint; }); this.__defineSetter__('hint', function(value) { this.internalHint = value; this.open(function(err, collection) { collection.hint = value; that.internalHint = collection.hint; }); }); this.emitter = new events.EventEmitter(); }
  • 42.
    } Patterns of mongoskin Pr oxy all m et h od s in sid e S k in Collec t ion . op en for (var name in Collection.prototype) { SkinCollection.prototype[name] = function() { var args = Array.prototype.slice.call(arguments); this.open(function(err, collection) { if (err) { args[args.length - 1](err);// callback(err) } else { Collection.prototype[name].apply(collection, args); } }); }; }
  • 43.
    Patterns of mongoskin Sk in Collec t ion . op en SkinCollection.prototype.open = function(callback) { //... var that = this; this.skinDb.open(function(err, db){ db.collection(that.collectionName, function(err, collection){ this.nativeCollection = collection; callback(err, collection); } }); }
  • 44.
    Patterns of mongoskin Ret u r n s S k in Cu r sor if n o c allb ac k SkinCollection.prototype.find = function() { var args = Array.prototype.slice.call(arguments); if (args.length > 0 && typeof(args[args.length - 1]) === 'function') { this._find.apply(this, args); }else { return new SkinCursor(null, this, args); } }; An d so d o w it h S k in Cu r sor
  • 45.
    Patterns of mongoskin Patt er n s of m on g osk in SkinClass contains all parameters to get NativeObject Proxy all method inside callback of op en ( ) Make open() method cache result Return SkinClass object to chain execution
  • 46.
    Reference MongoDB The DefinitiveGuide MongoDB Docs http://www.mongodb.org/display/DOCS/Home MongoDB Cookbook http://cookbook.mongodb.org/ Node mongodb https://github.com/christkv/node-mongodb-native Mongoskin https://github.com/guileen/node-mongoskin
  • 47.
    Thank you 桂糊涂@weibo guileen@gmail.com