读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历
  5b99XfAwWKiH 2023年11月02日 54 0


读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_Neo4j图数据库

使用Neo4j核心Java API进行遍历

第一种遍历方法是使用Neo4j核心Java API提供的标准方法。第一次遍历可以描述为“从代表用户John Johnson的节点开始, 查找这个用户看过的所有电影”。要导航到代表John已经看过的电影的节点, 我们将从代表John Johnson的节点开始, 然后按照所有HAS_SEEN关系找到目标电影。

第一个任务是定位遍历的起始节点: 用户John Johnson

Neo4j核心Java API具有单个查找方法, 可用于从图形数据库中加载节点——GraphDatabase Service.getNodeById(Long id) 。 这种方法通过内部标识符加载节点, 在Neo4j中用java.util.Long数字表示。 我们假设已知用户John Johnson节点的ID且ID以静态变量JOHN_JOHNSON_NODE_ID存储的。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_java_02


理想情况下, 我们将基于一些属性值而不是节点ID寻找起始节点, 例如, 通过姓名或者电子邮件地址找到user节点。遍历直接关系

为了找到John已经看过的所有电影, 首先要加载John节点, 然后从这个节点开始循着所有的HAS_SEEN关系查找。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_加载_03


使用Node.getRelationships方法以获得始于或终于给定节点的所有关系,这个方法返回了一个java.lang.Iterable对象, 该对象包含org.neo4j.graphdb.Relationship实例, 它可以用典型的Java方法通过for循环迭代。例如userJohn.getRelationships() 方法将返回起始和终止于起始节点的所有关系, 包括并不感兴趣的IS_FRIEND_OF关系。要识别仅仅是感兴趣的HAS_SEEN类型的关系, 需要在循环内检查每一个关系的名字。要得到与关系相关的节点, org.neo4j.graphdb.Relationship接口定义了两个方法: getStartNode , 返回关系的起始节点; getEndNode, 返回关系的终止节点。

Node.getRelationships() 方法不能保证遍历关系的顺序。 在本例子中,电影的顺序不重要, 但是如果需要排序的结果, 就必须遍历完了再做一次排序。

在前面的例子中, 从代表用户John Johnson的节点开始, 通过逐一检查与起始节点相连的关系的名字过滤感兴趣的关系。 用名字过滤关系是图形遍历的一个非常常用的方法, Neo4j提供了一个方便使用的专用API。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_lua_04


Neo4j核心Java API除了可以过滤除关系类型外的关系, 也可以加载从起始user节点向外的关系。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_java_05

遍历深度为2的关系

查找John的朋友喜欢而John还没有看过的电影,John有两个朋友, Jack和Kate。 Jack看过Fargo和Alien, Kate看过Heat。 但是John早已经看过Fargo, 因此我们希望从遍历获得两部电影:Alien和Heat。

首先, 我们从起始节点开始按照IS_FRIEND_OF关系查找John的所有朋友。 其次, 从代表朋友的节点开始沿着HAS_SEEN关系查找他们已经看过的电影。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_Neo4j图数据库_06


本程序的第一部分用局部变量存储了John的朋友。首先, 迭代从起点开始的所有IS_FRIEND_OF关系, 而不考虑方向。由于并不知道John是否是关系的起点还是终点, 因此不能使用Relationship.getStartNode或Relationship.getEndNode方法以确定关系的其他参与者。 Relationship.getOtherNode(Node node)方法。 给定关系的一端, 该方法就给出关系的另一端。在这种情况下, 因为知道关系的一端是John节点, 所以可以利用该方法查找John的朋友。最后, 添加朋友节点到java.lang.Set局部变量friends中。

在本程序的第二部分,找到了John的朋友看过的所有电影。 对每一个存储在临时局部变量中的节点,检查所有向外的HAS_SEEN关系,加载HAS_SEEN的终止节点(代表一部电影) 并作为结果将其存到局部set变量moviesFriendsLike中。

必须确保在输出中删除John看过的电影, 需要找到John看过的所有电影并从结果中删除。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_加载_07

在Java代码中, 有许多实现Iterable接口的例子——例如, 所有的java.util.Collection类, 像java.util.Set和java.util.List实现, 以及java.util.Vector和java.util.Stack。 但是Node.getRelationships 方法不返回任何这些著名的类,Node.getRelationships方法返回了一个Iterable对象。org.neo4j.kernel.impl.util.ArrayIntIterator既实现了Java Iterable接口, 也实现了Iterator接口, 并且是一个围绕java.util.Iterator的瘦包装类。 在对结果迭代之前实际上还没有访问结果中包含的元素。 这一实现是在第一次访问时延迟加载的, 一旦使用, 就不能再次使用——变为无效(这是Java迭代器期望的行为) 。 如果从Neo4j的Iterable接口加载大量的结果到你自己的Java list或set中, 必须意识到你的程序代码需要大量的Jave堆内存, 这样在运行程序时就会有可能出现OutOfMemoryError异常。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_加载_08


这个解决方案看上去比较好, 但是, 当面对有用户看过的几百万部电影时, 存在一个潜在的瓶颈(因为在每一次循环中都必须检查John是否已经看过) 。 在有限的数据集中, 这不会有什么影响, 但是, 必须指出的是, 当面对大数据集时, 编写简单的代码有时会更好, 即使多写一点也没关系。 在本例中, 一旦往前查找, 就会找到John看过的所有电影, 然后用这个数据集在for循环中过滤电影。

使用Neo4j的遍历API进行遍历

使用Neo4j内置遍历结构

Neo4j遍历API是一个具有流畅创建器API且基于回调函数的框架, 它允许你在一行代码中以可表达的方式创建遍历规则。 遍历API的主要部分是org.neo4j.graphdb.traversal.TraversalDescription接口, 它定义了创建器的方法用于描述遍历器行为。 Traversal Description(遍历描述) 是一个不可变的对象, 用于定义遍历规则。 添加任何新的遍历规则(在TraversalDescription接口中调用创建器方法中的任何一个) 总是返回新的TraversalDescription实例。你可以将遍历比作一个机器人, 按照一组定义好的遍历顺序规则和跟踪关系、 在结果中需要包含的关系和节点等, 通过关系从一个节点跳跃到另一个节点。

使用Neo4j遍历API查找朋友看过的电影:

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_Neo4j图数据库_09


Neo4j提供了一个默认的TraversalDescription接口实现, 它可以用静态方法Traversal.description() 实例化,由于很少需要自己提供这个实现, 因此, 通常从此开始创建TraversalDescription。 下一步定义遍历中的要包含关系, TraversalDescription维护使用TraversalDescription.relationships方法添加的关系列表, 并且只有在这个列表中包含的关系才能被遍历。 可以添加关系类型而不必指定方向, 在这种情况下, 两个方向都可以查找, 或者也可以定义一个相对于起始节点的查找方向。在下一行指定遍历器如何处理在遍历期间遇到的节点和关系的唯一性。我们期望每一个节点恰好访问一次, 因此, 在Uniqueness.NODE_GLOBAL(全局节点唯一性) 中设置唯一性,例如, 其他允许的值是Uniqueness.NODE_PATH(节点路径唯一性) , 即当从起点到终点的路径是唯一的时候, 它允许遍历同一个节点多次; 而Uniqueness.RELATIONSHIP_GLOBAL(全局关系唯一性) 允许遍历每一个关系仅仅一次。最后, 给TraversalDescription添加一个评估函数(Evaluator)。评估函数负责两个决策(作为Neo4j遍历API的一部分) :1.决定正在遍历的节点是否添加到遍历的结果中。2.决定遍历是否沿着当前图形的路径继续遍历下去, 如果评估到现在的路径应该放弃, 就移动到下一个路径(如有可能) 。Neo4j中的评估函数是由org.neo4j.graphdb.traversal.Evaluator接口定义的, Neo4j提供了大量可供使用的方便、 现成的实现方法。 这些方法可以通过org.neo4j.graphdb.traversal.Evaluator类中的静态工厂方法访问。 在程序4-6中使用了Evaluators.atDepth(int depth) 评估函数, 该评估函数仅接受指定深度上的所有节点, 并从起始节点计数。 另外, 该评估函数还阻止任何超出指定深度的遍历。

实现一个自定义评估函数:现在需要改进前一节例子中的代码以从结果中排除John看过的电影。 要实现这个目的, 需要给遍历描述添加新的规则。 Evaluator接口定义了一个需要实现的方法public Evaluation

evaluate(Path path) 。 这个方法接受org.neo4j.graphdb.Path类型的单一参数, 这个参数代表从起始节点到目前节点遍历的所有节点和关系。这个接口定义了许多便利的方法用以收集有关遍历当前状态的信息。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_lua_10


不要混淆路径的起始节点终止节点与关系的起始节点终止节点是很重要的。 关系的起始与终止节点依赖于关系的方向。 路径的起始与终止节点依赖于遍历顺序

evaluate方法能够返回org.neo4j.graphdb.traversal.Evaluation类型,这是一种Java枚举类型, 具有四种可能的值。 基于返回的Evaluator, 遍历器决定是停止(Neo4j术语为修正) 还是继续。 另外, 返回的Evaluator值用以确定是否将现在的节点保存在结果中(包括) 还是丢弃(排除) 。 这两个变量的结合定义了Evaluator枚举的四个值。

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_加载_11

用自定义评估函数改进的遍历定义

读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历_Neo4j图数据库_12


其实已经有了一个评估函数, 这个评估函数仅包含深度2的节点。 最好是不要删除它, 只把新的加进去即可。 在Neo4j遍历API中, 可以同时建立多个评估函数, 因此可以添加多个评估函数到单个遍历描述(Travelsal Description) 中。 如果在遍历的过程中包含多个评估函数, 那么使用布尔代数: 对包含在结果中的当前节点, 所有的评估函数都应该返回带一个INCLUDE元素(INCLUDE_AND_CONTINUE或INCLUDE_AND_PRUNE) 的评估值。 类似地, 要沿着同一路径继续往下遍历, 所有的评估函数都要返回一个CONTINUE评估函数(INCLUDE_AND_CONTINUE或EXCLUDE_AND_CONTINUE) 。

Neo4j数据库系列:

读书笔记——Neo4j实战 使用Neo4jAPI创建节点和关系

​读书笔记——Neo4j实战 使用Neo4jAPI 图形遍历

读书笔记——Neo4j实战 数据索引


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

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

暂无评论

推荐阅读
  kG7ef0NqClb6   2023年11月13日   32   0   0 java
  u2N3sQ7nC0dn   2023年11月13日   28   0   0 java
  rCd1NYtlhh0U   2023年11月13日   31   0   0 java
  rCd1NYtlhh0U   2023年11月13日   36   0   0 java
  Ydqp9G8hCwM0   2023年11月13日   35   0   0 java
  bSubxmxwEKmm   2023年11月13日   30   0   0 java
5b99XfAwWKiH