You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Objection.js中统计并分组答案的SO式上下投票

用Objection.js实现按投票类型统计答案投票数

模型关系前提

先确认你的Objection模型已正确定义关联:

  • Answer模型:hasMany(Vote, { foreignKey: 'post_id' })
  • Vote模型:belongsTo(VoteType)

方案1:使用查询构建器实现

直接用Objection的查询构建器复刻原生SQL逻辑,再转换为你要的嵌套格式:

const knex = Answer.knex(); // 获取实例

// 执行分组查询
const rawGroups = await Answer.query()
  .select(
    'answer_details',
    'vt.vote_type',
    knex.raw('COUNT(*) as vote_count')
  )
  .joinRelated('votes.vote_type') // 关联投票和投票类型
  .alias({
    votes: 'v',
    'votes.vote_type': 'vt'
  })
  .groupBy('answer_details', 'vt.vote_type');

// 转换为预期的嵌套格式
const formattedAnswers = rawGroups.reduce((acc, row) => {
  const targetAnswer = acc.find(item => item.answer_details === row.answer_details);
  if (targetAnswer) {
    targetAnswer.votes.push({ [row.vote_type]: row.vote_count });
  } else {
    acc.push({
      answer_details: row.answer_details,
      votes: [{ [row.vote_type]: row.vote_count }]
    });
  }
  return acc;
}, []);

// 最终结果
const output = { answers: formattedAnswers };

方案2:直接执行原生SQL

如果更习惯原生SQL写法,可以用Objection的原生查询功能,再处理结果格式:

const rawResults = await Answer.knex().raw(`
  SELECT vt.vote_type, a.answer_details, COUNT(*) as vote_count
  FROM answers a
  JOIN votes v ON v.post_id = a.id
  JOIN vote_types vt ON vt.id = v.vote_type_id
  GROUP BY vt.vote_type, a.answer_details
`);

// 格式转换逻辑和方案1一致
const formattedAnswers = rawResults.rows.reduce((acc, row) => {
  const targetAnswer = acc.find(item => item.answer_details === row.answer_details);
  if (targetAnswer) {
    targetAnswer.votes.push({ [row.vote_type]: row.vote_count });
  } else {
    acc.push({
      answer_details: row.answer_details,
      votes: [{ [row.vote_type]: row.vote_count }]
    });
  }
  return acc;
}, []);

const output = { answers: formattedAnswers };

内容的提问来源于stack exchange,提问作者Roger Satsi Dankudi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.30 18:35:55