Mongoose简明教程

Mogoose connection

  • default connection eg.
var dbURI = 'mongodb://localhost/mydatabase';
mongoose.connect(dbURI);
  • multiple connections eg.
 var dbURI = 'mongodb://localhost/myadmindatabase';
var adminConnection = mongoose.createConnection(dbURI);

mongoose.connect 和 mongoose.createConnection创建的连接均为连接池模式,默认poolSize: 5,可根据业务需要自行修改。

  • sample code
    helpers/mongoose_conn.js
import mongoose from 'mongoose';
import mongooseOptions from '../config/mongoose';
import { debugLogger, errorLogger } from './logger_creator';

mongoose.Promise = global.Promise;
// conenct mongodb
const monOptions = mongooseOptions();
mongoose.connect(monOptions.connectionString, monOptions.options);
const mongooseConn = mongoose.connection;

mongooseConn.once('connected', () => {
    debugLogger.debug('mongodb connect success!!!');
});

mongooseConn.on('error', (err) => {
    errorLogger.error(`mongodb error:${err}`);
});




export default mongooseConn;

config/mongoose.js

import { isProduction} from '../../../common/helpers/env_helper';

const mongooseOptions = function mongooseOptions() {
    if (isProduction()) {
        return {
            connectionString: 'mongodb://username:password@hostname:port,hostname:port/dbname', // TODO
            options: {
                auth: { authdb: 'dbname' },
                replset: { rs_name: 'mgset-xxx' },
                keepAlive: 1,
                useMongoClient: true,

            },
        };
    }
    return {
        connectionString: 'mongodb://192.168.1.1:27020/dbname', // TODO
        options: {
            keepAlive: 1,
            useMongoClient: true,
        },
    };
};
export default mongooseOptions;

Schema

Schema对应描述MongoDB中的Document的数据结构。

  • String
  • Number
  • Date
  • Boolean
  • Buffer
  • ObjectId
  • Mixed
  • Array

Schema的数据类型可支持扩展,字段可以动态添加。同时还支持各种限定条件。

 var userSchema = new mongoose.Schema({
     name: String,
     email: {type: String, unique:true},
     createdOn: { type: Date, default: Date.now },
     modifiedOn: Date,
     lastLogin: Date
   });

对应的文档对象:


   { "__v" : 0,
   "_id" : ObjectId("5126b7a1f8a44d1e32000001"),
   "createdOn" : ISODate("2013-02-22T00:11:13.436Z"),
   "email" : "simon@theholmesoffice.com",
   "lastLogin" : ISODate("2013-04-03T12:54:42.734Z"),
   "modifiedOn" : ISODate("2013-04-03T12:56:26.009Z"),
   "name" : "Simon Holmes" }

Model

Model是Schema的编译版本。

mongoose.model( 'User', userSchema );

CURD

Create

create有两种实现方式:

  var newUser = new User({
     name: 'Simon Holmes',
     email: 'simon@theholmesoffice.com',
     lastLogin : Date.now()
   }).save( function( err ){
     if(!err){
       console.log('User saved!');
     }
});

User.create({
     name: 'Simon Holmes',
     email: 'simon@theholmesoffice.com',
     lastLogin : Date.now()
   }, function( err, user ){
     if(!err){
       console.log('User saved!');
       console.log('Saved user name: ' + user.name);
       console.log('_id of saved user: ' + user._id);
} });

Read

  • QueryBuilder
var myQuery = User.find({'name' : 'Simon Holmes'})
   .where('age').gt(18)
   .sort('-lastLogin')
   .select('_id name email');
   // do some other operations
   // and then...
   myQuery.exec(function (err, users){
     if (!err){
       console.log(users); // output array of users found
} });
  • Single query
Model.find(conditions, [fields], [options], [callback])

User.find(
     {'name' : 'Simon Holmes'}, // users called Simon Holmes
     function (err, users){
       if (!err){console.log(users);}
   });

 User.find(
     {'name' : 'Simon Holmes'}, // users called Simon Holmes
     'name email', // returning just the name and email fields
     function (err, users){
       if (!err){console.log(users);}
   });

 User.find(
     {'name' : 'Simon Holmes'}, // users called Simon Holmes
     null, // returning all fields in model
     {sort : {lastLogin : -1}}, // sorted by lastLogin descending
     function (err, users){
       if (!err){console.log(users);}
   });

Model拥有内置的静态helper方法:

Model.find(query) 
Model.findOne(query) 
Model.findById(ObjectID) 

我们可以可以自定义helper方法(置于model compiled之前):

projectSchema.statics.findByUserID = function (userid, callback) {
     this.find(
       { createdBy: userid },
       '_id projectName',
       {sort: 'modifiedOn'}
       callback);
}

Update

Model拥有内置的静态helper方法:

update() //更新数据,不返回
findOneAndUpdate() 
findByIdAndUpdate()

Delete

Model拥有内置的静态helper方法:

remove()
findOneAndRemove()
findByIdAndRemove()

validators

  • 适用于所有的SchemaType的validator
email: { type: String, unique: true, required: true }
  • Number
 var teenSchema = new Schema({
     age : {type: Number, min: 13, max:19}
});
  • String
 var weekdaySchema = new Schema({
     day : {type: String, match: /^(mon|tues|wednes|thurs|fri)day$/i}
});

var weekdays = ['monday', 'tuesday', 'wednesday', 'thursday',
   'friday'];
   var weekdaySchema = new Schema({
     day : {type: String,  enum: weekdays}
   });

在save的callback中我们收到error:

{ message: 'Validation failed',
     name: 'ValidationError',
     errors:
      { email:
         { message: 'Validator "required" failed for path email',
           name: 'ValidatorError',
           path: 'email',
           type: 'required' },
        name:
         { message: 'Validator "required" failed for path name',
           name: 'ValidatorError',
           path: 'name',
           type: 'required' } } }
  • 自定义
// 定义函数
 var lengthValidator = function(val) {
     if (val && val.length >= 5){
       return true;
     }
     return false;
   };

name: {type: String, required: true, validate: { validator: lengthValidator, msg: 'Too short' } }

// 定义正则

 var weekdaySchema = new Schema({
     day : {type: String, validate: {validator:
   /^(mon|tues|wednes|thurs|fri)day$/i, msg: 'Not a day' }
   });

// 数组形式
 var validateLength = [lengthValidator, 'Too short' ];
 var validateDay = [/^(mon|tues|wednes|thurs|fri)day$/i, 'Not a day' ];

name: {type: String, required: true, validate: validateLength }
day : {type: String, validate: validateDay }

// 多个validator组合
var validateLength = [{validator: isNotTooShort, msg: 'Too short'} ];
name: {type: String, required: true, validate: validateLength },

复杂Schema

Population

MongoDB不支持JOIN,Population有点类似关系数据库中的JOIN

var projectSchema = new mongoose.Schema({
     ...
     createdBy: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
     contributors: [ {type: mongoose.Schema.Types.ObjectId, ref: 'User'} ] ,
    ... });

对应的document:

{ "projectName" : "Population test",
     "createdBy" : ObjectId("5126b7a1f8a44d1e32000001"),
     "_id" : ObjectId("51ac2fc4c746ba1645000002"),
     "contributors" : [
       ObjectId("5126b7a1f8a44d1e32000001"),
       ObjectId("5126b7a1f8a44d1e32000002") ] }

查询数据时:

Project
   .findById( req.params.id)
  .populate('createdBy', 'name email')
   .populate('contributors', 'name email')
   .exec(function(err,project) { ...

查询结果为:

{ __v: 0,
     _id: 51b495e01e686ea360000002,
     createdBy:
      { _id: 5126b7a1f8a44d1e32000001,
        email: 'simon@theholmesoffice.com',
        name: 'Simon Holmes' },
     modifiedOn: Sun Jul 07 2013 16:21:50 GMT+0100 (BST),
     projectName: 'Updated project schemas',
     createdOn: Sun Jun 09 2013 15:49:04 GMT+0100 (BST),
     contributors: [
       { _id: 5126b7a1f8a44d1e32000001,
         email: 'simon@theholmesoffice.com',
         name: 'Simon Holmes' },
       { _id: 5126b7a1f8a44d1e32000002,
         email: 'someone@theholmesoffice.com',
         name: 'Someone Else' }
] }

population支持进一步的子查询操作:

 .populate({
     path: 'contributors',
     match: { email: /@theholmesoffice\.com/i },
     select: 'name lastLogin',
     options: { limit: 5, sort: 'name' }
   })
.exec()

Subdocument

  var projectSchema = new mongoose.Schema({
     projectName: String,
     ...
     tasks: [taskSchema]
});

对应的document:

 {
     "projectName" : "Project 2",
     "tasks" : [
         "taskName" : "A task please",
         "taskDesc" : "A short description of the task",
         "createdBy" : ObjectId("5126b7a1f8a44d1e32000001"),
         "_id" : ObjectId("51ad7d6cfa492a174a000005"),
         "createdOn" : ISODate("2013-06-04T05:38:52.847Z")
}, {
         "createdBy" : ObjectId("5126b7a1f8a44d1e32000002"),
         "_id" : ObjectId("51ad7d80fa492a174a000006"),
         "createdOn" : ISODate("2013-06-04T05:39:12.728Z"),
         "modifiedOn" : ISODate("2013-06-04T05:39:48.553Z"),
         "taskDesc" : "A quick description of this one too",
         "taskName" : "A secondary task"
 } ]
}

每一个subdocument都会自动生成一个唯一 _id 字段。
所有对subdocument的操作,均需要通过parent进行。parent document save时,即完成了subdocument的save。

// 创建 subdocument
Project.findById( req.body.projectID, 'tasks modifiedOn',
  function (err, project) {
    if(!err){
      project.tasks.push({
        taskName: req.body.taskName,
        taskDesc: req.body.taskDesc,
        createdBy: req.session.user._id
      });
      project.modifiedOn = Date.now();
      project.save(function (err, project){
        var qstring = '?';
        if(err){
          console.log('Oh dear', err);
          if (err.name === "ValidationError") {
            for (var input in err.errors) {
              qstring += err.errors[input].path + '=invalid&';
              console.log(err.errors[input].message);
            }
          }else{
            res.redirect('/?error=true');
          }
          req.session.tmpTask = { "taskName": req.body.taskName, "taskDesc": req.body.taskDesc };
          res.redirect('/project/' + req.body.projectID + '/task/new' + qstring);
        } else {
          console.log('Task saved: ' + req.body.taskName);
          res.redirect( '/project/' + req.body.projectID );
        }
      });
    }
  }
);


// 查询 subdocument
 Project.findById( req.body.projectID, 'tasks modifiedOn',function (err, project) {
    if(!err){
        console.log(project.tasks); // array of tasks
        var thisTask = project.tasks.id(req.params.taskID); 
        console.log(thisTask); // individual task document
    }});

// 删除 subdocument
project.tasks.id(req.body.taskID).remove();

常见问题

Replica-Set

多个MongoDB Server,镜像同样的数据。
数据读写操作:当read时,请求任意一个set;当write时,只会写入Primary server。

Multiple mongos

分片:每个connection通过MongoDB的路由,再由路由进行转发给具体负责的MongoDB

Multiple connections

当应用创建了数据库的多个连接,可以并行处理多个请求。数据库驱动会自动处理这种情况。
只有当同时连接多个数据库或者每个连接的配置不同时,此时需要自己处理每个请求的逻辑。
eg.

var conn = mongoose.createConnection('mongodb://localhost1/test1');
var conn2 = mongoose.createConnection('mongodb://localhost2/test2');
var model1 = conn.model('Model', Schema);
var model2 = conn2.model('Model', Schema);
model1.find({long query}, function() {
   console.log("this will print out last");
});
model2.find({short query}, function() {
   console.log("this will print out first");
});
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 151,688评论 1 330
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 64,559评论 1 273
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 101,749评论 0 226
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 42,581评论 0 191
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 50,741评论 3 271
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 39,684评论 1 192
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,122评论 2 292
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 29,847评论 0 182
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 33,441评论 0 228
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 29,939评论 2 232
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,333评论 1 242
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 27,783评论 2 236
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,275评论 3 220
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 25,830评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,444评论 0 180
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 34,553评论 2 249
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 34,618评论 2 249

推荐阅读更多精彩内容