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

时间:2011-09-24 00:30:28

标签: nosql neo4j graph-databases

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

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

simple graph中,您可以像这样建模:

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 个答案:

答案 0 :(得分:19)

其中一些内容(例如invention_date)可以作为属性存储在边缘上,因为在大多数图形数据库中,边可以具有与顶点可以具有属性相同的属性。例如,你可以做这样的事情(代码跟TinkerPop's 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);   

现在,让我们稍微扩展它并加入Liebniz:

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

要找出牛顿在他发现的事情上所写的所有书籍,我们可以构建一个图遍历。我们从Newton开始,按照他发现的链接,然后反向遍历链接以获取有关该主题的书籍,然后再通过链接反向获取作者。如果作者是牛顿,那么回到书中并返回结果。此查询是用Gremlin编写的,{{3}}是一种基于Groovy的域特定语言,用于图遍历:

newton.out("DISCOVERED").in("SUBJECT").as("book").in("AUTHOR").filter{it == newton}.back("book").title.unique()

因此,我希望我已经展示了如何使用巧妙的遍历来避免创建中间节点来表示边缘的问题。在一个小型数据库中它并不重要,但在一个大型数据库中,你将会遭受大量性能攻击。

是的,很遗憾您无法将边缘与图形中的其他边缘相关联,但这是这些数据库的数据结构的限制。有时将所有内容都设为节点是有意义的,例如,在Mediator / CVT中,性能也具有更多的具体性。个人可能希望在评论中仅解决汤姆克鲁斯在“最后的武士”中的表现。但是,对于大多数图形数据库,我发现一些图形遍历的应用可以让我得到我想要的数据库。