【问题标题】:Model an undirected graph in Rails?在 Rails 中建模无向图?
【发布时间】:2011-12-20 01:12:23
【问题描述】:

导入graph databases的语言,懂

  1. 节点用圆圈表示),
  2. 用箭头表示),以及
  3. 属性节点/边的元数据

图片(由维基百科提供)描述了directed graph

在 Rails 中建模 undirected graph 的最佳方法是什么?

也就是说,所有边都是互易的图(如上图),并且每条边的属性无论方向如何都相同(与上图相反)。

让我们假设默认的 Rails 3 设置通过 ActiveRecord 使用 sql 存储。

double polymorphic association 将创建一个有向图,能够对上图描述的数据进行建模。

def Edge < ActiveRecord::Base
  belongs_to :head, polymorphic: true
  belongs_to :tail, polymorphic: true
end

class Node < ActiveRecord::Base
  has_many :from, as: :head
  has_many :to, as: :tail
end

class Group < ActiveRecord::Base
  # a Node of Type: Group
  has_many :from, as: :head
  has_many :to, as: :tail
end

应该扩展此模型以管理反向关系,还是有更好的模型可用?


应用程序的一个元素可能是图问题,但这并不意味着应用程序以问题为中心,必须对数据执行图横断,也不意味着数据集大于可用内存。

【问题讨论】:

  • 如果您需要大图的高性能,您需要根据您的假设工作。这不适合 (sql) RDBMS。
  • 不适合大图?绝对地。但仍有可能。一旦有了将要处理的真实数据的示例,在初始原型之后交换或修改存储层比我书中最初增加的复杂性更可取。 (调用 Knuth “过早优化...”)
  • 正确的工具和设计选择与过早优化不同。你知道如何很好地使用锤子,你可以用锤子敲螺丝,但这并不意味着它是完成这项工作的最佳工具。此时切换到螺丝刀并不是过早的优化。如果您打算认真对待这个项目,并且它不仅仅是一个玩具,那么这样的考虑是完全有意义的。如果这只是一个实验,看看关系数据库如何存储图表,那也没关系,但让我们将其添加到问题中,以便我们知道这是主要意图。

标签: ruby-on-rails activerecord graph polymorphism prototyping


【解决方案1】:

在无向图中,您唯一需要知道的是一个节点是否连接到另一个节点。而且没有方向这样的东西。

简单的方法:

class Node
  has_many :connected_nodes
  has_many :nodes, :through => :connected_nodes
end

class ConnectedNode
  belongs_to :node
  belongs_to :connected_node, :class_name => 'Node'
end

这也称为邻接列表:对于每个节点,我们可以轻松获得相邻(连接)节点的列表。

这种方法的一个可能问题是:我们将连接存储了两次。 A 连接到 B,B 连接到 A。

因此,将每个连接仅存储一次似乎更好,这样我们就可以真正接近您的原始提议。

class Connection
  belongs_to :node1, :class_name => 'Node'
  belongs_to :node2, :clasS_name => 'Node'
end

只有我们尽力不通过命名强加任何顺序或方向。

检索连接的节点是所有以node1node2 连接的节点,因此有效地忽略了任何可能的方向。

在这种情况下,您还需要验证与 (node1, node2) 的连接是唯一的,但 (node2, node1) 实际上是相同的,不能插入两次。

我个人的选择是使用第二个架构,尽管维护第一个解决方案可能更快(另请参阅question)。

我还发现了一个非常有趣的article,作者解释了如何将图形存储在数据库中。非常深刻,但更以数据库为中心。

希望这会有所帮助。

【讨论】:

  • 我同意我只想在数据库中存储一次连接/边缘,所以我更喜欢你的第二个例子。但是我的 Node 类在这个例子中会是怎样的呢?似乎 ActiveRecord 的 has_many 关系始终是定向的,不是吗?
  • node1.connections 将产生 node2。但 node2.connections 不会产生任何东西。 @nathanvda
  • 我没有展示如何实现它(但描述了它:查找连接为node1node2 的所有节点)。看来你只找一种?请再问一个问题,您可以在哪里展示您尝试过的内容以及出了什么问题,并将链接放在这里,我会看看。
【解决方案2】:

【讨论】:

  • 考虑到graph databases 是问题中的第一个链接,假设人们已经阅读了both 预先存在的posts。这个问题是通过我自己的原型设计提出的,恕我直言,在编写第一行代码时打破图形数据库是多余的。如果您不同意,我们将非常感谢提供解释。
  • 我完全错过了“使用 sql 存储”这一点。 GDB 是这些任务的一个很好的解决方案,因为它们提供了良好的链接遍历性能和查询。如果不打算进行严重的负载或长时间的链接步行,则使用附加字段连接表也是一个很好的解决方案。
  • 对于小图,只需将其保存在内存中,如果需要持久性,将其存储为 blob。对于大图,只需计算所需的磁盘访问次数。 RDBMS 加入杀死性能。
【解决方案3】:

不要使用多态关联,而是尝试使用 has_many, :through

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :persons, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :group
  belongs_to :person
end

class Person < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships
end

您可以将边的属性存储在 Membership 模型中。

【讨论】:

  • 据我了解,has_many through 将创建一个高效的无向图,并在迁移中添加一个add_index :memberships, [:group_id, :person_id], unique: true,但代价是表蔓延。尝试对图表进行精确建模,您的示例中需要一个附加表来处理 Person 类上的自引用 knows 边。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-25
  • 2017-05-20
  • 1970-01-01
相关资源
最近更新 更多