如何用Bookshelf.js实现关联表分页:限制主模型与关联子模型数量
解决Bookshelf.js中主模型分页同时限制每个关联子模型数量的问题
你遇到的这个坑我之前也踩过!问题出在Bookshelf处理关联查询的逻辑上:你在withRelated里加的qb.limit(10)是全局限制所有关联书籍的总数量,而不是给每个作者单独限制10本。所以数据库会先把10个作者的所有关联书籍查出来,然后只取前10条,再把这些书分配给对应的作者,自然就出现了有的作者分到几本、有的空着的情况。
下面给你三种可行的解决方案,按需选择:
1. 用Knex窗口函数(性能最优,推荐)
利用SQL的窗口函数ROW_NUMBER()给每个作者的书籍分组编号,然后只保留每个组的前10本。通过Bookshelf的query()方法可以直接嵌入这个复杂逻辑:
Author.where('postedBy', userId) .fetchPage({ pageSize: 10, page: 1, withRelated: [ { 'books': function(qb) { qb.select('books.*') .from(function() { // 先给每个作者的书籍排序并编号 this.select( 'books.*', knex.raw('ROW_NUMBER() OVER (PARTITION BY books.author_id ORDER BY books.id DESC) as row_num') ) .from('books') .as('ranked_books'); }) // 只保留每个作者的前10本 .where('row_num', '<=', 10); } } ] });
注意把ORDER BY books.id DESC换成你实际需要的排序规则(比如按出版时间排序),这样每个作者的书籍就是你想要的顺序,且最多10本。
2. 在模型关联中固定限制(适合数量不变的场景)
如果每个作者关联的书籍数量固定是10,直接在Author模型的关联定义里加限制更省事:
// Author模型的关联定义 books() { // 这里可以加排序和固定的limit return this.hasMany(Book).orderBy('id', 'desc').limit(10); }
之后查询时就不用在withRelated里额外处理了:
Author.where('postedBy', userId) .fetchPage({ pageSize: 10, page: 1, withRelated: ['books'] });
缺点是这个限制是硬编码的,没法在每次查询时动态调整数量。
3. 手动加载关联(逻辑最直观,适合小数据量)
先分页查出10个作者,再逐个给每个作者加载前10本书籍。虽然要多做几次查询,但逻辑简单易懂,调试也方便:
// 第一步:分页获取10个作者 const authors = await Author.where('postedBy', userId) .fetchPage({ pageSize: 10, page: 1 }); // 第二步:批量给每个作者加载前10本books await Promise.all(authors.models.map(async (author) => { const books = await author.related('books') .query(qb => qb.limit(10).orderBy('id', 'desc')) .fetch(); author.set('books', books); })); // 现在每个作者都带了最多10本books console.log(authors.toJSON());
这种方法适合作者数量不多的场景(比如你这里的10个),如果一次查几百个作者,多次查询的性能开销会比较明显。
内容的提问来源于stack exchange,提问作者Egor
相关产品推荐
相关产品推荐

