Mongoose在egg中的运用

Mongoose是什么?

Mongoose是MongoDB的一个对象模型工具,封装了许多MongoDB对文档的的增删改查等常用方法,让NodeJS操作Mongodb数据库变得更加灵活简单。
###在egg项目中如何使用?

1、安装

npm i egg-mongoose --save

2、配置

在根目录下的/config/plugin.js中配置插件

1
2
3
4
exports.mongoose = {
enable: true,
package: 'egg-mongoose',
};

3、连接数据库

在根目录下的/config/config.default.js增加配置,其中url为我们的数据库地址,可通过环境变量来区分开发环境还是生产环境,并且确定是否使用用户名密码的数据库
const prod = process.env.npm_config_server_prod;

1
2
3
4
5
6
7
8
mongoose: {
client: {
url: prod ? 'mongodb:eggadmin:123456@localhost:27017/DbName' : 'mongodb://127.0.0.1:27017/DbName',
options: {
useUnifiedTopology: true,
},
},
},

4、配置与使用

(1)数据表配置

在app目录下新建model文件夹,在model文件夹下新建JS文件作为数据表的配置内容,下面以书籍表的配置为例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
'use strict';

/**
* @description: Mongoose book Schema,
*/

module.exports = app => {
const mongoose = app.mongoose;
const Schema = mongoose.Schema;
const BookSchema = new Schema({
desc: { type: String }, /* 书籍描述 */
name: { type: String }, /* 书籍名称 */
press: { type: String }, /* 出版社 */
author: { type: String }, /* 作者 */
image: { type: Array }, /* 书籍图片列表*/
price: { type: String }, /* 价格 */
book_type: { /* 书籍分类id */
type: Schema.Types.ObjectId,
ref: 'BookClassify',
},
user: { /* 书籍发布者id */
type: Schema.Types.ObjectId,
ref: 'User',
},
create_time: { type: String }, /* 创建时间 */
status: { type: String }, /* 状态,1:待购买,2:已购买*/
look: { type: Number } /* 浏览数量 */
});
return mongoose.model('Book', BookSchema);
};

可以看到我们可以通过Schema来定义表结构,可以指定字段的类型及关联,设置完字段后就可以生成model了,这里算是非常简单的配置,更多配置方法可参考文档

(2)、使用mongoose方法

配置完数据表结构后,我们就可以再service层中调用mongoose的方法对文档进行增删查改了,已书籍列表的处理逻辑为例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
async findbookList(data) {
const { type, page, pageSize, desc, status, userId } = data;
const searchVal = {}
if (type) {
searchVal.book_type = mongoose.Types.ObjectId(type)
}
if (status) {
searchVal.status = status
}
if (userId) {
searchVal.user = mongoose.Types.ObjectId(userId)
}
const search_term = {
$or: [
{ desc: { $regex: desc ? desc : '', $options: '$i' } },
{ name: { $regex: desc ? desc : '', $options: '$i' } },
{ author: { $regex: desc ? desc : '', $options: '$i' } },
{ press: { $regex: desc ? desc : '', $options: '$i' } },
],
};
const totalNum = await this.ctx.model.Book.find(searchVal).and(search_term).countDocuments();
const result = await this.ctx.model.Book.find(searchVal)
.populate({
path: 'user',
select: { name: 1, image: 1 }
})
.populate({
path: 'book_type'
})
.and(search_term)
.sort({ create_time: -1 })
.skip((parseInt(page) - 1) * parseInt(pageSize))
.limit(parseInt(pageSize));
return result ? { bean: {
records: result,
current: page,
size: result.length,
total: totalNum,
}, ...app.config.msg.GET_SUCCESS } : app.config.msg.GET_ERR;
}

可以看到,通过this.ctx.model.Book就可以获取到Book的model并且可以调用mongoose需要的方法,例如populate、find、and、sort、skip、limit 等等。

5、egg-Mongoose常用的方法

增加数据
1
this.ctx.model.Book.create(data,callback);

其中data为json数据结构,callback为操作后的回调函数

查询数据
获取所有数据,返回是一个数组
1
this.ctx.model.Book.find()
获取一个数据,返回是一个对象
1
this.ctx.model.Book.findOne()
条件查询
1
this.ctx.model.Article.find(conditions,callback);

其中conditions为查询的条件,callback为回调函数
conditions有一下几种情况:

具体数据:

1
this.ctx.model.Book.find({_id5c4a19fb87ba4002a47ac4d, name: "射雕英雄传" }, callback);

条件查询:

1
2
3
4
5
6
7
"$lt"	小于
"$lte" 小于等于
"$gt" 大于
"$gte" 大于等于
"$ne" 不等于
// 查询价格大于100小于200的书籍数组
this.ctx.model.Book.find({ "price": { $get:100 , $lte:200 });

或查询 OR

1
2
3
4
5
6
"$in" 一个键对应多个值
"$nin" 同上取反, 一个键不对应指定值
"$or" 多个条件匹配, 可以嵌套 $in 使用
"$not" 同上取反, 查询与特定模式不匹配的文档

this.ctx.model.Book.find({"name":{ $in: ["射雕","倚天"]} );
删除数据
1
this.ctx.model.Book.remove(conditions,callback);
更新数据
1
this.ctx.model.Book.update(conditions, update, callback)

conditions为条件,update是更新的值对象

排序
1
this.ctx.model.Book.sort({ create_time: -1 });

其中-1表示降序返回。 1表示升序返回

限制数量
1
this.ctx.model.Book.limit(number);

number表示限制的个数

跳过文档返回
1
this.ctx.model.Book.skip(number);

number表示跳过的个数,skip经常搭配limit实现分页的功能

条件数组and

在find后面可使用and对查询结果进行进一步条件筛选,相当于并且的意思。

1
2
3
4
5
6
7
8
9
const search_term = {
$or: [
{ desc: { $regex: desc ? desc : '', $options: '$i' } },
{ name: { $regex: desc ? desc : '', $options: '$i' } },
{ author: { $regex: desc ? desc : '', $options: '$i' } },
{ press: { $regex: desc ? desc : '', $options: '$i' } },
],
};
this.ctx.model.Book.find().and(search_term)
关联查询populate
1
2
3
4
5
6
7
8
9
10
11
12
// 在model中配置字段时候指定关联的表名,就可以通过populate来进行表的关联查询
user: { /* 书籍发布者id */
type: Schema.Types.ObjectId,
ref: 'User',
},


this.ctx.model.Book.find()
.populate({
path: 'user',
select: { name: 1, image: 1 }
})
聚合管道Aggregate
1
2
3
4
5
this.ctx.model.Template.aggregate([
{ $match: { name } },
{ $sort: { create_time: -1 } },
{ $group: { _id: '$name', user_id: { $first: '$modifier' } } },
]);

Mongoose聚合管道aggregate常用的操作有$project 、$match 、$group、$sort、$limit、$skip、$lookup 表关联

批量操作bulkWrite

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
const template_list = await ctx.model.Template.aggregate([
{ $sort: { create_time: -1 } },
{ $group: { _id: '$name', template_id: { $first: '$_id' }, label: { $first: '$label' } } },
]);
const update_value = [];
template_list.forEach(item => {
if (!item.label) {
update_value.push({
updateOne: {
filter: { _id: item.template_id },
update: { label: '' },
},
});
}
});
await ctx.model.Template.bulkWrite(update_value);

可以进行一系列批量增加、删除、更新等操作。

mongoose还有非常多的方法可以提供给我的灵活使用,我们在使用的时候可以结合业务逻辑选择合适的方法来提高我们操作数据库的效率。在我们使用它之前可以认真的阅读官方文档