【问题标题】:Calculate a derived column in the select output - Scala Slick 3.2.3计算选择输出中的派生列 - Scala Slick 3.2.3
【发布时间】:2020-03-08 09:11:05
【问题描述】:

我正在尝试编写一些 REST API 来使用 Scala Slick 3.2.3 获取数据。有没有办法计算派生列并将其包含在返回的输出中?

我的模特:

case class Task(id: Option[TaskId], title: String, dueOn: String, status: String, createdAt: String, updatedAt: String)

表格类:

class TasksTable(tag: Tag) extends Table[Task](tag, _tableName = "TASKS") {
  def id: Rep[TaskId] = column[TaskId]("ID", O.PrimaryKey, O.AutoInc)
  def title: Rep[String] = column[String]("TITLE")
  def dueOn: Rep[String] = column[String]("DUE_ON")
  def status: Rep[String] = column[String]("STATUS")
  def createdAt: Rep[String] = column[String]("CREATED_AT")
  def updatedAt: Rep[String] = column[String]("UPDATED_AT")
  def * = (id.?, title, dueOn, status, createdAt, updatedAt) <> ((Task.apply _).tupled, Task.unapply)
}

道:

object TasksDao extends BaseDao {
  def findAll: Future[Seq[Task]] = tasksTable.result
}

我想在响应 json 中添加一个名为 timeline 的列,其中包含基于 dueOn 值计算的值“过期”、“今天”、“明天”、“即将到来”等。

我尝试搜索但找不到任何帮助。任何有关示例或任何指针的帮助将不胜感激。谢谢!

【问题讨论】:

  • 您可以使用与Task 相同的字段创建新类TaskResponse,但也可以添加timeline 字段。它对你有用吗?
  • 是可以的。但我不确定在哪里/如何实现派生该列值的逻辑。

标签: scala slick dao derived


【解决方案1】:

首先我要从定义时间轴的枚举模型开始:

object Timelines extends Enumeration {
  type Timeline = Value
  val Overdue: Timeline = Value("overdue")
  val Today: Timeline = Value("today")
  val Tomorrow: Timeline = Value("tomorrow")
  val Upcoming: Timeline = Value("upcoming")
}

然后我会将dueOne 列类型从普通的String 修改为LocalDate - 这在DAO 级别上更容易做到,因此Slick 将为我们处理解析错误。 因此,需要为LocalDate 定义自定义类型(详情请参阅:http://scala-slick.org/doc/3.0.0/userdefined.html#using-custom-scalar-types-in-queries)。

// Define mapping between String and LocalDate
private val defaultDateFormat: DateTimeFormatter = DateTimeFormatter.ISO_DATE // replace it with formatter you use for a date

def stringDateColumnType(format: DateTimeFormatter): BaseColumnType[LocalDate] = {
 MappedColumnType.base[LocalDate, String](_.format(format), LocalDate.parse(_, format))
}

implicit val defaultStringDateColumnType: BaseColumnType[LocalDate] = stringDateColumnType(defaultDateFormat)

private val defaultDateFormat: DateTimeFormatter = DateTimeFormatter.ISO_DATE // replace it with formatter you use for a date

// Change `dueOn` from String to LocalDate
case class Task(id: Option[TaskId], title: String, dueOn: LocalDate, status: String, createdAt: String, updatedAt: String)

class TasksTable(tag: Tag) extends Table[Task](tag, _tableName = "TASKS") {
  def id: Rep[TaskId] = column[TaskId]("ID", O.PrimaryKey, O.AutoInc)
  def title: Rep[String] = column[String]("TITLE")
  def dueOn: Rep[LocalDate] = column[LocalDate]("DUE_ON") // Then replace column type
  def status: Rep[String] = column[String]("STATUS")
  def createdAt: Rep[String] = column[String]("CREATED_AT")
  def updatedAt: Rep[String] = column[String]("UPDATED_AT")
  def * = (id.?, title, dueOn, status, createdAt, updatedAt) <> ((Task.apply _).tupled, Task.unapply)
}

然后使用新的附加 timeline 字段定义 API 级别模型 TaskResponse

case class TaskResponse(id: Option[TaskId], title: String, dueOn: LocalDate, status: String, createdAt: String, updatedAt: String, timeline: Timeline)

  object TaskResponse {
    import Timelines._
    def fromTask(task: Task): TaskResponse = {
      val timeline = dueOnToTimeline(task.dueOn)
     TaskResponse(task.id, task.title, task.dueOn, task.status, task.createdAt, task.updatedAt, timeline)
    }

    def dueOnToTimeline(dueOn: LocalDate): Timeline = {
      val today = LocalDate.now()
      Period.between(today, dueOn).getDays match {
        case days if days < 0 => Overdue
        case 0 => Today
        case 1 => Tomorrow
        case _ => Upcoming
      }
    }
  }

然后你可以创建TasksService负责转换的业务逻辑:

  class TasksService(dao: TasksDao)(implicit ec: ExecutionContext) {
    def findAll: Future[Seq[TaskResponse]] = {
      dao.findAll.map(_.map(TaskResponse.fromTask))
    }
  }

希望这会有所帮助!

【讨论】:

  • 谢谢伊万。真的很有帮助。但我遇到的问题是如何在def findAll: Future[Seq[TaskResponse]] = tasksTable.result 中使用dueOnToTimeline!我有一个新的案例类TaskResponse,其中包含timeline 的属性。我正在使用 SQLite,它没有为存储日期预留任何存储类。因此使用String
  • @deyujjal 对此我深表歉意,我已经重新考虑并编辑了我的答案。我真的希望它会有所帮助!
  • 这太棒了!!这就像魔术一样!我能够实现我真正想要的。我真的很感谢你在这方面的帮助。非常感谢!!!! :)
  • @deyujjal 非常欢迎您,我很高兴这对您有所帮助!
猜你喜欢
  • 2012-01-05
  • 2021-08-20
  • 1970-01-01
  • 2020-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-08
相关资源
最近更新 更多