沐光

记录在前端之路的点点滴滴

typeorm 操作 mongo 的方法小记

1. 前言

此篇文章用于记录使用 Typeorm 操作 MongoDB 的方法,方便后续再次使用时有记录总结,防止下次碰到类似问题时有得费心思查询思路。

  • mongodb 版本3.7.3
  • typeorm 版本0.2.45
  • @types/mongodb3.6

2. 了解 ObjectID

特性:

  • 总体上为递增的顺序;
  • 可以通过 ObjectID 获取时间信息;
  • JS 比对 ObjectID 时需要调用它的 toString 方法;

关于 ObjectId 的问题总结

3. 操作方法

3.1 基础查询

常用的 mongo 的操作符 含义
$in & $nin 数组匹配($in:匹配在数组内的; $nin:匹配不在数组内的)
$or & $nor 或操作符($or:匹配满足任一条件的; $nor:匹配任一条件均不满足的)
$eq & $ne 相等匹配($eq:等于; $ne:不等于)
$gt & $lt 大小匹配($gt:大于; $lt:小于)
$not 非操作符(需要配合别的操作符使用)
$and 与操作符($and:匹配满足所有条件的)
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
41
42
// $in 示例
const demoArr = ['aaa', 'bbb'];
repo.find({
where: {
name: { $in: demoArr },
},
});

// $or 示例($nor、$and、$nin 同理)
const demoReg = new RegExp('some fuzzy str');
repo.find({
where: {
$or: [{ name: demoReg }, { address: demoReg }],
},
});

// $eq 示例($ne 同理)
repo.find({
where: {
name: {
$eq: '测试',
},
},
});

// $gt 示例($lt 同理)
repo.find({
where: {
age: {
$gt: 12,
},
},
});

// $not 示例
repo.find({
where: {
age: {
$not: { $gt: 12 },
},
},
});

当然,typeorm 也为我们简化了一部分的操作写法,特别是简单的匹配查询时,我们可以直接这么写:

1
2
3
4
5
6
7
8
// 查询多条数据(返回数组)
repo.find({
id: new ObjectID('tar hex str'),
name: '123',
});

// 查询单条数据
repo.findOne({ id: new ObjectID('tar hex str') });

⚠️ 注:批量查询数据,需要根据 ObjectID 查询的时候,需要 new ObjectID(str) 来生成对应的类型才可查询,可能和版本问题有关。

3.2 级联查询

这里的“级联查询”包括两部分的操作:“单表聚合”、“多表联查”。

3.2.1 单表聚合

⚠️ 注:表格内容引自后面 “参考文章 - 6”

常用管道 含义
$group 将 collection 中的 document 分组,可用于统计结果
$match 过滤数据,只输出符合结果的文档
$project 修改输入文档的结构(例如重命名,增加、删除字段,创建结算结果等)
$sort 将结果进行排序后输出
$limit 限制管道输出的结果个数
$skip 跳过制定数量的结果,并且返回剩下的结果
$unwind 将数组类型的字段进行拆分
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 一般操作方法
const result = await repo
.aggregate([
// 先匹配需要的内容
{ $match: { id: new ObjectID(xxx) } },
// 然后聚合
{
$group: {
// 通过 id 聚合
id: '$id',
// 自定义 content 内容
content: {
// 批量取值并重命名
$push: {
name: '$name',
new_age: '$age',
},
},
},
},
// 其它控制……
])
.next();

3.2.2 多表联查

多表联查我们需要用到的是 $lookup 操作符,此操作符内容如下:

  • from:目标表
  • localField:当前表的匹配字段
  • foreignField:目标表的匹配字段
  • as:输出的集合名称

⚠️ 注:这里的 localFieldforeignField 就是两表关联的外键,注意别填反了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 示例
const data = await this.user
.aggregate([
{ $match: { name: 'wahaha' } },
{
$lookup: {
from: 'photo',
localField: 'id',
foreignField: 'user_id',
as: 'photo_list',
},
},
{
$lookup: {
from: 'family',
localField: 'id',
foreignField: 'user_id',
as: 'families',
},
},
])
.next();

3.3 新增操作

使用 Typeorm 操作 mongodb 的新增能力有三种方法:saveinsertOneinsertMany,一般我们使用 save 方法。它们之间稍微有一些区别:

3.3.1 save 方法

save 方法比较简单,直接将新增参数放入即可,支持自动生成 主键新增时间更新时间,它同时支持插入单条数据 & 多条数据。

1
2
3
4
5
6
7
8
9
10
11
// 插入单条数据
this.repo.save({
// 新增的参数
});

// 批量插入数据
this.repo.save([
{
// 新增的参数
},
]);

3.3.2 insertOne 方法

insertOne 方法为 save 的单条数据操作版,但它只支持自动生成 主键,在项目使用上可优先使用 save 方法。

1
2
3
this.repo.insertOne({
// 新增的参数
});

3.3.3 insertMany 方法

insertMany 方法为 save 的多条数据操作版,但它只支持自动生成 主键,在项目使用上可优先使用 save 方法。

1
2
3
4
5
this.repo.insertMany([
{
// 新增的参数
},
]);

3.4 删除操作

删除方法基本都使用 delete 方法,使用方法基本按照 TS 定义去操作即可:

1
2
// 可单、批量删除
repo.delete(xxx);

3.5 更新操作

更新操作也有多种:updateupdateOneupdateMany,基本和 find 差不多,用的较多的基本是 update 方法,属于综合性更新。

1
2
3
4
repo.update(tarId, {
modify_time: new Date(),
// 待更新的内容
});

⚠️ 注:typeorm 更新 mongodb 时不会自动更新时间,需要手动补充时间的更新内容。

4. 参考文档

  1. MongoDB $not 实例讲解
  2. 对多个字段同时查询
  3. typeorm mongodb
  4. TypeORM FindById doesn’t work with MongoDB
  5. MongoDB 中的 aggregate() 方法
  6. mongoDB 中聚合(aggregate)的具体使用
  7. 详解 MongoDB 中的多表关联查询($lookup)