从 Slick 连接到具有父子关系的实体的功能方法?
Functional way to go from a Slick join to entities with parent-child relationships?
我正在为新的 Scala Play 应用程序中的数据层尝试 Slick 3.2,我正在转动我的轮子,试图从针对一些相关 table 的查询中获得这些实体的 case class 表示。该应用程序立即(每当我们的系统中更新数据时)或按某个时间表将数据发送到任意目的地。即由以下三个 Postgres tables 表示:
CREATE TABLE destinations (
destination_id SERIAL PRIMARY KEY,
endpoint VARCHAR(100) NOT NULL,
protocol VARCHAR(100) NOT NULL
);
CREATE TABLE export_triggers (
export_trigger_id SERIAL PRIMARY KEY,
job_id INTEGER NOT NULL REFERENCES jobs,
destination_id INTEGER NOT NULL REFERENCES destinations,
trigger_type VARCHAR(50) NOT NULL,
export_schedule_id INTEGER REFERENCES export_schedules
);
CREATE TABLE export_schedules (
export_schedule_id SERIAL PRIMARY KEY,
description VARCHAR(1000),
cron VARCHAR(100) NOT NULL
);
以下情况 classes 表示 Scala 中的实体(Protocol 和 TriggerType 是枚举,为简洁起见我省略了):
case class Destination(endpoint: String, protocol: Protocol, exportTriggers: Seq[ExportTrigger])
case class ExportTrigger(triggerType: TriggerType, schedule: Option[ExportSchedule])
case class ExportSchedule(description: Option[String], cron: String)
Slick Codegen 生成的案例 classes 代表每个 table 中的一行。它们是 DestinationsRow
、ExportTriggersRow
和 ExportSchedulesRow
。
一个目的地可以包含多个 ExportTriggers,一个 ExportTrigger 可以包含 0 个或 1 个 ExportSchedules。我正在尝试实现一个函数 getDestinations(jobId: Int)
,它接受一个 jobId 并检索该作业的所有目的地(包括触发器和计划)。这是一个应该获取所有数据的 Slick 查询(为便于阅读而添加的新行):
ExportTriggers.filter(_.jobId === id)
join Destinations on (_.destinationId === _.destinationId)
joinLeft ExportSchedules on (_._1.exportScheduleId === _.exportScheduleId)
这个表达式的内部类型是 Seq[((ExportTriggersRow, DestinationsRow), Option[ExportSchedulesRow])]
,所以它应该包含创建我正在寻找的模型所需的映射,但是我很难从这个平面序列以功能方式将行添加到 Destination->[ExportTrigger]->ExportSchedule
结构,而不会陷入一些严重不可读的兔子洞。有什么想法吗?
根据您提供的信息,我会这样处理:
val result: Seq[((ExportTriggersRow, DestinationsRow), Option[ExportSchedulesRow])]
val grouped: Seq[Destination] = result.groupBy {
case ((_, destinationRow), _) => destinationRow
} map {
case (destinationRow, resultList) =>
val triggers = resultList.map {
case ((triggerRow, _), optScheduleRow) =>
val schedule = optScheduleRow.map { row =>
ExportSchedule(row.description, row.cron)
}
ExportTrigger(TriggerType(triggerRow.triggerType), schedule)
}
Destination(destinationRow.endpoint, Protocol(destinationRow.protocol), triggers)
}
首先按 destinationRow 分组,然后转换触发器和可选计划,最后创建 Destination。
编辑
更正了 grouped
的结果类型。它当然只是一个 Destinations
的序列,包含它们的 ExportTriggers
,而 ExportTriggers
又可能包含一个 ExportSchedule
我正在为新的 Scala Play 应用程序中的数据层尝试 Slick 3.2,我正在转动我的轮子,试图从针对一些相关 table 的查询中获得这些实体的 case class 表示。该应用程序立即(每当我们的系统中更新数据时)或按某个时间表将数据发送到任意目的地。即由以下三个 Postgres tables 表示:
CREATE TABLE destinations (
destination_id SERIAL PRIMARY KEY,
endpoint VARCHAR(100) NOT NULL,
protocol VARCHAR(100) NOT NULL
);
CREATE TABLE export_triggers (
export_trigger_id SERIAL PRIMARY KEY,
job_id INTEGER NOT NULL REFERENCES jobs,
destination_id INTEGER NOT NULL REFERENCES destinations,
trigger_type VARCHAR(50) NOT NULL,
export_schedule_id INTEGER REFERENCES export_schedules
);
CREATE TABLE export_schedules (
export_schedule_id SERIAL PRIMARY KEY,
description VARCHAR(1000),
cron VARCHAR(100) NOT NULL
);
以下情况 classes 表示 Scala 中的实体(Protocol 和 TriggerType 是枚举,为简洁起见我省略了):
case class Destination(endpoint: String, protocol: Protocol, exportTriggers: Seq[ExportTrigger])
case class ExportTrigger(triggerType: TriggerType, schedule: Option[ExportSchedule])
case class ExportSchedule(description: Option[String], cron: String)
Slick Codegen 生成的案例 classes 代表每个 table 中的一行。它们是 DestinationsRow
、ExportTriggersRow
和 ExportSchedulesRow
。
一个目的地可以包含多个 ExportTriggers,一个 ExportTrigger 可以包含 0 个或 1 个 ExportSchedules。我正在尝试实现一个函数 getDestinations(jobId: Int)
,它接受一个 jobId 并检索该作业的所有目的地(包括触发器和计划)。这是一个应该获取所有数据的 Slick 查询(为便于阅读而添加的新行):
ExportTriggers.filter(_.jobId === id)
join Destinations on (_.destinationId === _.destinationId)
joinLeft ExportSchedules on (_._1.exportScheduleId === _.exportScheduleId)
这个表达式的内部类型是 Seq[((ExportTriggersRow, DestinationsRow), Option[ExportSchedulesRow])]
,所以它应该包含创建我正在寻找的模型所需的映射,但是我很难从这个平面序列以功能方式将行添加到 Destination->[ExportTrigger]->ExportSchedule
结构,而不会陷入一些严重不可读的兔子洞。有什么想法吗?
根据您提供的信息,我会这样处理:
val result: Seq[((ExportTriggersRow, DestinationsRow), Option[ExportSchedulesRow])]
val grouped: Seq[Destination] = result.groupBy {
case ((_, destinationRow), _) => destinationRow
} map {
case (destinationRow, resultList) =>
val triggers = resultList.map {
case ((triggerRow, _), optScheduleRow) =>
val schedule = optScheduleRow.map { row =>
ExportSchedule(row.description, row.cron)
}
ExportTrigger(TriggerType(triggerRow.triggerType), schedule)
}
Destination(destinationRow.endpoint, Protocol(destinationRow.protocol), triggers)
}
首先按 destinationRow 分组,然后转换触发器和可选计划,最后创建 Destination。
编辑
更正了 grouped
的结果类型。它当然只是一个 Destinations
的序列,包含它们的 ExportTriggers
,而 ExportTriggers
又可能包含一个 ExportSchedule