【问题标题】:Tree data structure persistence in RubyRuby 中的树数据结构持久性
【发布时间】:2012-03-05 00:27:19
【问题描述】:

我有一个项目需要在 Ruby 中构建和存储大型数据树。我正在考虑对树进行序列化、反序列化和查询的不同方法,我想知道最好的方法是什么。我的主要限制是读取时间、查询效率和跨版本/跨平台兼容性。最常见的操作是根据 id/value 和/或特征的组合检索节点集。树的深度可达 15-20 层。移动子树是一个不常见的过程,但应该可以在没有太多黑魔法的情况下实现。 Rails 集成不是主要问题。我考虑的选项以及我担心的一些问题如下:

  • 编组树,并在需要时将它们加载到内存中并在 Ruby 中查询它们(随着树的增长效率低下,跨版本兼容性?)
  • 与上述相同,但使用 YAML(跨版本兼容性更好,但效率较低?)
  • 与上述相同,但使用自定义 XML 解析器(每次加载树时都需要从头重新创建对象?)
  • 将树序列化为 XML,将它们存储在 XML 数据库(例如 Sedna)中并使用 XPath 来查询树(没有使用此方法的经验,不确定效率?)
  • 使用邻接表查询存储在无模式数据库中的树(计算后代时效率低下?)
  • 使用物化路径(可能会超出深度树的最大字符串长度?)
  • 使用嵌套集(复杂的 SQL 查询?)
  • 使用array of ancestors 方法?根据 MongoDB 页面,在查询效率方面似乎很有趣,但我还没有找到任何关于这个算法的严肃讨论。

根据您的经验,哪种方法更符合我所描述的限制条件?如果我选择 XML 数据库,是否有更适合这个项目的数据库?还有其他我忽略的更有效的方法吗?感谢您的宝贵时间。

【问题讨论】:

  • 在我的工作中,我们将节点存储为记录,并将相关属性作为列属性和一个特殊的先前列引用父节点,或者如果没有,则为 null。如果结果集稀疏并且可以绑定最大可能的树深度,则可以使用几种 sql 方言、存储过程或自连接中可用的递归查询构造来组装子树。移动子树意味着更新给定值的先前列。到 xml reps 和 xpath 表达式的映射很简单。
  • 这是否有 SQL 标记,因为您正在考虑将树存储在关系数据库中?
  • 没错!你问是因为你有在关系数据库中存储树的经验吗? :)
  • 我有在关系数据库中存储大量不同类型数据的经验。
  • this answer

标签: sql ruby serialization tree persistence


【解决方案1】:

树非常适合图形数据库,例如 neo4j:http://neo4j.org/learn/

Neo4j 是一个图数据库,将数据存储在图的节点和关系中。最通用的数据结构,图优雅地表示任何类型的数据,保留域的自然结构。

Ruby 有一个很好的树接口: https://github.com/andreasronge/neo4j

Pacer 是一个 JRuby 库,它支持非常富有表现力的图遍历。 Pacer 允许您使用非常快速且内存高效的流处理来创建、修改和遍历图形。这也意味着几乎所有的处理都是在纯 Java 中完成的,所以当遇到常见的 Ruby 表现力与速度问题时,你可以吃蛋糕也吃,速度非常快!

https://github.com/pangloss/pacer

Neography 类似于 neo4j.rb 宝石,由 Ron 在 cmets 中提出(感谢 Ron!)

https://github.com/maxdemarzi/neography

【讨论】:

【解决方案2】:

由于您正在考虑使用 SQL 方法,因此需要考虑以下几点。

首先,树有多大?对于许多应用程序来说,10,000 片叶子看起来很大。然而,这对于数据库来说很小。在任何体面的数据库系统(如笔记本电脑)上,您应该能够在内存中存储数十万或数百万个叶子。

与其他方法相比,数据库给您带来的好处是:

-- 不必担心内存/磁盘性能。当数据溢出到磁盘时,性能不会受到太大影响。相比之下,考虑一下当哈希表溢出内存时会发生什么。

-- 能够添加索引以优化性能。

-- 能够“仅仅”通过修改 SQL 来更改树的访问路径

标准 SQL 的一个问题是您可以将树节点表示为一对简单的对: , , 。然后,通过简单的连接,您可以在父母和叶子之间移动。但是,连接会随着您向上移动而累积。

叹息。不同的数据库对此有不同的解决方案。 SQL Server 具有递归 CTE,可让您遍历树。 Oracle 对树结构有另一种方法。

这开始变得复杂了。

也许更好的方法是根据树中的层次结构分配一个“叶子”ID。所以,如果这是一棵二叉树,那么“10011”将是右分支、左分支、左分支、右分支、右分支的节点。在那里您将存储信息。 . .比如它是否有孩子等等。获取父级很容易,因为您可以截断最后一位。

您可以看到这将如何推广到非二叉树。拥有任意数量的孩子可能会带来一些挑战。

我相信这可能与“祖先数组”方法有关。

我想,我认为这会很好。然后,我建议您为所需的每个操作定义单独的存储过程:

usp_tree_FetchNode (NodeId) usp_tree_GetParent (NodeId) usp_tree_NodeDelete (NodeId) usp_tree_FetchSubTree (NodeId) 等等等等等等。

虽然 SQL 并不真正支持面向对象的编程,但您仍然可以使用干净的命名约定和良好的函数包装来组织代码。

我实际上认为这可能有效,并为开发代码提供了一种很好的方法。一个不错的副作用是您可以在应用程序之外分析树,这可能会暗示未来的增强功能。

【讨论】:

    【解决方案3】:

    你看过ancestry gem吗?我已经将它用于简单的树,但根据描述,它看起来符合您的要求。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-03-07
      • 1970-01-01
      • 1970-01-01
      • 2013-04-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多