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

如何在Sequelize中获取hasMany关联的最新单条记录

问题描述

模型关联定义:

KbDevice.hasMany(KbDeviceEvent, { foreignKey: 'kb_device_id', as: 'events' })

当前查询代码:

const allKbDevice = await KbDevice.findAll({
  attributes: ['id', 'make', 'model'],
  include: [
    {
      model: KbDeviceEvent,
      as: 'events',
      attributes: ['id', 'event', 'chamber_id','created_at'],
    },
  ],
});

需求:获取每个KbDevice对应的最新一条KbDeviceEvent记录(基于created_at字段),返回结果中该事件为单个对象而非数组。

当前返回示例:

{
  "id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
  "make": "Toyota",
  "model": "Lexus",
  "events": [
    {
      "id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
      "event": "deposit",
      "chamber_id": 2,
      "created_at": "2022-09-01T22:36:19.849Z"
    }
  ]
}

期望返回示例:

{
  "id": "d75f9f96-a468-4c95-8098-a92411f8e73c",
  "make": "Toyota",
  "model": "Lexus",
  "event": {
    "id": "a737bf7b-2b2e-4817-b351-69db330cb5c7",
    "event": "deposit",
    "chamber_id": 2,
    "created_at": "2022-09-01T22:36:19.849Z"
  }
}

解决方案

方法一:子查询筛选最新事件

直接在include中通过子查询定位每个设备的最新事件,修改as为目标字段名,并限制返回数量:

const allKbDevice = await KbDevice.findAll({
  attributes: ['id', 'make', 'model'],
  include: [
    {
      model: KbDeviceEvent,
      as: 'event',
      attributes: ['id', 'event', 'chamber_id','created_at'],
      where: {
        created_at: {
          [Sequelize.Op.eq]: Sequelize.literal(`(SELECT MAX(created_at) FROM kb_device_events WHERE kb_device_id = KbDevice.id)`)
        }
      },
      required: false, // 允许无事件的设备返回,需保留则设为true
      limit: 1
    }
  ]
});

方法二:定义一对一关联(推荐)

在模型中新增一对一关联,专门映射最新事件,后续查询更简洁:

// 在KbDevice模型中添加关联
KbDevice.hasOne(KbDeviceEvent, {
  foreignKey: 'kb_device_id',
  as: 'event',
  scope: {
    [Sequelize.Op.and]: Sequelize.literal(`created_at = (SELECT MAX(created_at) FROM kb_device_events WHERE kb_device_id = KbDevice.id)`)
  }
});

// 查询代码
const allKbDevice = await KbDevice.findAll({
  attributes: ['id', 'make', 'model'],
  include: [
    {
      model: KbDeviceEvent,
      as: 'event',
      attributes: ['id', 'event', 'chamber_id','created_at'],
      required: false
    }
  ]
});

方法三:查询后结果处理

如果数据库查询方案遇到兼容问题,可先获取全量数据,再通过代码过滤:

const allKbDevice = await KbDevice.findAll({
  attributes: ['id', 'make', 'model'],
  include: [
    {
      model: KbDeviceEvent,
      as: 'events',
      attributes: ['id', 'event', 'chamber_id','created_at'],
      order: [['created_at', 'DESC']] // 按时间倒序,最新事件排在首位
    },
  ],
});

// 转换结果格式
const processedDevices = allKbDevice.map(device => {
  const deviceData = device.toJSON();
  return {
    ...deviceData,
    event: deviceData.events[0] || null,
    events: undefined // 移除原数组字段
  };
});

内容的提问来源于stack exchange,提问作者Riza Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 09:39:22