Sequelize 查询与分页
· 阅读需 2 分钟
真正让 ORM 见真章的,不是建模,而是列表查询。博客系统看起来简单,但一旦有分类、标签、发布时间、状态和关键词搜索,分页接口很容易被写成一锅粥。
一个稳定的文章列表接口,通常至少要支持这些条件:
- 发布状态
- 分类筛选
- 标签筛选
- 关键字搜索
- 时间倒序
- 分页
先看一个比较常见的写法:
const { Op } = require('sequelize');
async function getPostList(query) {
const page = Math.max(Number(query.page) || 1, 1);
const pageSize = Math.min(Number(query.pageSize) || 10, 50);
const where = {
status: 'published',
};
if (query.keyword) {
where.title = {
[Op.like]: `%${query.keyword.trim()}%`,
};
}
if (query.categoryId) {
where.categoryId = Number(query.categoryId);
}
return Post.findAndCountAll({
where,
limit: pageSize,
offset: (page - 1) * pageSize,
order: [['publishedAt', 'DESC']],
});
}
这段代码足够解决大部分首页和后台列表场景,但要注意三个细节。
1. findAndCountAll 很方便,但不是永远都便宜
它能同时返回总数和当前页数据,适合常规列表页。但如果 include 太多、筛选条件太复杂,它生成的 SQL 会明显变重。这个时候可以把“查总数”和“查数据”拆开。
2. 分页参数一定要设上限
后台经常会有人直接把 pageSize 传成 500。ORM 不会替你兜底,数据库压力会实打实地落下来。对博客这种内容系统,前台列表 10 到 20 条通常就够了。
3. 搜索字段别一开始就铺太宽
很多人喜欢一上来就 title、summary、content 三个字段全文模糊搜索。技术博客文章正文通常很长,这样做对普通关系库并不友好。
更稳的策略是:
- 列表接口只搜标题或摘要
- 正文全文检索后面再交给搜索服务处理
适合博客的分页返回结构
除了数据库查询本身,接口返回结构也很关键。我更习惯统一成下面这种格式:
return {
page,
pageSize,
total: result.count,
list: result.rows,
};
这样前端切换分页组件、无限滚动或者管理后台表格时,适配都比较顺手。
小结
Sequelize 的查询语法并不复杂,但博客列表真正需要的是“边界感”。把筛选条件收敛好、把分页上限卡住、把排序稳定住,内容列表就会非常耐用。
