Neo4j图数据建模基础|图重构
  iwbGD3gmtxyT 2023年11月02日 44 0

本部分内容包含:

  • 为什么要重构图数据模型与图
  • 把新的标签添加到图模型中

一、为什么要重构?


重构是改变数据模型和图的过程。


重构有三个原因:

  • 当前建模成功的图并不能满足所有用例。
  • 出现了一个新的用例,您必须在数据模型中加以说明。
  • 用例的Cypher执行得并不理想,尤其是当图的规模改变时。

二、重构步骤

要重构图数据模型和图,必须:

  • 设计新的数据模型。
  • 编写Cypher代码来转换现有图以实现新的数据模型。
  • 重新测试所有用例,可能使用更新的Cypher代码。

三、图标签

(一)运行时的标签

节点标签用作查询的定位点。通过指定一个标签,我们可以指定一个或多个节点的子集来启动查询。使用标签有助于减少检索的数据量。

例如:

MATCH(n)RETURN n:返回图中的所有节点。

MATCH(n:Person)RETURN n:返回图中的所有Person节点。

建模的目标应该是减少查询所涉及的图的大小。

在Cypher中,我们可以生成一个查询计划,显示查询期间发生的操作。下图显示了通过查询的db命中数来检索所有Person节点的查询计划:

Neo4j图数据建模基础|图重构_用例

在本课程的后面部分,您将学习如何生成查询计划。

如果Person节点还有一个标签,该标签是一个人来自的国家,那么您可以使用此密码检索来自美国的所有人:

MATCH(n:US)RETURN n返回图中碰巧是Person节点的所有US节点。

但是,使用这样一个特定的标签可能会有点过头,尤其是如果查询可以是:

匹配(n:个人),其中n.country=“US”返回n

在Cypher中,无法参数化标签,因此将国家/地区保留为属性会使Cypher代码更加灵活。

但是,如果您有一个为一个节点设置多个标签的强大用例,那么您应该这样做。

(二)不要过度使用标签

您应该在数据模型中明智地使用标签。如果它们对您的大多数用例都有帮助,那么应该使用它们。最佳做法是将节点的标签数量限制为4个。

以下是数据模型中过度使用标签的示例:

Neo4j图数据建模基础|图重构_层次结构_02

在这里,我们看到Person节点有一个标签,表示一个人来自的国家,如前面所述。

此外,我们还可以看到电影节点的多个标签。标签表示电影可用的语言。

这是另一个类似的场景,您必须确定一个重要用例是否与电影的语言相关。同样,如果对节点使用属性就足够了,那么最好不要使用标签。

(三)新用例

下面是一个示例,添加标签将有助于我们在运行时进行查询。

如果我们添加一个新的用例会怎么样:

用例#10:1950年之前出生的演员是什么?

下面是用于测试此用例的Cypher查询:

(四)分析查询

您可以使用PROFILE关键字查看查询的性能。

PROFILE MATCH (p:Person)-[:ACTED_IN]-()
WHERE p.born < '1950'
RETURN p.name

下图给出上述性能分析的结果:

Neo4j图数据建模基础|图重构_数据模型_03

因为缓存是自动填充的,所以有时很难用一个小数据集来衡量性能。也就是说,db命中数和经过的时间可能无法比较。但是,您可以看到的是查询中检索到的行数,并且可以比较该行数。

在这个查询的第一步中,我们看到返回了5行。可以想象,如果这是一个有数百万个节点的完全加载的图,那么在步骤1中,它将需要检索许多Person节点,其中一些不是参与者。优化此检索的一种方法是更改数据模型,以包含Person节点的参与者标签。

(五)重构模型

如果我们重构,最初的逐标签节点扫描将只检索参与者节点。

下面是我们将在图中创建的重构实例模型:

Neo4j图数据建模基础|图重构_层次结构_04

(六)重构图

使用Cypher,我们可以轻松变换图形。使用您将在下一个问题中执行的这段代码,我们可以找到所有具有ACTED_IN关系的Person节点。然后,我们为节点设置一个标签。

MATCH (p:Person)
WHERE exists ((p)-[:ACTED_IN]-())
SET p:Actor

在后文中,我们将要重构图以添加Actor标签。

四、添加Actor标签

下面是我们将在图中创建的重构实例模型,在图中,我们向一些Person节点添加了一个Actor标签:

Neo4j图数据建模基础|图重构_数据模型_05

(一)配置查询

执行此查询:

PROFILE MATCH (p:Person)-[:ACTED_IN]-()
WHERE p.born < '1950'
RETURN p.name

在这个查询的第一步中,我们看到返回了5个人的行。

(二)重构图

使用Cypher,您可以轻松地转换图以添加演员标签。

执行此密码将参与者标签添加到相应的节点:

MATCH (p:Person)
WHERE exists ((p)-[:ACTED_IN]-())
SET p:Actor

图中有5个Person节点,但只有4个节点的关系为:ACTED\u。因此,上面的查询应该将Actor标签应用于五个人节点中的四个节点。

(三)查询性能剖析

现在我们已经重构了图形,我们必须再次更改查询和剖析。

执行此查询:

PROFILE MATCH (p:Actor)-[:ACTED_IN]-()
WHERE p.born < '1950'
RETURN p.name

在该查询的第一步中,我们看到返回了4个Actor行。

五、重构后的重新测试

重构后重新测试

重构图形后,应该重新访问用例的所有查询。

您应该首先确定是否需要重写任何查询以利用重构。

接下来,我们重写一些查询以利用重构。

在对实际应用程序进行测试期间,尤其是使用完全缩放的图形时,您还可以分析新查询,以查看它是否提高了性能。在我们使用的小实例模型上,您不会看到显著的改进,但您可能会看到检索到的行数有所不同。

用例1:人们在电影中扮演了什么角色?

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle'
RETURN p.name AS Actor

新代码变成:

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle'
RETURN p.name AS Actor

分析查询

对于使用Person标签的查询,我们可以看到这个结果,它首先检索5个Person节点:

Neo4j图数据建模基础|图重构_层次结构_06

对于使用Actor标签的查询,我们看到这样一个结果,它首先检索4个Actor节点,这是对这个小图的一个轻微改进:

Neo4j图数据建模基础|图重构_用例_07

用例3:一个人演了什么电影?

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS Movie

新代码成为:

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS Movie

用例5:谁是电影中最年轻的演员?

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Hoffa'
RETURN p.name AS Actor, p.born as `Year Born` ORDER BY p.born DESC LIMIT 1

新代码成为:

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Hoffa'
RETURN p.name AS Actor, p.born as `Year Born` ORDER BY p.born DESC LIMIT 1

用例6:一个人在电影中扮演什么角色?

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)-[r:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle' AND
p.name = 'Meg Ryan'
RETURN r.role AS Role

新代码成为:

MATCH (p:Actor)-[r:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle' AND
p.name = 'Meg Ryan'
RETURN r.role AS Role

用例8:演员在哪些戏剧电影中扮演角色?

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks' AND
'Drama' IN m.genres
RETURN m.title AS Movie

新代码成为:

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks' AND
'Drama' IN m.genres
RETURN m.title AS Movie

用例#10:1950年之前出生的演员是什么?

为了测试的完整性,我们添加了这个新用例。

我们重写此查询以使用Actor标签。

原始代码:

MATCH (p:Person)
WHERE p.born < '1950'
RETURN p.name

新代码成为:

MATCH (p:Actor)
WHERE p.born < '1950'
RETURN p.name

查询性能剖析

如果您有一个缩放图(节点/关系比我们在本课程中使用的要多),那么还应该使用PROFILE关键字来比较重构后查询的性能。

六、使用Actor标签重新测试

您已经重构了图形,将Actor标签添加到相应的节点。您现在必须根据重构图重新测试用例。

请注意,如果您有一个完全缩放的图,您将看到使用Person标签和Actor标签的查询之间的差异。您可以分析查询以比较在after之前检索到的行。

用例1:人们在电影中扮演了什么角色?

运行此Cypher代码,使用电影《西雅图不眠之夜》测试此用例。

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle'
RETURN p.name AS Actor

它应该返回两个演员的名字,Tom Hanks和Meg Ryan。

用例3:一个人演了什么电影?

运行此Cypher代码以使用TomHanks测试此用例。

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS Movie

它应该返回电影:Apollo 13 and Sleepless in Seattle。

用例5:谁是电影中最年轻的演员?

运行此Cypher代码,用电影Hoffa测试此用例。

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE m.title = 'Hoffa'
RETURN p.name AS Actor, p.born as `Year Born` ORDER BY p.born DESC LIMIT 1

应该返回Danny DeVito和他的出生年份。

用例6:一个人在电影中扮演什么角色?

通过电影《西雅图不眠之夜》和梅格·瑞安(MegRyan)运行此密码来测试此用例。

MATCH (p:Actor)-[r:ACTED_IN]-(m:Movie)
WHERE m.title = 'Sleepless in Seattle' AND
p.name = 'Meg Ryan'
RETURN r.role AS Role

应该返回:Annie Reed

用例8:演员在哪些戏剧电影中扮演角色?

运行此Cypher代码,与TomHanks一起测试此用例。

MATCH (p:Actor)-[:ACTED_IN]-(m:Movie)
WHERE p.name = 'Tom Hanks' AND
'Drama' IN m.genres
RETURN m.title AS Movie

如果在此查询中将“Drama”更改为“Comedy”,则会返回不同的结果。

用例#10:1950年之前出生的演员是什么?

运行此Cypher代码以测试1950年的此用例。

MATCH (p:Actor)
WHERE p.born < '1950'
RETURN p.name

应该返回:Danny DeVito 和Jack Nicholson

运行此Cypher返回图中的所有内容。

最后,请使用如下脚本全面测试一下吧:

MATCH (n)
RETURN n

七、添加Director标签

在上一个重构中,将Actor标签添加到图中的节点。

现在,我们继续修改上面创建的查询,把标记有Person的节点而且具有传出关系DIRECTED的节点再添加标签Director。

根据以往的经验,在重构图后,必须对任何受影响的用例进行测试。

用例2:谁导演了一部电影?

你可以将下面的查询复制到查询窗格中,并将其修改为使用Director标签,然后使用电影Hoffa测试此用例。

MATCH (p:Person)-[:DIRECTED]-(m:Movie)
WHERE m.title = 'Hoffa'
RETURN p.name AS Director

正常情况下,上面的查询运行结果将返回Danny DeVito。

八、语义正交标签问题

“语义正交”是一个奇特的术语,意思是标签之间不应该有任何关系。您应该小心,不要在不同的上下文中使用相同类型的标签。例如,对所有类型的节点使用region对于大多数查询都是不合适的。

下面是一个示例,其中Person节点和User节点都用区域标记。如果没有区域对这两种类型的节点都重要的用例,那么对Person节点和User节点使用这些相同的标签是没有帮助的。

因此,不要像下图这样操作:

Neo4j图数据建模基础|图重构_层次结构_08

描述类层次结构

此外,还应该避免标记节点,以描述层次结构。

假设我们有这样的银幕演员协会成员等级:

Neo4j图数据建模基础|图重构_层次结构_09

这通常被称为“继承”或“is-A”关系。如果节点具有多个表示层次结构的标签,则不应执行此操作,例如:

Neo4j图数据建模基础|图重构_用例_10

而是应该这样:

Neo4j图数据建模基础|图重构_层次结构_11




【版权声明】本文内容来自摩杜云社区用户原创、第三方投稿、转载,内容版权归原作者所有。本网站的目的在于传递更多信息,不拥有版权,亦不承担相应法律责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@moduyun.com

  1. 分享:
最后一次编辑于 2023年11月08日 0

暂无评论

推荐阅读
iwbGD3gmtxyT