【问题标题】:Scala tuples to hierarchyScala 元组到层次结构
【发布时间】:2018-06-14 17:20:52
【问题描述】:

在 Slick 中,SQL 连接如下:

select a.id, a.name, b.name, c.name, c.added from A a join B b join C c

将返回像这样的元组:

(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。

【问题讨论】:

  • 结构 (1,Jim,Some(Foo),Mark,Some(2018-06-14T08:06:56Z)) 的大小将是恒定的,或者这将是可变的。如果它的大小不变,那就更容易了。
  • 您能否发布一个示例输入元组数据结构,您可以在其中从 SQL 获取数据?

标签: scala tuples slick


【解决方案1】:

我会这样做; 首先为您的项目域创建类。

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

然后像此示例gist 那样设置您的 Slick 数据库映射。您可以使用split(",")toIntmkString(",")Project 中持久化List[Int],或者使用连接表和case class StaffOnProject(projectId: Int, staffId: Int)。 现在,您可以使用服务类中的连接来收集数据,如下所示;我想说有很多方法可以做到这一点。

// 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)
    })
  )

Jackson 可以(反)序列化所有这些案例类。 ManagerWithProjectsAndStaff 将按照您描述的格式进行序列化。

【讨论】:

    【解决方案2】:

    使用groupBy() 运算符收集具有公共字段的元组。下面的第一个groupBy 选择前两个字段并将它们映射到idnameMap 条目。第二个,嵌套的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)))
          }))
    })
    

    注意这种方法,虽然简单并不能很好地处理不良数据。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-01-24
      • 1970-01-01
      • 2011-05-06
      • 1970-01-01
      • 2017-04-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多