我试图弄清楚如何在考虑并发性的情况下将我自己的闭包表实现从另一种语言移植到Scala。
我有两个模型,一个Node(id | parentID)和一个NodeTree(id | ancestor | descendant),其中每个条目类似于树中的边。
对于每个新节点,我必须执行以下操作: 查询所有祖先(或为它们过滤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 [长]需要:选项[长]
再一次:我想要做的就是:对于每个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-table中的条目)。
我希望稍微澄清一下:)
答案 0 :(得分: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
}
您不必单独单独检索父母。它可以在同一个会话中完成。在上面,您可以轻松替换父母&#39;使用TableQuery进行理解你想要反对(有或没有过滤器)。
另请注意,此处您将返回受插入操作影响的行数序列。要改为返回节点ID列表(假设你在db中将节点ID标记为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)=&gt; ntEntry.copy(id = Some(ntId)))而不仅仅是(nodeTrees),用于检索并将auto inc id映射到result。
更新:试试这个:
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)))
}
答案 1 :(得分: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)))
它是否解决了这个问题?很难从你的问题中准确地判断出你的模型是什么。