在我的上一篇关于图表数据建模的文章中,我们讨论了分类变量,以及如何选择是否将某种东西绘制为节点,属性或标签。
随着这种方式,现在是时候对关系进行深入研究了。在本文中,让我们深入了解域中的哪些关系,以及如何使用它们。
在本文中,我们将占据社交网站的示例,因为它很容易理解图形。人们在他们的账户中创建“朋友”关系,人们“帖子”内容和“喜欢”内容。

关系是动词!
最简单的方式是考虑关系是为了写下有关我们域的声明性句子。写一些真实的事实,并隔离“名词”(以粗体)和“动词”(以斜体为单位)。例子:
一个人发布一篇文章一个人交往另一个人 (朋友)一个人有兴趣
在域的简化视图中,所有名词都是节点,所有的关系都是动词。关系类型是动词的奇异形式。因此,这意味着一个看起来像(:person) – [:posts] – >(:blog)等。
如果你见过RDF,这种方法应该像对象的RDF三重概念一样相当地击打你,那里的关系基本上是句子的谓词。句子中的第一个名词是主题,第二名名词是对象。因此,将数据模型映射到这些组中的3个元素是一个常见的图方法(在RDF-领域中他们实际称之为三星!)
在简单的解释下,任务是将您的域分解为大量简单的声明性句子。这为您提供了一堆节点和关系。然后你有大部分模型,大多数都必须做出命名决定。
这很好……但也很简单。让我们更深入地讨论模型中的关系,以及他们真正卑鄙的是,除了句子中的动词的简单比较。
关系标准化数据
数据标准化通常是针对关系数据库讨论的主题,但它也适用于图形。什么是数据标准化?
数据标准化的目标是减少甚至消除数据冗余,这是应用程序开发人员的重要考虑因素,因为它非常难以将对象存储在多个位置中的关系数据库中。
当我们考虑一下数据(让我们说一个人的主要兴趣)并将其放入一个单独的节点,由关系链接,我们隐含地执行的一件事就是存储一次感兴趣,而不是每个节点。这是一个简单的例子。
非标准化:
CREATE (p1:Person { name: "David", interest: ["Guitar"] })
CREATE (p2:Person { name: "Sarah", interest: ["Guitar"] })
标准化:
CREATE (s:Interest { name: "Guitar" })
CREATE (p1:Person { name: "David" })
CREATE (p1)-[:HAS]->(s)
CREATE (p2:Person { name: "Sarah" })
CREATE (p2)-[:HAS]->(s)
请注意,在归一化示例中,“吉他”存储一次,并在非归一化示例中存储,它存储了两次。我们可以将人节点链接到单独兴趣的事实使我们能够正常化我们的数据。
回到该数据标准化文章,这种方式的优点是更高的凝聚力,更少的数据复制。如果我们想将兴趣的名称从“吉他”到“六弦吉他”更改为什么?使用标准化示例,我们只需要更新一个字符串,我们已经完成了。在“非标准化示例”中,我们可能必须更新数百万节点!那是低凝聚力和重复开始伤害的地方。
您如何知道您的数据是否标准化为“足够”?你应该和这个一起去哪个?这提出了“表还形式”主题,该主题始终在关系数据库的数据建模中教授。就像传统的关系数据库一样,如Boyce-Codd正常形式的第一个,第2版,第3个,第2版,图数据库可以这样做。

> The staircase of normal forms
此处适用的所有理论直接转换为图,但通常用其他来源写入表格。所以在这张图片中,我们看到它谈论“列”,但如果我们将其转换为“节点属性”,我们几乎仍然很好。如何将正常形式应用于图数据建模的完整翻译将不得不等待另一篇文章,但您应该检查这些技术,它们与图形直接相关。定义,但它意味着一个属性在另一个属性上依赖于另一个属性,尽管可以直接携带。
了解你的关系’语义
当我们考虑在模型中实际意味着什么时,有几种不同的种类。在建模时,知道其中哪一个是您所需的关系,因为它限制了可能存在的关系的情况,以及它在现实世界中的实际意义。
关系有一个域名(来自它来自的东西)和它的范围(它将到的东西)。我们认为与将一个节点映射到另一个节点的函数一样的关系。
提示:对于域中的每个关系,弄清楚它在现实世界中的关系,因为它将帮助您和您的型号用户了解它们可以且无法执行此数据。
“有一个”:这表达了一个零件/整个关系,否则称为“组成”。例如,在我们的社交网络中,has_interest是一个“有”的关系,因为人们可能有很多。如果您想要做的是创建一个“一袋物品”,你可能会处理一个关系。“是”:这表达了父母和孩子之间的继承关系。相信它与否,这些往往会在物业图形建模的情况下部分地提出,因为它们与标签很容易。例如,我可以拥有(:人:员工{name:“John”})或者我可以拥有(:person {name:“John”}) – [:IS_A] – >(:员工)。前者更频繁地出现。功能:关系类似于真正的功能,这意味着给定单个域,可以只有一个范围节点。功能关系的示例是has_home_address。你应该拥有其中一个,而不是五个。但是像has_interest这样的关系显然是不起作用的,因为我们的用户可以有许多不同的兴趣!传递:如果关系从a到b,并且从b到c,那么它也是如此,它也是如此。这是一个例子是are_related_to。我的祖父与我父亲有关。我父亲与我有关。所以is_related_to是传递的,因为我的祖父与我有关。回到我们的has_interest关系,这个一个也不同。我可能对吉他感兴趣,但这并不意味着它对我感兴趣!反射:这个问题在物业图建模中没有经常出现,但这意味着该关系意味着每个节点都有其中一个。例如(:人) – [:知道] – >(:人)是反身,因为所有人都知道自己。事实证明,IS_RELATED_TO也是反身!但是has_home_address显然不是。请注意,通过反折叠关系,目标标签(:人)将与源标签相同……因为它是反身!对称:如果关系是真的,另一种方式也是如此。同样,在(:人) – [:知道] – >(:人),它是对称的,因为如果一个知道B,你可以很肯定地知道A.但是你可以看到像has_home_address这样的关系是如何对称性,因为地址不能让一个人作为家庭地址,这将没有意义。香草:我刚刚做到了这个学期。你在这里听到了第一个人。但我会提到任何与上述属性的任何关系,如“香草”。
在其他图建模学科中,他们可能会添加其他人,例如不对称,不确定和其他人,但我们通常不需要考虑这些属性图建模,因为我们可以在双向关系中遍历关系,并且我们通常不会尝试使用它们在RDF / OWL世界中逻辑推断。
提示:一个良好的做法是最大限度地减少您的香草关系。知道其他类型适用的应用对模型的语义有用,对应用程序约束有用,因此您可以在可能的情况下与所有关系一起拥有。但有时它不是!所以不要对它是纯粹主义者。经验法则:如果你不确定,它是香草。
可能需要等待另一篇文章的一件事是如何将香草关系分解成各种类型的潜在不同或其他关系的集合。现在已经足够了,暗示你可以做到这一点,这是一个有趣的练习,看看你的模型中可能是如何。
提示:通过改进vanilla关系意味着和更具体的特定来完成它。在后面的一部分中,我们将提供一个具体例子,即如何用于重构vanilla关系。
决定关系属性
这些关系类型影响了一个重要的下游考虑:是否将属性放在您的关系中。当我们断言关于我们关系的属性时,我们正在将元数据放在上面。
最重要的事情是:
最常见的是,关系根本没有属性。下一个最有可能的情况是它们只有管理元数据,例如创建关系时,更新,创建关系或“版本”整数,以让我们版本关系。这个“管理元数据”可以放在任何类型的关系上,因为它通常从关系类型的语义上离来,因此它可以适合任何关系类型。下一个可能的方案是关系属性实际上是路径元数据,例如重量或距离。诸如Neo4j的某些系统目前不能与专门的全文索引的执行索引关系属性。这意味着有一个原因的原因,可以避免它们,因为可以通过放置在关系属性值的路径上放置标准来获得更快或更具选择性的。

Drake knows his property graph data modeling
因为所有这一切,如果您发现自己希望您可以在关系属性上索引,您应该最有可能考虑将该关系的关系分解为节点。您可能开始将这种关系视为自己的权利,而不是另外两件事的关系。
一如既往地,保持灵活性对您的域名做正确的事情,但一般尝试最小化关系的属性,除非您有一个令人信服的原因,您可以清楚地解释,然后就是他们在那里。
改变关系
“Reify”意味着采取摘要并使其混凝土。当我们将关系视为对象本身时(例如,使用与银行转账的关系,“走私进入动词”以便说话),事情开始难以实现以前的原因。因此,答案是重新确定关系或将其转换为一流的节点,然后简化。
“重新修复关系”也有时被称为制作中间节点,或者创建“超边缘”,其中节点站在可以自身具有关系的关系。
一个例子是银行转账:
/* Very bad */
CREATE (a1:Account)-[:TRANSFER {
id: 555,
amount: 123,
currency: 'USD',
bank: 'Wells Fargo',
time: '2pm'
}]->(a2:Account)
像这样的设计开始看起来像是有意义的,因为帐户传输是两个账户之间的金钱流。但这是一个非常糟糕的模型,因为:
帐户传输本身是我们域名具有丰富的属性,而不仅仅是一个动词。它缺乏标准化数据的机会。我们无法链接到一个单独的预先成立:银行或:货币节点,因为关系不能拥有关系!它会把我们涂成一个角落。当它结合交易555被取消时,我们将如何做到?它不可扩展。:转移关系是香草。它讨论过上面有任何属性吗?它不是对称的,功能,传递或反身。它也不是hast_a或is_a!一团糟。你知道有一堆未弯曲的属性,所以仔细查找来自井法戈的所有基于美元的转移将是痛苦的。
更好的是重新refify:将关系转移到单独的节点中:
/* Much better */
MATCH (wf:Bank { name: "Wells Fargo" })
MATCH (c:Currency { name: "USD" })
CREATE (a:Account)-[:INITIATES]->
(b:BankTransfer { id: 555, time: '2pm', amount: 123 })
-[:RECEIVES]->(b:Account)
CREATE (b)-[:ORIGIN_BANK]->(wf)
CREATE (b)-[:CURRENCY]->(c)
注意这里发生了什么!
[:TRANSFER]已被改将:BankTransfer通过重用银行和货币节点,改善凝聚力并降低冗余来改善数据归一化。关系众多,但它们都更加简单,更容易解释:ORIGIN_BANK, CURRENCY是功能关系!通过重新定义TRANSFER关系方面来INITIATES与RECEIVES,阐明了发生的内容的意图。RECEIVES成为一个功能关系(只有一个帐户可以接收转移),并且INITIATES可以称为A.标准索引适用,所以查看来自富国银行的所有USD的转账都会快速!

结论
在本文中,我们涵盖了数据标准化,关系与动词之间的连接,关系的不同类型的语义可以接受,以及如何将复杂的关系重新冻成更简单的关系。
在教学文章中,我们总是有限的需要表现出简单的例子;建模者的挑战将是在实际工作中将原则应用于您的域名,并且只有一种方法可以做到这一点:练习。
(本文由闻数起舞翻译自Nick Iuviene的文章《Graph Data Modeling: All About Relationships》,转载请注明出处,原文链接:https://medium.com/neo4j/graph-data-modeling-all-about-relationships-5060e46820ce)
原创文章,作者:YCZMEB,如若转载,请注明出处:https://www.beidanyezhu.com/a/91992.html
微信扫一扫