MIKRO-ORM:如何高效处理帖子的投票统计?
高效获取帖子总投票数的最优方案
嘿,这个问题我太熟了!你之前用序列化器的思路确实会加载所有投票记录,数据量大的时候性能拉胯,完全没必要。下面给你梳理几种从优到次的方案,直接解决痛点:
1. 首选:用@Formula让数据库自动计算(最简洁+最高效)
直接在Post实体里加一个计算字段,让数据库帮你完成统计,查询帖子时自动带出总票数,不用额外操作,性能拉满:
@Entity() export class Post extends Node<Post> { // ... 其他字段保持不变 // 新增Formula字段,自动计算Up-Down的总票数 @Formula(`( SELECT COALESCE(SUM(CASE WHEN pv.vote = 'Up' THEN 1 WHEN pv.vote = 'Down' THEN -1 ELSE 0 END), 0) FROM post_votes pv WHERE pv.post_id = id )`) voteCount: number; @OneToMany(() => PostVotes, (pv) => pv.post) votes = new Collection<PostVotes>(this); }
这样你查询帖子的时候,直接就能拿到voteCount:
const post = await em.findOne(Post, postId); console.log(post.voteCount); // 直接得到总票数,不用加载任何投票记录
为什么这个最优?
- 数据库原生做聚合计算,比JS在内存里过滤高效N倍
- 只返回一个数字,传输数据量极小
- 代码最简洁,不用写额外的查询逻辑
如果需要单独统计Up/Down票数,也可以加两个@Formula:
@Formula(`(SELECT COUNT(*) FROM post_votes pv WHERE pv.post_id = id AND pv.vote = 'Up')`) upVoteCount: number; @Formula(`(SELECT COUNT(*) FROM post_votes pv WHERE pv.post_id = id AND pv.vote = 'Down')`) downVoteCount: number;
2. 备选:用Query Builder手动构建聚合查询
如果需要更灵活的查询逻辑(比如同时过滤帖子条件+统计票数),可以用MikroORM的Query Builder直接写聚合:
const postWithVote = await em.createQueryBuilder(Post, 'p') .select([ 'p.*', `COALESCE(SUM(CASE WHEN pv.vote = 'Up' THEN 1 WHEN pv.vote = 'Down' THEN -1 ELSE 0 END), 0) AS voteCount` ]) .leftJoin('p.votes', 'pv') .where('p.id = :id', { id: postId }) .groupBy('p.id') .getSingleResult(); console.log(postWithVote.voteCount);
这种方式适合复杂场景,比如批量查询多个帖子并统计各自的票数,比循环查每个帖子高效得多。
3. 避坑:不要用序列化器或加载所有投票记录
你之前尝试的序列化器方式,本质是把所有PostVotes加载到内存后再用JS过滤计算,当帖子的投票数上千甚至上万时,内存占用和响应时间都会爆炸,完全不推荐。
4. 不推荐:单独执行两次count()查询
比如先查Up的数量,再查Down的数量,然后相减:
const upCount = await em.count(PostVotes, { post: postId, vote: PostVote.Up }); const downCount = await em.count(PostVotes, { post: postId, vote: PostVote.Down }); const total = upCount - downCount;
这种方式虽然比序列化器好,但需要两次数据库查询,效率不如一次聚合查询,除非你的业务有特殊需求需要单独拿到Up/Down的数量,否则没必要。
总结下来,@Formula是最省心高效的方案,直接让数据库帮你搞定统计,代码简洁还性能拉满~
内容的提问来源于stack exchange,提问作者jags
相关产品推荐
相关产品推荐

