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");
});