在两个外键上进行光滑查询

时间:2015-09-02 19:53:07

标签: scala slick

我有以下查询来获取一个人的所有祖先和后代。好吧,目前只有 祖先后代。

  def relatives = FamilyTree.all.filter(p => p.descendant === id || p.ancestor === id).flatMap(_.descendantFK)

Family Tree表如下所示:

id | ancestor (references id on persons) | descendant (references id on persons)

以上查询为我提供了所有后代,因为它在后代外键上的flatMaps

现在我想一起检索所有祖先后代!

Flatmap让我在这里遇到麻烦。我怎样才能在descendantFK和ancestorFK上进行flatMap?

基本上我想要的是

....flatMap(r => r.descendantFK || r.ancestorFK)

当然上述情况不起作用。

在SQL中我想要的东西:

SELECT * FROM familyTree WHERE ancestor == id OR descendant == id

编辑:根据请求添加类定义:

case class Person(id: Long, foreName: String, lastName: String)
{
    def relatives: Query[Persons, Person, Seq] =
    {
        for
        {
            person <- Persons.all.filter(_.id === this.id)
            relative <- person.relatives
        } yield (relative)
    }
}

class Persons(tag: Tag) extends Table[Person](tag, "persons")
{
    def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
    def foreName = column[String]("forename")
    def lastName = column[String]("lastname")

    def * = (id, foreName, lastName) <> (Person.tupled, Person.unapply)

    def relatives = PersonFamilyTree.all.filter(c => c.descendant === id || c.ancestor === id).flatMap(t => t.descendantFK)
}

object Persons
{
   lazy val all = TableQuery[Persons]

   val findById = Compiled {a: Rep[Long] => all.filter(_.id === a)}
}

数据库包装类

class SQLDatabaseWrapper(pathToDatabase: String, driver: String)
{
    val db = Database.forURL(pathToDatabase, driver = this.driver)

    private val persons = Persons.all
    private val familyTree = PersonFamilyTree.all

    val setup = DBIO.seq(
        // Create the tables, including primary and foreign keys
        (persons.schema ++ familyTree.schema).create,

        // Insert some dummy data
        persons += new Person(-1, "Granpa", ""),
        persons += new Person(-1, "Pa", ""),
        persons += new Person(-1, "Daughter", ""),
        persons += new Person(-1, "Son", ""),

        familyTree += new PersonFamilyTreeEntry(-1, 1, 2), // Granpa -> Pa
        familyTree += new PersonFamilyTreeEntry(-1, 1, 3), // Granpa -> Daughter
        familyTree += new PersonFamilyTreeEntry(-1, 1, 4), // Granpa -> Son

        familyTree += new PersonFamilyTreeEntry(-1, 2, 3), // Pa -> Daughter
        familyTree += new PersonFamilyTreeEntry(-1, 2, 4)  // Pa -> Son
    )

    val setupFuture = db.run(setup)

    def getAllPersons(): Future[Seq[Person]] =
    {
        db.run(persons.result)
    }

    def getPerson(id: Long): Future[Person] =
    {
        //  db.run(persons.filter(_.foreName === "Nathanael").result)
        db.run(Persons.findById(id).result).map(_.head)
    }

    def getRelatives(person: Person): Future[Seq[Person]] =
    {
        val relativesQuery: Query[Persons, Person, Seq] = person.relatives
        db.run(relativesQuery.result)
        //    db.run(person.familyMembers.result)
    }

    case class PersonFamilyTreeEntry(id: Long, ancestor: Long, descendant: Long)

    class PersonFamilyTree(tag: Tag) extends Table[PersonFamilyTreeEntry](tag, "person_family_tree")
    {
        def id = column[Long]("id", O.PrimaryKey, O.AutoInc)
        def ancestor = column[Long]("ancestor")
        def descendant = column[Long]("descendant")

        def * = (id, ancestor, descendant) <> (PersonFamilyTreeEntry.tupled, PersonFamilyTreeEntry.unapply _)

        def ancestorFK = foreignKey("ancestor_fk", ancestor, Persons.all)(_.id)
        def descendantFK = foreignKey("descendant_fk", descendant, Persons.all)(_.id)
    }

    object PersonFamilyTree
    {
        lazy val all = TableQuery[PersonFamilyTree]
    }
}

这就是我在其他地方查询(或手动测试)的方式:

val pa: Future[Person] => sqlDatabaseWrapperInstance.getPerson(2)

pa.onSuccess{
    case p: Person =>
    {
        val relativesOfPa = sqlDatabaseWrapperInstance.getRelatives(p)
        relativesOfPa.onSuccess
        {
             case r: Seq[Person] => r foreach(println(_))
        }
    }
}

1 个答案:

答案 0 :(得分:2)

应该能够使用++unionAll)来完成此操作:

....flatMap(r => r.descendantFK ++ r.ancestorFK)

以另一种方式思考这个问题,你可以通过加入来做到这一点:

for {
    person <- Person.all
    other <- FamilyMember.all
    if (other.ancestor === person.id && other.ancestor === id)
       || (other.descendant === person.id && other.descendant === id)
} yield person 

您也可以使用带有子查询的in来执行此操作:

val familyMembers = FamilyTree.all
  .filter(p => p.descendant === id || p.ancestor === id)
  .map(p => if (p.descendant === id) p.descendant else p.ancestor)
Persons.all.filter(_.id in familyMembers)