2015-05-14 2 views
2

Я пытаюсь выяснить, как переносить мою собственную реализацию таблицы закрытия с другого языка на Scala с учетом параллелизма.Slick 3 Transaction

У меня есть две модели: Node (id | parentID) и NodeTree (id | ancestor | descendant), где каждая запись напоминает ребро в дереве.

Для каждого нового узла я должен сделать следующее: запросов всех предков (или фильтровать TableQuery для них), а затем добавить NodeTree-Entry (Ребро) для каждого предка

Благодаря Panther я получил до сих пор:

private val nodes = TableQuery[Nodes] 

override def create(node: Node): Future[Seq[Int]] = 
    { 
     val createNodesAction = (
      for 
      { 
       parent <- nodes 
       node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parent.id, descendant = node.id, deleted = None, createdAt = new Timestamp(now.getTime), updatedAt = new Timestamp(now.getTime))) 
      } yield (node) 
     ).transactionally 

     db run createNodesAction 
    } 

Но это приводит к несоответствию типа;

тип несоответствие; найдено: slick.lifted.Rep [Long] требуется: Опция [Long]

Еще раз: Все, что я хочу сделать, это: Для каждого ParentNode (= родитель каждого из родителей до последнего предка-узла не имеет родителя!) Я хочу создать запись в nodeTree, чтобы позже я мог легко захватить всех потомков и предков только с помощью другого вызова метода, который фильтрует через NodeTree-Table.

(Просто закрытие таблицы, на самом деле)

редактировать: Это мои модели

case class Node(id: Option[Long], parentID: Option[Long], level: Option[Long], deleted: Option[Boolean], createdAt: Timestamp, updatedAt: Timestamp) 

class Nodes(tag: Tag) extends Table[Node](tag, "nodes") 
{ 
    implicit val dateColumnType = MappedColumnType.base[Timestamp, Long](d => d.getTime, d => new Timestamp(d)) 

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 
    def parentID = column[Long]("parent_id") 
    def level = column[Long]("level") 
    def deleted = column[Boolean]("deleted") 
    def createdAt = column[Timestamp]("created_at") 
    def updatedAt = column[Timestamp]("updated_at") 

    def * = (id.?, parentID.?, level.?, deleted.?, createdAt, updatedAt) <> (Node.tupled, Node.unapply) 
} 

case class NodeTree(id: Option[Long], ancestor: Option[Long], descendant: Option[Long], deleted: Option[Boolean], createdAt: Timestamp, updatedAt: Timestamp) 

class NodeTrees(tag: Tag) extends Table[NodeTree](tag, "nodetree") 
{ 
    implicit val dateColumnType = MappedColumnType.base[Timestamp, Long](d => d.getTime, d => new Timestamp(d)) 

    def id = column[Long]("id", O.PrimaryKey, O.AutoInc) 
    def ancestor = column[Long]("ancestor") 
    def descendant = column[Long]("descendant") 
    def deleted = column[Boolean]("deleted") 
    def createdAt = column[Timestamp]("created_at") 
    def updatedAt = column[Timestamp]("updated_at") 

    def * = (id.?, ancestor.?, descendant.?, deleted.?, createdAt, updatedAt) <> (NodeTree.tupled, NodeTree.unapply) 
} 

То, что я хочу сделать, это замыкание таблицы (http://technobytz.com/closure_table_store_hierarchical_data.html), который заполняет его края (nodeTree) автоматически, когда Я создаю узел. Поэтому я не хочу вручную добавлять все эти записи в базу данных, но когда я создаю узел на уровне 5, я хочу, чтобы весь путь (= записи в таблице nodetree) создавался автоматически.

Я надеюсь, что очищает вещи немного :)

ответ

2

Попробуйте это:

override def create(node: Node): Future[Seq[Int]] = 
{ 
    val parents = getAllParents(node) 
    val createNodesAction = (
     for { 
     parent <- parents 
     node <- nodeTrees += NodeTree(id = None, ancestor = parent.id, descendant = node.id) 
     } yield (node) 
    ).transactionally 

    db run createNodesAction 
} 

Вы не должны отдельно получать родителей отдельно. Это можно сделать в том же сеансе. Вы можете легко заменить «родителей» на понимание с помощью TableQuery, с которым вы хотите работать (с фильтром или без него).

Также обратите внимание, что здесь вы возвращаете последовательность чисел строк, на которые влияет действие вставки. Для того, чтобы вместо того, чтобы возвращать список узлов идентификаторов (предполагая, что вы бы отметили узел Идентификаторы как AUTO_INC в дБ), то вы можете сделать что-то вроде этого:

override def create(node: Node): Future[Seq[Int]] = 
{ 
    val createNodesAction = (
     for { 
     parent <- parents 
     node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parent.id, descendant = node.id) 
     } yield (node) 
    ).transactionally 

    db run createNodesAction 
} 

Разница заключается в том: (nodeTrees возвращающимся nodeTrees.map (_. id) в ((ntEntry, ntId) => ntEntry.copy (id = Some (ntId))) вместо (nodeTrees), который извлекает и отображает auto inc id в результат.


Update: Попробуйте это:

override def create(node: Node): Future[Seq[Int]] = 
{ 
    def createNodesAction(parentId: Long): DBIOAction[NodeTree, NoStream, Read with Write] = (
     for { 
     node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = Some(ntId))) += NodeTree(id = None, ancestor = parentId, descendant = node.id) 
     } yield (node) 
    ).transactionally 

    // TODO: Init and pass in 'parents' 
    db.run(DBIO.sequence(parents.map(createNodesAction(_.id))) 
} 
+0

Я принимаю ваш ответ, но есть несколько проблем: Во-первых: nt.id = ntId -> ntId имеет тип Long, а опция [Long] нужна здесь. Кроме того, nt.id = ntId -> переназначение в val:/Кроме того, не могли бы вы уточнить ... будет ли обновляться запрос таблицы (если будет больше записей, будет ли он повторно селе?), Или мне нужно будет сделать это вручную ? – Sorona

+0

Отредактирован ответ на адрес, задающий необязательное значение и переназначение на проблему val. На ваш вопрос о выборе нескольких записей в insert_ я не смог найти способ сделать это. Возможно, это связано с тем, что идентификатор, вероятно, извлекается с использованием другой SQL-конструкции ([last_insert_id] (https://dev.mysql.com/doc/refman/5.0/en/information-functions.html#function_last-insert-id)). В настоящее время для выбора других элементов из вставленной строки (например, отметки времени) я делаю это вручную, запустив запрос выбора на извлеченный идентификатор. – panther

+0

Значение id не является членом List [models.Node] * sighs * Мне нужно распаковать список или даже сгладить его? Если я попробую его с TableQuery, я даже получаю: несоответствие типа; найдено: slick.lifted.Rep [Long] required: Option [Long] – Sorona

1

Попробуйте изменить к этой линии.

node <- (nodeTrees returning nodeTrees.map(_.id) into ((ntEntry, ntId) => ntEntry.copy(id = ntId)) += NodeTree(id = None, ancestor = parent.id, descendant = node.id, deleted = None, createdAt = new Timestamp(now.getTime), updatedAt = new Timestamp(now.getTime))) 

Решает проблему? Трудно сказать с вашего вопроса, что именно ваши модели.

+1

Я добавил код модели к вопросу, хотя я не вижу, как это должно помочь. Во всяком случае, я также добавил еще одно описание того, чего именно хочу достичь. В любом случае, спасибо за вашу помощь! :) – Sorona

Смежные вопросы