Ловкая 3 транзакция

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

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

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

Благодаря пантере я зашел так далеко:

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] требуется: Option[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, я хочу, чтобы весь путь (= записи в таблице узлов) создавался автоматически.

Я надеюсь, что это немного проясняет ситуацию :)


person Community    schedule 14.05.2015    source источник


Ответы (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
}

Вам не нужно отдельно извлекать родителей. Это можно сделать в рамках одного сеанса. Выше вы можете легко заменить 'parents' в for-comprehension на TableQuery, с которым вы хотите работать (с фильтром или без него).

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

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 с результатом.


Обновление. Попробуйте следующее:

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)))
}
person panther    schedule 19.05.2015
comment
Я бы принял ваш ответ, но есть несколько проблем: Во-первых: nt.id = ntId -> ntId имеет тип Long, а здесь нужен Option[Long]. Кроме того, nt.id = ntId -> переназначение на val:/ Кроме того, не могли бы вы уточнить... будет ли обновлен запрос таблицы (если есть больше записей, будет ли он переустановлен?) или мне нужно будет сделать это вручную ? - person ; 23.05.2015
comment
Отредактировал ответ на установку необязательного значения адреса и переназначение проблемы val. Что касается вашего вопроса о выборе нескольких записей при вставке, я не смог найти способ сделать это. Это может быть связано с тем, что идентификатор, вероятно, извлекается с использованием другой конструкции SQL (last_insert_id). В настоящее время для выбора других элементов из вставленной строки (например, временной метки) я делаю это вручную, выполняя запрос выбора для полученного идентификатора. - person panther; 26.05.2015
comment
идентификатор значения не является членом List[models.Node] вздыхает Мне нужно распаковать список или даже сгладить его? Если я попробую это с помощью TableQuery, я даже получу: несоответствие типов; найдено: slick.lifted.Rep[Long] требуется: Option[Long] - person ; 28.05.2015
comment
On: найдено: slick.lifted.Rep[Long] required: Option[Long], вы увидите Rep[T], а не T, когда пытаетесь использовать значение перед его выполнением. Это связано с тем, что каждый тип поднимается до Rep[T] для выполнения перед его фактическим запуском, а затем сопоставляется обратно с T после выполнения. Пожалуйста, поделитесь кодом, как вы пытаетесь его использовать. - person panther; 02.06.2015
comment
Кроме того, это то, что вы ищете. - person panther; 02.06.2015
comment
Я пробую ваш второй пример. И... ну, для каждого узла я хочу создать несколько записей NodeTree-Entries, которые ссылаются на эту самую запись. Я не понимаю, почему это не должно быть возможно. - person ; 07.06.2015
comment
Извините, я понятия не имею, что это должно делать. Я не могу найти таблицы для импорта (вы имели в виду таблицы?) И что такое NodesRow? Должны ли это быть узлы из TableQuery? Нужно ли для этого активировать Stream-Support (экспериментальную) или что-то в этом роде? Как сказано для таблиц, он находит что-то только из пространства имен Google. - person ; 15.06.2015
comment
Думаю, в данном случае это должен быть NodeTree. По сути, это сопоставление класса case с соответствующей строкой БД. - person panther; 16.06.2015
comment
Я бы очень хотел дать вам награду / репутацию, потому что вы мне очень помогли, но это все еще не работает, и я понятия не имею, почему. Я пробовал с Tables.NodeTree и с Table.NodeTree, но не повезло. Также: db.run(DBIO.sequence(parents.map(createNodesAction(_.id)))) дает мне еще одно TypeMismatch (ожидаемое Long, фактическое (Any) => Any) Я даже не понимаю почему. - person ; 02.07.2015

Попробуйте перейти на эту строку.

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

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

person beefyhalo    schedule 29.06.2015
comment
Я добавил код своей модели к вопросу, хотя не понимаю, как это должно помочь. Во всяком случае, я также добавил еще одно описание того, чего именно я хочу достичь. Спасибо в любом случае за вашу помощь! :) - person ; 01.07.2015