Skip to content

Commit

Permalink
feat: support multi client (#15)
Browse files Browse the repository at this point in the history
* feat: support multi client

1.support multi client, add Singleton `app.mongooseDB`
2.now `app.mongoose` is default Mongoose instance, remove `app.__mongoose`
3.loadModel after loaded

* test: misspell & optimize test sub model

* docs: multiple connections

* feat: add loadModel
  • Loading branch information
jinasonlin authored and jtyjty99999 committed Mar 2, 2018
1 parent 979b931 commit 22d134b
Show file tree
Hide file tree
Showing 50 changed files with 660 additions and 96 deletions.
109 changes: 95 additions & 14 deletions README.md
Expand Up @@ -27,57 +27,138 @@ Egg's mongoose plugin.
$ npm i egg-mongoose --save
```

## Usage
## Configuration

Change `{app_root}/config/plugin.js` to enable `egg-mongoose` plugin:

```js
// {app_root}/config/plugin.js
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};
```

## Configuration
## Simple connection

### Config

```js
// {app_root}/config/config.default.js
exports.mongoose = {
url: 'mongodb://127.0.0.1/example',
options: {}
options: {},
};
// recommended
exports.mongoose = {
client: {
url: 'mongodb://127.0.0.1/example',
options: {},
},
};
```

see [config/config.default.js](config/config.default.js) for more detail.
### Example

## Multi-mongos support
```js
// {app_root}/app/model/user.js
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;

const UserSchema = new Schema({
userName: { type: String },
password: { type: String },
});

return mongoose.model('User', UserSchema);
}

// {app_root}/app/controller/user.js
exports.index = function* (ctx) {
ctx.body = yield ctx.model.User.find({});
}
```

## Multiple connections

### Config

```js
// {app_root}/config/config.default.js
exports.mongoose = {
url: 'mongodb://mongosA:27501,mongosB:27501',
options: {}
clients: {
// clientId, access the client instance by app.mongooseDB.get('clientId')
db1: {
url: 'mongodb://127.0.0.1/example1',
options: {},
},
db2: {
url: 'mongodb://127.0.0.1/example2',
options: {},
},
},
};
```

## Example
### Example

```js
// app/model/user.js
// {app_root}/app/model/user.js
module.exports = app => {
const mongoose = app.mongoose;
const UserSchema = new mongoose.Schema({
const Schema = mongoose.Schema;
const conn = app.mongooseDB.get('db1');

const UserSchema = new Schema({
userName: { type: String },
password: { type: String }
password: { type: String },
});

return mongoose.model('User', UserSchema);
return conn.model('User', UserSchema);
}

// {app_root}/app/model/book.js
module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const conn = app.mongooseDB.get('db2');

const BookSchema = new Schema({
name: { type: String },
});

return conn.model('Book', BookSchema);
}

// app/controller/user.js
exports.index = function* (ctx) {
ctx.body = yield ctx.model.User.find({}); // you should use upper case to access mongoose model
ctx.body = yield ctx.model.User.find({}); // get data from db1
}

// app/controller/book.js
exports.index = function* (ctx) {
ctx.body = yield ctx.model.Book.find({}); // get data from db2
}
```

### Default config

see [config/config.default.js](config/config.default.js) for more detail.

## Multi-mongos support

```js
// {app_root}/config/config.default.js
exports.mongoose = {
client: {
url: 'mongodb://mongosA:27501,mongosB:27501',
options: {
mongos: true,
},
},
};
```

## Questions & Suggestions

Please open an issue [here](https://github.com/eggjs/egg-mongoose/issues).
Expand Down
2 changes: 1 addition & 1 deletion agent.js
@@ -1,5 +1,5 @@
'use strict';

module.exports = agent => {
require('./lib/mongoose')(agent);
if (agent.config.mongoose.agent) require('./lib/mongoose')(agent);
};
2 changes: 1 addition & 1 deletion app.js
@@ -1,5 +1,5 @@
'use strict';

module.exports = app => {
require('./lib/mongoose')(app);
if (app.config.mongoose.app) require('./lib/mongoose')(app);
};
3 changes: 3 additions & 0 deletions config/config.default.js
Expand Up @@ -10,4 +10,7 @@
exports.mongoose = {
url: '',
options: {},
loadModel: true,
app: true,
agent: false,
};
85 changes: 58 additions & 27 deletions lib/mongoose.js
Expand Up @@ -3,68 +3,99 @@
const assert = require('assert');
const path = require('path');
const mongoose = require('mongoose');
const EventEmitter = require('events');
const awaitEvent = require('await-event');

let count = 0;

module.exports = app => {
const config = app.config.mongoose;
assert(config.url, '[egg-mongoose] url is required on config');
app.coreLogger.info('[egg-mongoose] connecting %s', config.url);
const { client, clients, url, options, defaultDB, customPromise, loadModel } = app.config.mongoose;

// compatibility
if (!client && !clients && url) {
app.config.mongoose.client = {
url,
options,
};
}

const customPromise = app.config.mongoose.customPromise;
mongoose.Promise = customPromise ? customPromise : Promise;

// mongoose.connect('mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]' [, options]);
const db = mongoose.createConnection(config.url, config.options);
db.Schema = mongoose.Schema;
app.mongoose = db;
app.__mongoose = mongoose;
// TODO addSingleton support config[this.configName]?
app.addSingleton('mongoose', createOneClient);

app.mongooseDB = app.mongoose;

// set default connection(ref models has fixed in mongoose 4.13.7)
if (app.mongooseDB instanceof mongoose.Connection) {
mongoose.connection = app.mongooseDB;
} else if (defaultDB && app.mongooseDB.get(defaultDB) instanceof mongoose.Connection) {
mongoose.connection = app.mongooseDB.get(defaultDB);
}

app.mongoose = mongoose;
app.mongoose.loadModel = () => loadModelToApp(app);

if (loadModel) {
app.beforeStart(() => {
loadModelToApp(app);
});
}
};

function createOneClient(config, app) {
const { url, options } = config;

assert(url, '[egg-mongoose] url is required on config');

const heartEvent = new EventEmitter();
heartEvent.await = awaitEvent;
app.coreLogger.info('[egg-mongoose] connecting %s', url);

const db = mongoose.createConnection(url, options);

/* istanbul ignore next */
db.on('error', err => {
err.message = `[egg-mongoose]${err.message}`;
app.coreLogger.error(err);
});

/* istanbul ignore next */
db.on('disconnected', () => {
app.coreLogger.error(`[egg-mongoose] ${config.url} disconnected`);
app.coreLogger.error(`[egg-mongoose] ${url} disconnected`);
});

db.on('connected', () => {
heartEvent.emit('connected');
app.coreLogger.info(`[egg-mongoose] ${config.url} connected successfully`);
app.coreLogger.info(`[egg-mongoose] ${url} connected successfully`);
});

/* istanbul ignore next */
db.on('reconnected', () => {
app.coreLogger.info(`[egg-mongoose] ${config.url} reconnected successfully`);
app.coreLogger.info(`[egg-mongoose] ${url} reconnected successfully`);
});

loadModel(app);

app.beforeStart(function* () {
app.coreLogger.info('[egg-mongoose] starting...');
yield heartEvent.await('connected');
yield awaitEvent(db, 'connected');
const index = count++;
/*
*remove heartbeat to avoid no authentication
const serverStatus = yield db.db.command({
serverStatus: 1,
});
const serverStatus = yield db.db.command({
serverStatus: 1,
});
assert(serverStatus.ok === 1, '[egg-mongoose] server status is not ok, please check mongodb service!');
assert(serverStatus.ok === 1, '[egg-mongoose] server status is not ok, please check mongodb service!');
*/
app.coreLogger.info('[egg-mongoose] start successfully and server status is ok');
app.coreLogger.info(`[egg-mongoose] instance[${index}] start successfully`);
});
};

function loadModel(app) {
return db;
}

function loadModelToApp(app) {
const dir = path.join(app.config.baseDir, 'app/model');
app.loader.loadToApp(dir, 'model', {
inject: app,
caseStyle: 'upper',
filter(model) {
return (model.modelName && model.base && model.base.Mongoose);
return typeof model === 'function' && model.prototype instanceof app.mongoose.Model;
},
});
}
Expand Up @@ -6,5 +6,5 @@ module.exports = app => {
name: { type: String },
});

return mongoose.model('Book', BoookSchema);
return mongoose.model('Book', BoookSchema, null, { cache: false });
};
Expand Up @@ -6,5 +6,5 @@ module.exports = app => {
name: { type: String },
});

return mongoose.model('User', UserSchema);
return mongoose.model('User', UserSchema, null, { cache: false });
};
6 changes: 6 additions & 0 deletions test/fixtures/apps/mongoose-loadModel/app.js
@@ -0,0 +1,6 @@
'use strict';

module.exports = app => {
app.mymongoose = app.mongooseDB.createInstance(app.config.mymongoose);
app.mongoose.loadModel();
};
11 changes: 11 additions & 0 deletions test/fixtures/apps/mongoose-loadModel/app/model/Book.js
@@ -0,0 +1,11 @@
'use strict';

module.exports = app => {
const mymongoose = app.mymongoose;
const mongoose = app.mongoose;
const BoookSchema = new mongoose.Schema({
name: { type: String },
});

return mymongoose.model('Book', BoookSchema, null, { cache: false });
};
11 changes: 11 additions & 0 deletions test/fixtures/apps/mongoose-loadModel/app/model/user.js
@@ -0,0 +1,11 @@
'use strict';

module.exports = app => {
const mymongoose = app.mymongoose;
const mongoose = app.mongoose;
const UserSchema = new mongoose.Schema({
name: { type: String },
});

return mymongoose.model('User', UserSchema, null, { cache: false });
};
12 changes: 12 additions & 0 deletions test/fixtures/apps/mongoose-loadModel/config/config.default.js
@@ -0,0 +1,12 @@
'use strict';

exports.mongoose = {
loadModel: false,
};

exports.mymongoose = {
url: 'mongodb://127.0.0.1/test',
options: {},
};

exports.keys = 'aaa';
27 changes: 27 additions & 0 deletions test/fixtures/apps/mongoose-multi-defaultDB/app/controller/book.js
@@ -0,0 +1,27 @@
'use strict';

module.exports = app => {
class BookController extends app.Controller {
* create() {
const book = new this.ctx.model.Book({
name: this.ctx.request.body.name,
user: this.ctx.request.body.user,
});
yield book.save();
this.ctx.body = book;
}

* show() {
const id = this.ctx.params.id;
const book = yield this.ctx.model.Book.findById(id)
.populate({
path: 'user',
})
.exec();
this.ctx.body = book;
}
}

return BookController;

};

0 comments on commit 22d134b

Please sign in to comment.