安装配置
1.安装依赖
npm install egg-mongoose --save
2.开启插件
# /config/plugin.tsimport { EggPlugin } from 'egg';const plugin: EggPlugin = {mongoose: {enable: true,package: 'egg-mongoose',},};export default plugin;
3.插件配置
# /config/config.default.tsimport { EggAppConfig, PowerPartial } from 'egg';export default () => {const config = {} as PowerPartial<EggAppConfig>;config.mongoose = {url: process.env.MONGO_URL || 'mongodb://localhost:27017/blog',options: {poolSize: 40,},};return {...config,};};
本项目锁使用的环境:
"egg-mongoose": "3.2.0"node -vv10.5.0
使用
一.创建 Model(模型)
eggjs 在 /app/model/ 下定义数据模型
以我的 blog 项目为例,假设有一个 tag 表、article 表,article 表通过 tag_id 关联 tag 表如下:
1.tag 表
# /app/model/tag.tsmodule.exports = app => {const mongoose = app.mongoose;const Schema = mongoose.Schema;const PostSchema = new Schema({tag_name: {type: String,required: true,},created_time: {type: Date,default: new Date(),},updated_time: {type: Date,default: new Date(),},});return mongoose.model('Tag', PostSchema);};
2.article 表
通过 tag_id 关联 tag 表
# /app/model/article.tsmodule.exports = app => {const mongoose = app.mongoose;const Schema = mongoose.Schema;const PostSchema = new Schema({tag_id: {type: Schema.Types.ObjectId,required: true,ref: 'Tag',},title: {type: String,required: true,},status: {type: Boolean,default: false,},content: {type: String,default: '',},weather: {type: String,default: '',},image: {type: String,default: '',},images: {type: Array,default: [],},pv: {type: Number,default: 0,},created_time: {type: Date,default: new Date(),},updated_time: {type: Date,default: new Date(),},});return mongoose.model('Article', PostSchema);};
更多 schema 使用:mongoose schema
二.简单常用
eggjs 中,一般在 service 层操作 model 层,返回的是一个 promise,so 可以直接用 await 同步编程
我们先定义 search 为查询条件
1.增
新增一篇文章
# article 为对象const article: Article;this.ctx.model.Article.create(article);
批量新增
# article 为数组const article: Array<Article>;this.ctx.model.Article.create(article);
2.删
删除一篇文章
this.ctx.model.Article.deleteOne(search);
删除多篇文章
this.ctx.model.Article.remove(search);
3.查
查找一篇文章
this.ctx.model.Article.findOne(search);
查找多篇
# search 为空 或者空对象返回全部this.ctx.model.Article.find(search);
分页查找 skip、limit
this.ctx.model.Article.find(search).sort({ _id: -1 }) # 按照创建时间倒序.skip(page_size * (current_page - 1)) # 跳过前n个数据.limit(page_size); # 限制n个数据
populate
附带将tag表中的数据也查出来返回
this.ctx.model.Article.find(search).populate("tag_id")
execPopulate 执行填充
// Load just the movie's directorlet movie = await Movie.findOne();movie.director.name; // undefinedmovie.actors[0].name; // undefined// Populate the directorawait movie.populate('director').execPopulate();movie.director.name; // 'James Cameron'movie.actors[0].name; // undefined// Populate the actorsawait movie.populate('actors').execPopulate();movie.director.name; // 'James Cameron'movie.actors[0].name; // 'Arnold Schwarzenegger'movie.actors[1].name; // 'Linda Hamilton'
4.改
替换文章内容
# 注意,是直接全部替换为 new_data# findOneAndUpdate默认返回旧的数据# 若需要部分更新,应使用 $set 操作符return await this.ctx.model.Article.findOneAndUpdate(search, new_data);
返回修改后最新的数据
# 注意第三个参数return await this.ctx.model.Article.findOneAndUpdate(search, new_data, { new: true });
三.操作符
$set
$set 对指定文档中的某些键进行更新,如果这个字段不存在则创建它
# 修改文章 pv为 10000const operation: any = {$set: {pv: 10000,},};return await this.ctx.model.Article.findOneAndUpdate(search, operation);
lt、
lte、$ne
- (>) 大于 $gt
- (<) 小于 $lt
- (>=) 大于等于 $gte
- (<= ) 小于等于 $lte
(!==) 不等于 $ne
# 查pv大于10的文章const search = {pv: {$gt: 1000,},};this.ctx.model.Article.find(search);
and、
nor
$or 满足任一表达式
- $and 同时满足多个表达式
- $not 不满足表达式
- $nor 不满足任一表达式
查找 pv 大于 10 且公开的文章
const search: any = {$and: [{ pv: { $gt: 10 } },{ status: true },],};this.ctx.model.Article.find(search);
$inc
$inc 用来增加和减少已有键的值,只能用于 Number 类型。对于不存在的键,会自动创建相应的键,并且值为给定的值
文章 pv 自增
const operation: any = {$inc: {pv: 1,},};this.ctx.model.Article.findOneAndUpdate(search, operation);
$push、$pull
$push 向已有的数组末尾添加一个元素,如果没有就创建一个新的数组
$pull 会删除掉数组中符合条件的元素
const operation: any = {$push: {images: {content: 'hello world',},},};await this.ctx.model.Article.findOneAndUpdate(search, operation);const operation: any = {$pull: {images: {content: 'hello world',},},};await this.ctx.model.Article.findOneAndUpdate(search, operation);
$in、$nin
- $in 包含
- $nin 不包含
$in 类似与 js Araay includes 方法,与数组中任意一个值相等
查找 pv 在[1,2,3]的文章
const search: any = {pv: {$in: [ 1, 2, 3 ],},};this.ctx.model.Article.find(search);
$type
$type 匹配数据类型
详细的类型请:MongoDB $type 操作符
匹配 status 字段类型为 boolean
const search: any = {status: {$type: 8,},};this.ctx.model.Article.find(search);
$exists
$exists 判断字段是否存在
查找 status 字段存在的文章
const search: any = {status: {$exists: true,},};this.ctx.model.Article.find(search);
$regex
$regex 正则匹配内容
正则匹配内容 123
const search: any = {content: { $regex: '123', $options: 'i' },};this.ctx.model.Article.find(search);
实际上,也可以直接用字面量
const search: any = {content: /123/g,};this.ctx.model.Article.find(search);
$where
$where 类似于 mysql 的 where
查找 pv 大于 20 的文章
const search: any = {$where: 'this.pv>20',};this.ctx.model.Article.find(search);
四.aggregate 聚合
MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的
1.$match
$match 用于过滤数据,只输出符合条件的文档。
this.ctx.model.Article.aggregate([{ $match: search },]);
注:在使用聚合查询进行$match匹配时,如果匹配的是id则要将id字符串进行额外处理,将id字符串转成对应的ObjectId,而转换方法也要注意,只能使用mongoose.Types.ObjectId()否则将查询不出结果
let mongoose = require('mongoose');//{$match: {categoriesId: "5edb465c998ec658dc60c30f"}} //没有数据//{$match: {categoriesId: mongoose.Schema.Types.ObjectId("5edb465c998ec658dc60c30f")}} //也没有数据{$match: {categoriesId: mongoose.Types.ObjectId("5edb465c998ec658dc60c30f")}} //好了
2.$project
$project 用于修改输入文档的结构,可以用来重命名、增加或删除域
修改文档结构,只输出 content 字段
this.ctx.model.Article.aggregate([{ $match: search },{$project: {content: 1,},},]);
3.$limit、$skip
- $limi: 限制返回的文档数
- $skip:跳过指定数量的文档
常用的分页查找
this.ctx.model.Article.aggregate([{ $match: search },{ $skip: page_size * (current_page - 1) },{ $limit: page_size },]);
4.$group
$group 将集合中的文档分组,用于统计结果。类似于 mysql group by
统计每个标签的文章总数 count
this.ctx.model.Article.aggregate([{$group: {_id: '$tag_id',count: {$sum: 1,},},},]);
5.$sort
$sort 将输入文档排序后输出
this.ctx.model.Article.aggregate([{ $match: search },{ $sort: { updated_time: -1 } },]);
6.$lookup、$unwind
- $unwind 将 Array 类型字段拆分成多条,每条包含数组中的一个值
- $lookup mongo3.2 版本新增,用于实现联表查询
有几个参数:
| 语法 | 解释 |
|---|---|
| from | 源数据表 |
| localField | 待 Join 的数据表 |
| foreignField | Join 的数据表的 match 字段 |
| as | 为输出文档的新增值命名 |
查找文章详情,根据 tag_id 查找 tag 表的详情
this.ctx.model.Article.aggregate([{$lookup: {from: 'tags',localField: 'tag_id',foreignField: '_id',as: 'tag_info',},},{ $unwind: '$tag_info' },]);
