多表关联时如何获取Lucid Model分页查询的正确计数
解决AdonisJS中多对多关联分组查询的分页计数错误问题
我明白你遇到的问题——当通过leftJoin关联中间表并做聚合统计后,paginate()返回的总条数和实际的Ad数量不符。这是因为Adonis的分页器在处理带groupBy的查询时,默认的count逻辑没有考虑分组:它会直接统计关联后的所有记录数,而不是分组后的Ad条目数(比如一个Ad关联3个Campaign,join后会产生3条记录,count就会算3,但实际你只需要统计1个Ad)。
下面给你几个可行的解决方案:
方案一:手动计算总条数并构建分页结果
这种方法直接绕过分页器的自动count逻辑,自己获取正确的Ad总数,再手动做数据分页:
const Ad = use('App/Models/Ad'); const page = 1; // 目标页码 const perPage = 10; // 每页展示条数 // 第一步:获取符合条件的Ad总数量(直接查询Ad表,不受关联数据影响) const total = await Ad.query() .count('id as total') .first() .then(result => result.total); // 第二步:获取带聚合统计的分页Ad数据 const adsData = await Ad.query() .leftJoin('campaign_ads', 'ads.id', 'campaign_ads.ad_id') .select('ads.*') .sum('campaign_ads.spend as spend') .sum('campaign_ads.sent as sent') .sum('campaign_ads.clicks as clicks') .sum('campaign_ads.leads as leads') .sum('campaign_ads.ftds as ftds') .groupBy('ads.id') .offset((page - 1) * perPage) .limit(perPage) .fetch(); // 第三步:手动构建标准分页返回对象 const paginatedAds = { total, perPage, page, lastPage: Math.ceil(total / perPage), data: adsData };
方案二:使用Adonis的关联聚合(推荐)
如果你用的是Adonis v5+,可以利用模型关联的withAggregate方法,让框架自动处理聚合和分页计数,代码更简洁且不易出错:
首先在Ad模型中定义好多对多关联:
// App/Models/Ad.js class Ad extends Model { campaigns() { return this.belongsToMany('App/Models/Campaign') .pivotTable('campaign_ads') .pivotColumns(['spend', 'sent', 'clicks', 'leads', 'ftds']); } }
然后用withAggregate获取中间表字段的总和,同时正常使用paginate:
const Ad = use('App/Models/Ad'); const paginatedAds = await Ad.query() .withAggregate('campaigns', (query) => { query .sum('spend as spend') .sum('sent as sent') .sum('clicks as clicks') .sum('leads as leads') .sum('ftds as ftds'); }) .paginate(page, perPage);
这种方式下,主查询始终针对ads表,聚合统计通过子查询完成,分页器会正确统计Ad的总数量,不会受关联数据的影响。
方案三:修正分页器的count查询(进阶)
如果你想坚持用原来的leftJoin写法,可以修改分页器的count逻辑,让它统计分组后的唯一Ad ID:
const Ad = use('App/Models/Ad'); const query = Ad.query() .leftJoin('campaign_ads', 'ads.id', 'campaign_ads.ad_id') .select('ads.*') .sum('campaign_ads.spend as spend') .sum('campaign_ads.sent as sent') .sum('campaign_ads.clicks as clicks') .sum('campaign_ads.leads as leads') .sum('campaign_ads.ftds as ftds') .groupBy('ads.id'); // 重写分页的count查询逻辑 query.count = () => { return Ad.query() .leftJoin('campaign_ads', 'ads.id', 'campaign_ads.ad_id') .count('distinct ads.id as total') .first() .then(res => res.total); }; const paginatedAds = await query.paginate(page, perPage);
这个方法通过自定义count函数,让分页器统计distinct ads.id的数量,确保总条数是实际的Ad数量。
内容的提问来源于stack exchange,提问作者JakGuru
相关产品推荐
相关产品推荐

