几乎所有我见过的指南/教程都只展示了如何从数据库中直接可用的列中解析值.例如,以下是一种非常常见的模式,我理解它在许多方面如何有用:
case class Campaign(id: Int, campaign_mode_id: Int, name: String)
class Application @Inject()(db: Database) extends Controller {
val campaign = {
get[Int]("campaign.id") ~
get[Int]("campaign.campaign_mode_id") ~
get[String]("campaign.name") map {
case id ~ campaign_mode_id ~ name => Campaign(id, campaign_mode_id, name)
}
}
def index = Action {
val data : List[Campaign] = db.withConnection { implicit connection =>
SQL("SELECT id, campaign_mode_id, name FROM campaign").as(campaign.*)
}
Ok(Json.toJson(data))
}
}
Run Code Online (Sandbox Code Playgroud)
它产生的结果可能如下所示:
[
{
id: 2324,
campaign_mode_id: 13,
name: "ABC"
},
{
id: 1324,
campaign_mode_id: 23,
name: "ABCD"
}
]
Run Code Online (Sandbox Code Playgroud)
现在,如果广告系列表格中有一个额外的日期字段,就像started_on广告系列启动时提到的那样?或者另一个称为num_followers整数的字段是指跟随者的数量?
并且假设我想在运行数据库查询之后和返回我的JSON之前进行一些计算.例如,我想要包含一个latest_compaign_date引用started_on最新广告系列日期的内容.或者说我希望包含一个average_num_followers与所有广告系列的平均关注者数量相关的内容.
我的最终结果如下:
{
latest_compaign_date: 12 Dec 2018,
average_num_followers: 123,
campaigns: [
{
id: 2324,
campaign_mode_id: 13,
name: "ABC"
},
{
id: 1324,
campaign_mode_id: 23,
name: "ABCD"
}
]
}
Run Code Online (Sandbox Code Playgroud)
我知道,对于我给出的示例,我最好在我的数据库查询中进行这些计算,而不是在我的应用程序代码中.但是,如果我必须做一些非常复杂的事情并且想在我的应用程序代码中出于某种原因这样做呢?我应该如何修改我的ResutSetParser来实现这一点?
以下是我尝试过的几种方法:
case class CampaignData(newestCampaignDate: Long, newestCampaignId: Long, averageNumFollowers: Float, campaigns: Seq[Campaign])
def aggregater(rows: Seq[Row]): CampaignData = {
val newestCampaignDate: Long = getNewestDate(rows)
val newestCampaignId: Long = getNewestCampaign(rows)
val averageNumFollowers: Float = getAverageNumFollowers(rows)
val campaigns: Seq[Campaign] = rows.map(row => {
val rowMap: Map[String, Any] = row.asMap
Campaign(
rowMap("campaign.id").asInstanceOf[Int],
rowMap("campaign.campaign_mode_id") match { case None => 0 case Some(value) => value.asInstanceOf[Int]},
rowMap("campaign.name") match { case None => "" case Some(value) => value.asInstanceOf[String]}
)
})
CampaignData(newestCampaignDate, newestCampaignId, averageNumFollowers, campaigns)
}
def index = Action {
val data : Seq[Row] = db.withConnection { implicit connection =>
SQL("SELECT id, campaign_mode_id, name, started_on, num_followers FROM campaign")
}
Ok(Json.toJson(aggregater(data)))
}
Run Code Online (Sandbox Code Playgroud)
这种方法闻起来很糟糕,因为必须处理每个领域使用asInstanceOf并且match非常繁琐且诚实地感觉不安全.而且直觉上,我觉得Anorm应该有更好的东西,因为我可能不是第一个遇到这个问题的人.
case class Campaign(id: Int, campaign_mode_id: Int, name: String)
case class CampaignData(newestCampaignDate: Long, newestCampaignId: Long, averageNumFollowers: Float, campaigns: Seq[Campaign])
val campaign = {
get[Int]("campaign.id") ~
get[Int]("campaign.campaign_mode_id") ~
get[Int]("campaign.num_followers") ~
get[Long]("campaign.started_on") ~
get[String]("campaign.name") map {
case id ~ campaign_mode_id ~ num_followers ~ started_on ~ name => Map(
"id" -> id,
"campaign_mode_id" -> campaign_mode_id,
"num_followers" -> num_followers,
"started_on" -> started_on,
"name" -> name
)
}
}
def index = Action {
val data : Map[String, Any] = db.withConnection { implicit connection =>
SQL("SELECT id, campaign_mode_id, name, started_on, num_followers FROM campaign").as(campaign.*)
}
Ok(Json.toJson(aggregator(data)))
}
def aggregator(data: Map[String, Any]): CampaignData = {
val newestCampaignDate: Long = getNewestDate(data)
val newestCampaignId: Long = getNewestCampaign(data)
val averageNumFollowers: Float = getAverageNumFollowers(data)
val campaigns: Seq[Campaign] = getCampaigns(data)
CampaignData(newestCampaignDate, newestCampaignId, averageNumFollowers, campaigns)
}
Run Code Online (Sandbox Code Playgroud)
这种方法在我不需要处理的意义上更好isInstanceOf,但是那时必须处理中间体存在更大的问题Map.它使得所有的getter函数(例如getCampaigns)变得更加复杂.我觉得Anorm必须提供一些我不知道的开箱即用的东西.
正如您在第一个代码片段中发布的那样,以下代码
def index = Action {
val data : List[Campaign] = db.withConnection { implicit connection =>
SQL("SELECT id, campaign_mode_id, name FROM campaign").as(campaign.*)
}
Ok(Json.toJson(data))
}
Run Code Online (Sandbox Code Playgroud)
由于 Anorm 提取器,返回类型安全的活动列表。
通常,您将使用类型安全函数来预处理结果,如下所示
case class CampaignAggregateData(campaigns:List[Campaign], averageNumFollowers:Int, newestCampaignId:Option[Long])
def aggregate(f:List[Campaign]):CampaignAggregatedData
def index = Action {
val manyCampaign : List[Campaign] = db.withConnection { implicit connection =>
SQL("SELECT id, campaign_mode_id, name FROM campaign").as(campaign.*)
}
val aggregatedData:CampaignAggregateData = aggregate(manyCampaign)
Ok(Json.toJson(data))
}
Run Code Online (Sandbox Code Playgroud)
如果您需要由数据库引擎执行聚合,则通常db.withConnection在单个操作中包含多个语句