如何在图形数据库(如Neo4j)中建模真实世界的关系?

20

我有一个关于在图形数据库中建模的一般性问题,我似乎无法理解。

如何建模这种关系:“牛顿发明了微积分”?

简单图中,您可以这样建模:

Newton (node) -> invented (relationship) -> Calculus (node)

...所以随着你添加更多的人和发明,你会有一堆“虚构”的图形关系。

问题是,你开始需要添加大量的关系属性:

  • invention_date
  • influential_concepts
  • influential_people
  • books_inventor_wrote

...然后你会想要开始在这些属性和其他节点之间创建关系,例如:

  • influential_people:与人物节点相关联的关系
  • books_inventor_wrote:与书籍节点相关联的关系

所以现在似乎“真实世界的关系”(“虚构”)应该实际上是图中的一个节点,而图应该如下所示:

Newton (node) -> (relationship) -> Invention of Calculus (node) -> (relationship) -> Calculus (node)

为了让事情变得更加复杂,其他人也参与了微积分的发明,所以图形现在变成了这样:

Newton (node) -> 
  (relationship) -> 
    Newton's Calculus Invention (node) -> 
      (relationship) -> 
        Invention of Calculus (node) -> 
          (relationship) -> 
            Calculus (node)
Leibniz (node) -> 
  (relationship) -> 
    Leibniz's Calculus Invention (node) -> 
      (relationship) -> 
        Invention of Calculus (node) -> 
          (relationship) -> 
            Calculus (node)

所以我提出这个问题,因为似乎你不想在实际的图形数据库"关系"对象上设置属性,因为你可能希望在某个时候将它们视为图中的节点。
这是正确的吗?
我一直在研究Freebase Metaweb Architecture,他们似乎把所有东西都看作一个节点。例如,Freebase有一个Mediator/CVT的概念,您可以创建一个"Performance"节点,将一个"Actor"节点链接到一个"Film"节点,就像这里:http://www.freebase.com/edit/topic/en/the_last_samurai。不太确定这是否是同一个问题。
您用来确定"真实世界关系"是否应该成为图形节点而不是图形关系的一些指导原则是什么?
如果有关于这个主题的好书籍,我很想知道。谢谢!
1个回答

19

其中一些东西,例如invention_date,可以像大多数图形数据库中的边缘属性一样存储在边缘上。例如,您可以执行以下操作(代码如下TinkerPop的Blueprints):

Graph graph = new Neo4jGraph("/tmp/my_graph");
Vertex newton = graph.addVertex(null);
newton.setProperty("given_name", "Isaac");
newton.setProperty("surname", "Newton");
newton.setProperty("birth_year", 1643); // use Gregorian dates...
newton.setProperty("type", "PERSON");

Vertex calculus = graph.addVertex(null);
calculus.setProperty("type", "KNOWLEDGE");

Edge newton_calculus = graph.addEdge(null, newton, calculus, "DISCOVERED");
newton_calculus.setProperty("year", 1666);   

现在,让我们稍微扩展一下并加入莱布尼茨:

Vertex liebniz = graph.addVertex(null);
liebniz.setProperty("given_name", "Gottfried");
liebniz.setProperty("surnam", "Liebniz");
liebniz.setProperty("birth_year", "1646");
liebniz.setProperty("type", "PERSON");

Edge liebniz_calculus = graph.addEdge(null, liebniz, calculus, "DISCOVERED");
liebniz_calculus.setProperty("year", 1674);

添加书籍:

Vertex principia = graph.addVertex(null);
principia.setProperty("title", "Philosophiæ Naturalis Principia Mathematica");
principia.setProperty("year_first_published", 1687);
Edge newton_principia = graph.addEdge(null, newton, principia, "AUTHOR");
Edge principia_calculus = graph.addEdge(null, principia, calculus, "SUBJECT");
为了找出牛顿写的关于他所发现的所有书籍,我们可以构造一个图遍历。我们从牛顿开始,沿着他发现的事物的外部链接向前走,然后反向遍历链接以获取该主题的书籍,并再次反向连接以获取作者。如果作者是牛顿,则返回书籍并返回结果。此查询使用Gremlin编写,它是基于Groovy的专用于图遍历的领域特定语言。
newton.out("DISCOVERED").in("SUBJECT").as("book").in("AUTHOR").filter{it == newton}.back("book").title.unique()

因此,我希望我已经展示了如何使用聪明的遍历来避免创建表示边缘的中间节点时可能出现的问题。在小型数据库中,这并不重要,但在大型数据库中,这样做会导致性能大幅下降。

是的,在图形中无法将边缘与其他边缘关联起来确实很遗憾,但这是这些数据库数据结构的限制。有时候将所有内容都作为节点是有意义的,例如在Mediator/CVT中,性能具有更加具体的表现形式。个人可能只想在评论中讨论Tom Cruise在“最后的武士”中的表演。但是,对于大多数图形数据库,我发现应用一些图形遍历可以让我从数据库中获取所需的信息。


非常好的回答!这真的为我解决了很多问题,谢谢。 - Lance

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接