安装配置

1.安装依赖

  1. npm install egg-mongoose --save

2.开启插件

  1. # /config/plugin.ts
  2. import { EggPlugin } from 'egg';
  3. const plugin: EggPlugin = {
  4. mongoose: {
  5. enable: true,
  6. package: 'egg-mongoose',
  7. },
  8. };
  9. export default plugin;

3.插件配置

  1. # /config/config.default.ts
  2. import { EggAppConfig, PowerPartial } from 'egg';
  3. export default () => {
  4. const config = {} as PowerPartial<EggAppConfig>;
  5. config.mongoose = {
  6. url: process.env.MONGO_URL || 'mongodb://localhost:27017/blog',
  7. options: {
  8. poolSize: 40,
  9. },
  10. };
  11. return {
  12. ...config,
  13. };
  14. };

本项目锁使用的环境:

  1. "egg-mongoose": "3.2.0"
  2. node -v
  3. v10.5.0

使用

一.创建 Model(模型)

eggjs 在 /app/model/ 下定义数据模型
以我的 blog 项目为例,假设有一个 tag 表、article 表,article 表通过 tag_id 关联 tag 表如下:

1.tag 表

  1. # /app/model/tag.ts
  2. module.exports = app => {
  3. const mongoose = app.mongoose;
  4. const Schema = mongoose.Schema;
  5. const PostSchema = new Schema({
  6. tag_name: {
  7. type: String,
  8. required: true,
  9. },
  10. created_time: {
  11. type: Date,
  12. default: new Date(),
  13. },
  14. updated_time: {
  15. type: Date,
  16. default: new Date(),
  17. },
  18. });
  19. return mongoose.model('Tag', PostSchema);
  20. };

2.article 表

通过 tag_id 关联 tag 表

  1. # /app/model/article.ts
  2. module.exports = app => {
  3. const mongoose = app.mongoose;
  4. const Schema = mongoose.Schema;
  5. const PostSchema = new Schema({
  6. tag_id: {
  7. type: Schema.Types.ObjectId,
  8. required: true,
  9. ref: 'Tag',
  10. },
  11. title: {
  12. type: String,
  13. required: true,
  14. },
  15. status: {
  16. type: Boolean,
  17. default: false,
  18. },
  19. content: {
  20. type: String,
  21. default: '',
  22. },
  23. weather: {
  24. type: String,
  25. default: '',
  26. },
  27. image: {
  28. type: String,
  29. default: '',
  30. },
  31. images: {
  32. type: Array,
  33. default: [],
  34. },
  35. pv: {
  36. type: Number,
  37. default: 0,
  38. },
  39. created_time: {
  40. type: Date,
  41. default: new Date(),
  42. },
  43. updated_time: {
  44. type: Date,
  45. default: new Date(),
  46. },
  47. });
  48. return mongoose.model('Article', PostSchema);
  49. };

更多 schema 使用:mongoose schema

二.简单常用

eggjs 中,一般在 service 层操作 model 层,返回的是一个 promise,so 可以直接用 await 同步编程
我们先定义 search 为查询条件

1.增

新增一篇文章

  1. # article 为对象
  2. const article: Article;
  3. this.ctx.model.Article.create(article);

批量新增

  1. # article 为数组
  2. const article: Array<Article>;
  3. this.ctx.model.Article.create(article);

2.删

删除一篇文章

  1. this.ctx.model.Article.deleteOne(search);

删除多篇文章

  1. this.ctx.model.Article.remove(search);

3.查

查找一篇文章

  1. this.ctx.model.Article.findOne(search);

查找多篇

  1. # search 为空 或者空对象返回全部
  2. this.ctx.model.Article.find(search);

分页查找 skip、limit

  1. this.ctx.model.Article.find(search)
  2. .sort({ _id: -1 }) # 按照创建时间倒序
  3. .skip(page_size * (current_page - 1)) # 跳过前n个数据
  4. .limit(page_size); # 限制n个数据

populate

附带将tag表中的数据也查出来返回

  1. this.ctx.model.Article.find(search)
  2. .populate("tag_id")

execPopulate 执行填充

  1. // Load just the movie's director
  2. let movie = await Movie.findOne();
  3. movie.director.name; // undefined
  4. movie.actors[0].name; // undefined
  5. // Populate the director
  6. await movie.populate('director').execPopulate();
  7. movie.director.name; // 'James Cameron'
  8. movie.actors[0].name; // undefined
  9. // Populate the actors
  10. await movie.populate('actors').execPopulate();
  11. movie.director.name; // 'James Cameron'
  12. movie.actors[0].name; // 'Arnold Schwarzenegger'
  13. movie.actors[1].name; // 'Linda Hamilton'

4.改

替换文章内容

  1. # 注意,是直接全部替换为 new_data
  2. # findOneAndUpdate默认返回旧的数据
  3. # 若需要部分更新,应使用 $set 操作符
  4. return await this.ctx.model.Article.findOneAndUpdate(search, new_data);

返回修改后最新的数据

  1. # 注意第三个参数
  2. return await this.ctx.model.Article.findOneAndUpdate(search, new_data, { new: true });

三.操作符

$set

$set 对指定文档中的某些键进行更新,如果这个字段不存在则创建它

  1. # 修改文章 pv为 10000
  2. const operation: any = {
  3. $set: {
  4. pv: 10000,
  5. },
  6. };
  7. return await this.ctx.model.Article.findOneAndUpdate(search, operation);

egg-mongoose使用总结 - 图7lt、egg-mongoose使用总结 - 图8lte、$ne

  • (>) 大于 $gt
  • (<) 小于 $lt
  • (>=) 大于等于 $gte
  • (<= ) 小于等于 $lte
  • (!==) 不等于 $ne

    1. # 查pv大于10的文章
    2. const search = {
    3. pv: {
    4. $gt: 1000,
    5. },
    6. };
    7. this.ctx.model.Article.find(search);

    egg-mongoose使用总结 - 图9and、egg-mongoose使用总结 - 图10nor

  • $or 满足任一表达式

  • $and 同时满足多个表达式
  • $not 不满足表达式
  • $nor 不满足任一表达式

查找 pv 大于 10 且公开的文章

  1. const search: any = {
  2. $and: [
  3. { pv: { $gt: 10 } },
  4. { status: true },
  5. ],
  6. };
  7. this.ctx.model.Article.find(search);

$inc

$inc 用来增加和减少已有键的值,只能用于 Number 类型。对于不存在的键,会自动创建相应的键,并且值为给定的值

文章 pv 自增

  1. const operation: any = {
  2. $inc: {
  3. pv: 1,
  4. },
  5. };
  6. this.ctx.model.Article.findOneAndUpdate(search, operation);

$push、$pull

$push 向已有的数组末尾添加一个元素,如果没有就创建一个新的数组

$pull 会删除掉数组中符合条件的元素

  1. const operation: any = {
  2. $push: {
  3. images: {
  4. content: 'hello world',
  5. },
  6. },
  7. };
  8. await this.ctx.model.Article.findOneAndUpdate(search, operation);
  9. const operation: any = {
  10. $pull: {
  11. images: {
  12. content: 'hello world',
  13. },
  14. },
  15. };
  16. await this.ctx.model.Article.findOneAndUpdate(search, operation);

$in、$nin

  • $in 包含
  • $nin 不包含

    $in 类似与 js Araay includes 方法,与数组中任意一个值相等

查找 pv 在[1,2,3]的文章

  1. const search: any = {
  2. pv: {
  3. $in: [ 1, 2, 3 ],
  4. },
  5. };
  6. this.ctx.model.Article.find(search);

$type

$type 匹配数据类型

详细的类型请:MongoDB $type 操作符
匹配 status 字段类型为 boolean

  1. const search: any = {
  2. status: {
  3. $type: 8,
  4. },
  5. };
  6. this.ctx.model.Article.find(search);

$exists

$exists 判断字段是否存在

查找 status 字段存在的文章

  1. const search: any = {
  2. status: {
  3. $exists: true,
  4. },
  5. };
  6. this.ctx.model.Article.find(search);

$regex

$regex 正则匹配内容

正则匹配内容 123

  1. const search: any = {
  2. content: { $regex: '123', $options: 'i' },
  3. };
  4. this.ctx.model.Article.find(search);

实际上,也可以直接用字面量

  1. const search: any = {
  2. content: /123/g,
  3. };
  4. this.ctx.model.Article.find(search);

$where

$where 类似于 mysql 的 where

查找 pv 大于 20 的文章

  1. const search: any = {
  2. $where: 'this.pv>20',
  3. };
  4. this.ctx.model.Article.find(search);

四.aggregate 聚合

MongoDB 的聚合管道将 MongoDB 文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的

常用的管道操作符:

1.$match

$match 用于过滤数据,只输出符合条件的文档。

  1. this.ctx.model.Article.aggregate([
  2. { $match: search },
  3. ]);

注:在使用聚合查询进行$match匹配时,如果匹配的是id则要将id字符串进行额外处理,将id字符串转成对应的ObjectId,而转换方法也要注意,只能使用mongoose.Types.ObjectId()否则将查询不出结果

  1. let mongoose = require('mongoose');
  2. //{$match: {categoriesId: "5edb465c998ec658dc60c30f"}} //没有数据
  3. //{$match: {categoriesId: mongoose.Schema.Types.ObjectId("5edb465c998ec658dc60c30f")}} //也没有数据
  4. {$match: {categoriesId: mongoose.Types.ObjectId("5edb465c998ec658dc60c30f")}} //好了

2.$project

$project 用于修改输入文档的结构,可以用来重命名、增加或删除域

修改文档结构,只输出 content 字段

  1. this.ctx.model.Article.aggregate([
  2. { $match: search },
  3. {
  4. $project: {
  5. content: 1,
  6. },
  7. },
  8. ]);

3.$limit、$skip

  • $limi: 限制返回的文档数
  • $skip:跳过指定数量的文档

常用的分页查找

  1. this.ctx.model.Article.aggregate([
  2. { $match: search },
  3. { $skip: page_size * (current_page - 1) },
  4. { $limit: page_size },
  5. ]);

4.$group

$group 将集合中的文档分组,用于统计结果。类似于 mysql group by

统计每个标签的文章总数 count

  1. this.ctx.model.Article.aggregate([
  2. {
  3. $group: {
  4. _id: '$tag_id',
  5. count: {
  6. $sum: 1,
  7. },
  8. },
  9. },
  10. ]);

5.$sort

$sort 将输入文档排序后输出

  1. this.ctx.model.Article.aggregate([
  2. { $match: search },
  3. { $sort: { updated_time: -1 } },
  4. ]);

6.$lookup、$unwind

  • $unwind 将 Array 类型字段拆分成多条,每条包含数组中的一个值
  • $lookup mongo3.2 版本新增,用于实现联表查询

有几个参数:

语法 解释
from 源数据表
localField 待 Join 的数据表
foreignField Join 的数据表的 match 字段
as 为输出文档的新增值命名

查找文章详情,根据 tag_id 查找 tag 表的详情

  1. this.ctx.model.Article.aggregate([
  2. {
  3. $lookup: {
  4. from: 'tags',
  5. localField: 'tag_id',
  6. foreignField: '_id',
  7. as: 'tag_info',
  8. },
  9. },
  10. { $unwind: '$tag_info' },
  11. ]);