Recursive tree-like table query with Slick

耗尽温柔 提交于 2019-12-07 03:37:09

问题


My table data forms a tree structure where one row can reference a parent row in the same table.

What I am trying to achieve, using Slick, is to write a query that will return a row and all it's children. Also, I would like to do the same, but write a query that will return a child and all it's ancestors.

In other words:

findDown(1) should return

List(Group(1, 0, "1"), Group(3, 1, "3 (Child of 1)"))

findUp(5) should return

List(Group(5, 2, "5 (Child of 2)"), Group(2, 0, "2"))

Here is a fully functional worksheet (except for the missing solutions ;-).

package com.exp.worksheets

import scala.slick.driver.H2Driver.simple._

object ParentChildTreeLookup {

  implicit val session = Database.forURL("jdbc:h2:mem:test1;", driver = "org.h2.Driver").createSession()

  session.withTransaction {
    Groups.ddl.create
  }

  Groups.insertAll(
    Group(1, 0, "1"),
    Group(2, 0, "2"),
    Group(3, 1, "3 (Child of 1)"),
    Group(4, 3, "4 (Child of 3)"),
    Group(5, 2, "5 (Child of 2)"),
    Group(6, 2, "6 (Child of 2)"))

  case class Group(
    id: Long = -1,
    id_parent: Long = -1,
    label: String = "")

  object Groups extends Table[Group]("GROUPS") {
    def id = column[Long]("ID", O.PrimaryKey, O.AutoInc)
    def id_parent = column[Long]("ID_PARENT")
    def label = column[String]("LABEL")
    def * = id ~ id_parent ~ label <> (Group, Group.unapply _)
    def autoInc = id_parent ~ label returning id into {
      case ((_, _), id) => id
    }

    def findDown(groupId: Long)(implicit session: Session) = { ??? }

    def findUp(groupId: Long)(implicit session: Session) = { ??? }
  }

}

A really bad, and static attempt at findDown may be something like:

private def groupsById = for {
  group_id <- Parameters[Long]
  g <- Groups; if g.id === group_id
} yield g

private def childrenByParentId = for {
  parent_id <- Parameters[Long]
  g <- Groups; if g.id_parent === parent_id
} yield g


def findDown(groupId: Long)(implicit session: Session) = { groupsById(groupId).list union childrenByParentId(groupId).list }

But, I'm looking for a way for Slick to recursively search the same table using the id and id_parent link. Any other good ways to solve the problem is really welcome. Keep in mind though, that it would be best to minimise the number of database round-trips.


回答1:


You could try calling SQL from slick. The SQL call to go up the hierarchy would look something like this (This is for SQL Server):

WITH org_name AS 
(
    SELECT DISTINCT
        parent.id AS parent_id,
        parentname.label as parent_label,
        child.id AS child_id,
        childname.ConceptName as child_label
    FROM
        Group parent RIGHT OUTER JOIN 
        Group child ON child.parent_id = parent.id
), 
jn AS 
(   
    SELECT
        parent_id,
        parent_label,
        child_id,
        child_label
    FROM
        org_name 
    WHERE
        parent_id = 5
    UNION ALL 
        SELECT
            C.parent_id,
            C.parent_label,
            C.child_id,
            C.child_label 
        FROM
            jn AS p JOIN 
            org_name AS C ON C.child_id = p.parent_id
) 
SELECT DISTINCT
    jn.parent_id,
    jn.parent_label,
    jn.child_id,
    jn.child_label
FROM
    jn 
ORDER BY
    1;

If you want to go down the hierarchy change the line:

org_name AS C ON C.child_id = p.parent_id

to

org_name AS C ON C.parent_id = p.child_id



回答2:


In plain SQL this would be tricky. You would have multiple options:

  1. Use a stored procedure to collect the correct records (recursively). Then convert those records into a tree using code
  2. Select all the records and convert those into a tree using code
  3. Use more advanced technique as described here (from Optimized SQL for tree structures) and here. Then convert those records into a tree using code

Depending on the way you want to do it in SQL you need to build a Slick query. The concept of Leaky Abstractions is very evident here.

So getting the tree structure requires two steps:

  1. Get the correct (or all records)
  2. Build (using regular code) a tree from those records

Since you are using Slick I don't think it's an option, but another database type might be a better fit for your data model. Check out NoSQL for the differences between the different types.



来源:https://stackoverflow.com/questions/15070620/recursive-tree-like-table-query-with-slick

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!