Sequelize - 如何提取具有 1 个关联记录的记录并按关联记录属性排序
Posted
技术标签:
【中文标题】Sequelize - 如何提取具有 1 个关联记录的记录并按关联记录属性排序【英文标题】:Sequelize - How to pull records with 1 associated record and order by associated record attribute 【发布时间】:2019-11-26 15:08:45 【问题描述】:我有一个 Convos 表,里面有很多消息。
我想要什么:拉出所有 convos 和最后一条消息。通过last_message.created_at
订购车队
models.Convos.findAll(
include: [
model: models.Messages,
as: "last_message",
order: [ [ 'created_at', 'DESC' ]],
limit: 1,
],
where:
[Op.or]: [
sender_id: req.decoded.id
,
recipient_id: req.decoded.id
],
,
)
我最接近订购的是:
order: [
[model: models.Messages, as: 'last_message', 'created_at', 'DESC'],
],
但这给出了错误:
`Unhandled rejection SequelizeDatabaseError: missing FROM-clause entry for table "last_message"`
从这里,我猜这个错误可能暗示有没有任何消息的 convos,使 last_message.created_at
未定义(虽然我可能完全误解了这个错误)。
因此,从那里开始,我一直在尝试在 where
语句中添加一个子句,该子句只提取至少有 1 条消息的 convos。这是我尝试过的一堆东西,它们都抛出了错误:
添加到哪里:
Sequelize.literal("`last_message`.`id` IS NOT NULL")
'$models.Messages.id$': [Op.ne]: null ,
Sequelize.fn("COUNT", Sequelize.col("last_message")): [Op.gt]: 0
'$last_message.id$': [Op.ne]: null
'$last_message.id$':
[Op.ne]: null
我也尝试过having
而不是where
声明:
having: Sequelize.where(Sequelize.fn('COUNT', Sequelize.col('$last_message.id$')), '>=', 0)
如何按关联记录 last_message.created_at
对 convos 进行正确排序?
更新 - CONVOS 模型的相关部分
"use strict";
module.exports = (sequelize, DataTypes) =>
let Convos = sequelize.define(
"Convos",
sender_id:
type: DataTypes.INTEGER,
references:
model: "Users",
key: "id"
,
recipient_id:
type: DataTypes.INTEGER,
references:
model: "Users",
key: "id"
,
created_at: DataTypes.DATE,
updated_at: DataTypes.DATE
,
timestamps: false,
freezeTableName: true,
schema: "public",
tableName: "convos"
);
Convos.associate = models =>
Convos.hasMany(models.Messages,
as: "last_message",
foreignKey: "convo_id",
sourceKey: "id"
);
;
return Convos;
;
更新
当关联模型具有limit
时,我发现问题在于使用 Sequelize.literal。例如,这有效:
models.Convos.findAll(
include: [
model: models.Messages,
as: "last_message",
order: [ [ 'created_at', 'DESC' ]],
//limit: 1,
required: true,
duplicating: false,
,
],
where:
[Op.or]: [
sender_id: req.decoded.id
,
recipient_id: req.decoded.id
],
,
order: [[Sequelize.literal(`last_message.created_at`), 'DESC']],
offset: offset,
limit: 10,
).then(convos => ....
但是当我取消注释包含部分中的 limit: 1
时,我收到错误消息:
Unhandled rejection SequelizeDatabaseError: missing FROM-clause entry for table "last_message"
这里是没有limit 1
的查询日志:
Executing (default): SELECT "Convos"."id", "Convos"."sender_id", "Convos"."recipient_id", "Convos"."created_at", "Convos"."updated_at", "last_message"."id" AS "last_message.id", "last_message"."body" AS "last_message.body", "last_message"."read" AS "last_message.read", "last_message"."group_meeting_id" AS "last_message.group_meeting_id", "last_message"."user_id" AS "last_message.user_id", "last_message"."created_at" AS "last_message.created_at", "last_message"."updated_at" AS "last_message.updated_at", "last_message"."convo_id" AS "last_message.convo_id", "last_message->user"."id" AS "last_message.user.id", "last_message->user"."first_name" AS "last_message.user.first_name", "last_message->user"."avatar_file_name" AS "last_message.user.avatar_file_name", "senderUser"."id" AS "senderUser.id", "senderUser"."first_name" AS "senderUser.first_name", "senderUser"."avatar_file_name" AS "senderUser.avatar_file_name", "recipientUser"."id" AS "recipientUser.id", "recipientUser"."first_name" AS "recipientUser.first_name", "recipientUser"."avatar_file_name" AS "recipientUser.avatar_file_name" FROM "public"."convos" AS "Convos" INNER JOIN "public"."msgs" AS "last_message" ON "Convos"."id" = "last_message"."convo_id" LEFT OUTER JOIN "public"."users" AS "last_message->user" ON "last_message"."user_id" = "last_message->user"."id" LEFT OUTER JOIN "public"."users" AS "senderUser" ON "Convos"."sender_id" = "senderUser"."id" LEFT OUTER JOIN "public"."users" AS "recipientUser" ON "Convos"."recipient_id" = "recipientUser"."id" WHERE ("Convos"."sender_id" = 32 OR "Convos"."recipient_id" = 32) ORDER BY last_message.created_at DESC LIMIT 10 OFFSET 70;
使用limit: 1
查询:
Executing (default): SELECT "Convos"."id", "Convos"."sender_id", "Convos"."recipient_id", "Convos"."created_at", "Convos"."updated_at", "senderUser"."id" AS "senderUser.id", "senderUser"."first_name" AS "senderUser.first_name", "senderUser"."avatar_file_name" AS "senderUser.avatar_file_name", "recipientUser"."id" AS "recipientUser.id", "recipientUser"."first_name" AS "recipientUser.first_name", "recipientUser"."avatar_file_name" AS "recipientUser.avatar_file_name" FROM "public"."convos" AS "Convos" LEFT OUTER JOIN "public"."users" AS "senderUser" ON "Convos"."sender_id" = "senderUser"."id" LEFT OUTER JOIN "public"."users" AS "recipientUser" ON "Convos"."recipient_id" = "recipientUser"."id" WHERE ("Convos"."sender_id" = 32 OR "Convos"."recipient_id" = 32) ORDER BY last_message.created_at DESC LIMIT 10 OFFSET 0;
这里有一些链接有助于理解 limit
导致问题,但我仍然没有找到解决这个问题的解决方案。
Link 1
Link 2
Link 3
Link 4
谢谢!
【问题讨论】:
你知道如何在不使用 sequelize.js 的情况下获得想要的结果吗?仅仅通过使用 SQL? @madflow 喜欢纯 SQL 吗?我试图用Sequelize.literal("
last_message.
id` IS NOT NULL")` 写一些东西,简短的回答是否定的——否则我不会发布问题哈哈 :) 我理解这个概念,但我正在努力获取正确的语法。
那么 - 请发布您的表结构和数据库引擎。锦上添花:创建一个小提琴:db-fiddle.com。从那里开始 - 将其转换为 sequalize.js 生态系统应该很容易。
制作小提琴没有意义 - 我不知道如何写这个,这就是问题所在。 db 关系已经说明 - convo 有很多消息。这个问题不需要整个表结构,更多的是关于order
和where
语句的sequelize 语法的问题。
在你说having
而不是where
的最后一点,不应该是>0
(不是>=
) - (Sequelize.fn('COUNT', Sequelize. col('$last_message.id$')), '>', 0)
【参考方案1】:
免责声明:我不知道续集。并且 - 有三个记录版本,您没有说明您使用的是哪一个。
我想要什么:拉出所有 convos 和最后一条消息。按以下顺序订购车队 last_message.created_at
我可以提供 SQL (Postgres) 解决方案。
我假设有一个 convos 表(类似):
CREATE TABLE convos (
id INT PRIMARY KEY,
sender_id INT,
recipient_id INT
);
还有一个消息表
CREATE TABLE messages (
id INT PRIMARY KEY,
convo_id INT REFERENCES convos (id),
created_at timestamp without time zone
);
还有一些测试数据:
INSERT INTO
convos (id, sender_id, recipient_id)
VALUES (1,1,1), (2,2,2), (3,3,4);
INSERT INTO
messages (id, convo_id, created_at)
VALUES
(1,1, '1990-07-24'),
(2,1, '2019-07-24'),
(3,2, '1990-07-24'),
(4,2, '2019-07-24'),
(5,3, null);
当您要查询convos
表并从messages
表中获取最新消息时,您必须在子查询(或CTE,或横向连接,...)中使用messages
的结果.
例如:
SELECT
convos.sender_id,
convos.recipient_id,
messages.last_message_date
FROM convos
LEFT JOIN
(
SELECT convo_id, max(created_at) as last_message_date
FROM messages
GROUP BY convo_id
) messages ON convos.id=messages.convo_id
ORDER BY messages.last_message_date DESC
所以对于 sequelize.js - 您必须了解它如何使用关联模型进行子查询并使用结果。
【讨论】:
嗨 - 感谢您的尝试。您是否阅读了我的帖子中的更新部分?它显示了一个有效的查询,但如果我尝试限制为仅 1 条消息,则会引发错误。我正在使用 sequelize.js。同样 - 我将 convos 模型和关联放在问题中。 是的 - 我已阅读更新。我对此的看法是:您只能限制子查询中的消息。如果 sequelize 有一个 api - 或者你可以使用literal
,宾果游戏。也许您可以在他们的聊天中获得帮助:sequelize-slack.herokuapp.com.
并链接这个问题。以上是关于Sequelize - 如何提取具有 1 个关联记录的记录并按关联记录属性排序的主要内容,如果未能解决你的问题,请参考以下文章