Scala 元组到层次结构
Scala tuples to hierarchy
在 Slick 中,SQL 像这样连接:
select a.id, a.name, b.name, c.name, c.added from A a join B b join C c
将 return 这样的元组:
(1,Jim,Some(Foo),Mark,Some(2018-06-14T08:06:56Z))
(1,Jim,Some(Foo),Anne,Some(2018-06-14T12:04:50Z))
(1,Jim,Some(Bar),Jose,Some(2018-06-14T17:40:19Z))
将此类元组转换为层次结构的最 Scala 方式是什么?例如:
Map(
"id" -> 1,
"name" -> "Jim",
"projects" -> List(
Map(
"name" -> "Foo",
"staffs" -> List(
Map(
"name" -> "Mark",
"added" -> "2018-06-14T08:06:56Z"
),
Map(
"name" -> "Anne",
"added" -> "2018-06-14T12:04:50Z"
)
)
),
Map(
"name" -> "Bar",
"staffs" -> List(
Map(
"name" -> "Jose",
"added" -> "2018-06-14T17:40:19Z"
)
)
)
)
)
请注意,生成的对象将使用 Jackson 转换为 JSON。
使用 groupBy()
运算符收集具有公共字段的元组。下面的第一个 groupBy
选择前两个字段并将它们映射到 id
和 name
的 Map
条目。第二个嵌套的 groupBy
在项目字段上进行子选择并创建项目列表。第三次迭代现在选择与项目相关的人员。
val listOfProjectMaps = resultList.groupBy(t => (t._1, t._2))
.map({
case (k, v) => Map(
"id" -> k._1,
"name" -> k._2,
"projects" ->
v.groupBy(tv => tv._3.get)
.map({
case (k1, v1) =>
Map(
"name" -> k1,
"staffs" ->
v1.map(v2 => Map(
"name" -> v2._4,
"added" -> v2._5.get)))
}))
})
请注意这种方法,虽然直截了当并不能很好地处理坏数据。
这是我的做法;
首先为您的项目域创建 classes。
case class Manager(managerId: Int, name: String)
case class Project(projectId: Int, name: String, staffIds: List[Int])
case class Staff(staffId: Int, name: String, added: String) // or java.time.ZonedDateTime
然后像本例一样设置 Slick 数据库映射 gist。您可以使用 split(",")
和 toInt
和 mkString(",")
将 List[Int]
保留在 Project
中,或者使用连接 table 和 case class StaffOnProject(projectId: Int, staffId: Int)
.
现在你可以在你的服务中使用连接来收集你的数据 class 就像这样;我会说有很多方法可以做到这一点。
// Probably not what I would do but this is the structure you asked for
case class ProjectWithStaff(project: Project, staff: List[Staff])
case class ManagerWithProjectsAndStaff(manager: Manager, projects: List[ProjectWithStaff])
def getManagerWithProjectsAndStaff(managerId:Int): Future[ManagerWithProjectsAndStaff] =
for {
manager <- db.run(Managers.byId(managerId).result.head)
projects <- db.run(Projects.byManager(managerId).result)
staffs <- db.run(Staffs.table.filter(_.staffId inSet projects.flatMap(_.staffIds).toSet).result)
} yield ManagerWithProjectsAndStaff(
manager,
projects.map(p => {
val projectStaff = staffs.filter(s => p.staffIds.contains(s.staffId))
ProjectWithStaff(p, projectStaff)
})
)
所有这些案例 classes 都可以由 Jackson 进行(反)序列化。 ManagerWithProjectsAndStaff
将以您描述的格式序列化。
在 Slick 中,SQL 像这样连接:
select a.id, a.name, b.name, c.name, c.added from A a join B b join C c
将 return 这样的元组:
(1,Jim,Some(Foo),Mark,Some(2018-06-14T08:06:56Z))
(1,Jim,Some(Foo),Anne,Some(2018-06-14T12:04:50Z))
(1,Jim,Some(Bar),Jose,Some(2018-06-14T17:40:19Z))
将此类元组转换为层次结构的最 Scala 方式是什么?例如:
Map(
"id" -> 1,
"name" -> "Jim",
"projects" -> List(
Map(
"name" -> "Foo",
"staffs" -> List(
Map(
"name" -> "Mark",
"added" -> "2018-06-14T08:06:56Z"
),
Map(
"name" -> "Anne",
"added" -> "2018-06-14T12:04:50Z"
)
)
),
Map(
"name" -> "Bar",
"staffs" -> List(
Map(
"name" -> "Jose",
"added" -> "2018-06-14T17:40:19Z"
)
)
)
)
)
请注意,生成的对象将使用 Jackson 转换为 JSON。
使用 groupBy()
运算符收集具有公共字段的元组。下面的第一个 groupBy
选择前两个字段并将它们映射到 id
和 name
的 Map
条目。第二个嵌套的 groupBy
在项目字段上进行子选择并创建项目列表。第三次迭代现在选择与项目相关的人员。
val listOfProjectMaps = resultList.groupBy(t => (t._1, t._2))
.map({
case (k, v) => Map(
"id" -> k._1,
"name" -> k._2,
"projects" ->
v.groupBy(tv => tv._3.get)
.map({
case (k1, v1) =>
Map(
"name" -> k1,
"staffs" ->
v1.map(v2 => Map(
"name" -> v2._4,
"added" -> v2._5.get)))
}))
})
请注意这种方法,虽然直截了当并不能很好地处理坏数据。
这是我的做法; 首先为您的项目域创建 classes。
case class Manager(managerId: Int, name: String)
case class Project(projectId: Int, name: String, staffIds: List[Int])
case class Staff(staffId: Int, name: String, added: String) // or java.time.ZonedDateTime
然后像本例一样设置 Slick 数据库映射 gist。您可以使用 split(",")
和 toInt
和 mkString(",")
将 List[Int]
保留在 Project
中,或者使用连接 table 和 case class StaffOnProject(projectId: Int, staffId: Int)
.
现在你可以在你的服务中使用连接来收集你的数据 class 就像这样;我会说有很多方法可以做到这一点。
// Probably not what I would do but this is the structure you asked for
case class ProjectWithStaff(project: Project, staff: List[Staff])
case class ManagerWithProjectsAndStaff(manager: Manager, projects: List[ProjectWithStaff])
def getManagerWithProjectsAndStaff(managerId:Int): Future[ManagerWithProjectsAndStaff] =
for {
manager <- db.run(Managers.byId(managerId).result.head)
projects <- db.run(Projects.byManager(managerId).result)
staffs <- db.run(Staffs.table.filter(_.staffId inSet projects.flatMap(_.staffIds).toSet).result)
} yield ManagerWithProjectsAndStaff(
manager,
projects.map(p => {
val projectStaff = staffs.filter(s => p.staffIds.contains(s.staffId))
ProjectWithStaff(p, projectStaff)
})
)
所有这些案例 classes 都可以由 Jackson 进行(反)序列化。 ManagerWithProjectsAndStaff
将以您描述的格式序列化。