图解Janusgraph系列-查询图数据过程源码分析

大家好,我是洋仔,JanusGraph图解系列文章,实时更新~

图数据库文章总目录:

  • 整理所有图相关文章,请移步(超链):图数据库系列-文章总目录
  • 地址:https://liyangyang.blog.csdn.net/article/details/111031257

源码分析相关可查看github(求star~~): https://github.com/YYDreamer/janusgraph

下述流程高清大图地址:https://www.processon.com/view/link/5f471b2e7d9c086b9903b629

版本:JanusGraph-0.5.2

转载文章请保留以下声明:

作者:洋仔聊编程
微信公众号:匠心Java
原文地址:https://liyangyang.blog.csdn.net/

一:查询场景

1.1 图数据

使用JanusGraph官方提供的测试图诸神之图来测试,如下图:

具体的诸神之图的创建分析,请看《JanusGraph-官方测试图:诸神之图分析》文章

1.2 查询语句

查询语句如下:

    @Testpublic void searchTest(){// 获取name为hercules节点GraphTraversal<Vertex, Vertex> herculesVertex = graph.traversal().V().has("name", "hercules");// hercules节点战斗(battled)过12次的怪兽(monster)GraphTraversal<Vertex, Vertex> monsterVertices = herculesVertex.outE().has(T.label, "battled").dedup().has("time", "12").inV();// 获取monsterVertices节点对应的主人GraphTraversal<Vertex, Vertex> plutoVertex = monsterVertices.inE("pet").outV().has("age", 4000);if (plutoVertex.hasNext()){Vertex next = plutoVertex.next();// 输出主人的姓名System.out.println(next.property("name"));}}

执行结果:

vp[name->pluto]

我们查询就是诸神之图的左下角部分,包含了

  • vertex的查找
  • edge的查找
  • property的过滤
  • signature key的过滤(time)
  • composite index的使用(name)
  • 第三方索引支持的mixed index使用(age)

二: 查询流程分析

查询流程可以大致分为三部分:组装查询语句、优化查询语句、执行算子链

预备知识

gremlin语句官网:http://kelvinlawrence.net/book/Gremlin-Graph-Guide.html

在gremlin语句中,查询语句的类型其中包含三种哦:初始语句、中间语句、最终语句(触发执行查询语句)

  • 初始语句:代表开始gremlin的语句,类似于V()、E()两种
  • 中间语句:不会触发查询语句执行的语句,类似于has()、outE()、inV()等,大部分都是中间语句
  • 最终语句:也叫作触发语句,这些语句会触发查询语句的执行! 访问图库并进行图数据查询;类似于hasNext()、toList()等语句

只有在执行到最终语句部分时,gremlin语句才会真正的执行!

2.1 组装查询语句

Janusgrahp的查询中,首先做的就是组装查询对象; 根据我们写的Gremlin语句,将gremlin语句拆分成不同的算子;不同的语句组装成对应的算子步骤step对象保存,生成一个查询执行算子step链;

在组装查询语句时,所有的查询Gremlin语句,通常都是通过.V()或者.E()开头,在这两部分中会创建一个查询对象,用于存放之后的每一个查询step

例如上述执行查询语句,当执行完成以下语句时,便是进行组装查询对象,不没有真正的去查询底层存储的数据!

      // 获取name为hercules节点GraphTraversal<Vertex, Vertex> herculesVertex = graph.traversal().V().has("name", "hercules");// hercules节点战斗(battled)过12次的怪兽(monster)GraphTraversal<Vertex, Vertex> monsterVertices = herculesVertex.outE().has(T.label, "battled").dedup().has("time", "12").inV();// 获取monsterVertices节点对应的主人GraphTraversal<Vertex, Vertex> plutoVertex = monsterVertices.inE("pet").outV().has("age", 4000);

执行完成后,查看组装后的查询对象:

其中包含:图实例对象、事务对象、查询步骤链等,我们看下最终的查询步骤链,在对象中steps的属性:

steps = {ArrayList@5271}  size = 100 = {GraphStep@5350} "GraphStep(vertex,[])"1 = {HasStep@5351} "HasStep([name.eq(hercules)])"2 = {VertexStep@5352} "VertexStep(OUT,edge)"3 = {HasStep@5353} "HasStep([~label.eq(battled)])"4 = {DedupGlobalStep@5354} "DedupGlobalStep"5 = {HasStep@5355} "HasStep([time.eq(12)])"6 = {EdgeVertexStep@5356} "EdgeVertexStep(IN)"7 = {VertexStep@5357} "VertexStep(IN,[pet],edge)"8 = {EdgeVertexStep@5358} "EdgeVertexStep(OUT)"9 = {HasStep@5359} "HasStep([age.eq(4000)])"

上述的gremlin查询语句主要被分为10个算子step执行;

2.2 优化查询语句

Gremlin的查询优化中,存在由策略模式设计实现的19个执行策略类,包含:

  1. Connective Strategy:连接策略
  2. Match Predicate Strategy:匹配谓词策略
  3. Filter Ranking Strategy:过滤排名策略
  4. Inline Filter Strategy:内联过滤策略
  5. Incident To Adjacent Strategy:相邻策略事件
  6. Adjacent To Incident Strategy:邻近事件策略
  7. Early Limit Strategy:早期限制策略
  8. Count Strategy:计数策略
  9. Repeat Unroll Strategy:重复展开策略
  10. Path Retraction Strategy:路径收回策略
  11. Lazy Barrier Strategy:惰性屏障策略
  12. Adjacent Vertex Has Id Optimizer Strategy:相邻顶点有Id优化器策略
  13. Adjacent Vertex Is Optimizer Strategy:相邻顶点是优化器策略
  14. Adjacent Vertex Filter Optimizer Strategy:相邻顶点过滤器优化器策略
  15. JanusGraph Local Query Optimizer Strategy:JanusGraph局部查询优化器策略
  16. JanusGraph Step Strategy:JanusGraph步骤策略
  17. JanusGraphIo Registration Strategy:JanusGraphIo注册策略
  18. Profile Strategy:配置文件策略
  19. Standard Verification Strategy:标准验证策略

将上述的查询算子链循环执行所有的19个策略,针对不同的策略进行不同的处理;

我们主要收一下上述的Match Predicate Strategy、:

Filter Ranking Strategy

依据优先级调整算子链算子执行顺序! 采用while+for循环的方式,将所有的算子调整到对应的位置!

 while (modified) {modified = false;final List<Step> steps = traversal.getSteps();for (int i = 0; i < steps.size() - 1; i++) {// do somthing// update modified!}}

优先级如下,数字越大,优先级越低,执行的时机越靠后:

        if (!(step instanceof FilterStep || step instanceof OrderGlobalStep))return 0;else if (step instanceof IsStep || step instanceof ClassFilterStep)rank = 1;else if (step instanceof HasStep)rank = 2;else if (step instanceof WherePredicateStep && ((WherePredicateStep) step).getLocalChildren().isEmpty())rank = 3;else if (step instanceof TraversalFilterStep || step instanceof NotStep)rank = 4;else if (step instanceof WhereTraversalStep)rank = 5;else if (step instanceof OrStep)rank = 6;else if (step instanceof AndStep)rank = 7;else if (step instanceof WherePredicateStep) // has by()-modulationrank = 8;else if (step instanceof DedupGlobalStep)rank = 9;else if (step instanceof OrderGlobalStep)rank = 10;

调整过后的算子链:

steps = {ArrayList@5271}  size = 100 = {GraphStep@5350} "GraphStep(vertex,[])"1 = {HasStep@5351} "HasStep([name.eq(hercules)])"2 = {VertexStep@5352} "VertexStep(OUT,edge)"3 = {HasStep@5353} "HasStep([~label.eq(battled)])"4 = {HasStep@5355} "HasStep([time.eq(12)])"5 = {DedupGlobalStep@5354} "DedupGlobalStep"6 = {EdgeVertexStep@5356} "EdgeVertexStep(IN)"7 = {VertexStep@5357} "VertexStep(IN,[pet],edge)"8 = {EdgeVertexStep@5358} "EdgeVertexStep(OUT)"9 = {HasStep@5359} "HasStep([age.eq(4000)])"

Inline Filter Strategy

同作用域多个算子内联为一个一个算子

相同作用域下,相同算子的内联整合:

例如,下述语句的两个has会在第一部分的步骤生成两个has算子

has(T.label,"battled").has("time", "12")

两个has同时作用于V()在同一作用域,又是相同类型,所以在Inline Filter Strategy策略中,会将两个has内联为一个has算子

相同作用域下,不同算子的内联整合:

包含好多种情况,也是举一个例子,如果一个has算子一个vertex算子如下:

outE().has(label,"battled")

满足以下三个条件:

  1. has算子的前置算子为vertex算子(也就是上述out()产生的算子类型)
  2. 前置的vertex算子返回的类型时edge类型,也就是outE、inE、bothE这三种
  3. 前置的vertex算子,也就是outE、inE、bothE这三种没有指定edge label

的前提下,has算子的内容为过滤Edge的label;则可以将上述的两种组合成一个vertex算子,可以用以下语句表示:

outE().has(label,"battled") ==内联为==>  outE("battled")

调整过后的算子链:

steps = {ArrayList@5271}  size = 90 = {GraphStep@5350} "GraphStep(vertex,[])"1 = {HasStep@5351} "HasStep([name.eq(hercules)])"2 = {VertexStep@5463} "VertexStep(OUT,[battled],edge)"3 = {HasStep@5355} "HasStep([time.eq(12)])"4 = {DedupGlobalStep@5354} "DedupGlobalStep"5 = {EdgeVertexStep@5356} "EdgeVertexStep(IN)"6 = {VertexStep@5357} "VertexStep(IN,[pet],edge)"7 = {EdgeVertexStep@5358} "EdgeVertexStep(OUT)"8 = {HasStep@5359} "HasStep([age.eq(4000)])"

Incident To Adjacent Strategy

作用也是合并算子,但是合并的是vertex算子(outE、inE、bothE)和edge算子(outV、inV、bothV);

举个例子,如下语句,包含一个vertex算子(inE)和一个edge算子(outV)

inE("pet").outV()

这个策略的作用就是将这两个算子合成一个in("pet")算子,如下:

inE("pet").outV() ==合并==> in("pet")

调整后的算子链:

steps = {ArrayList@5271}  size = 80 = {GraphStep@5350} "GraphStep(vertex,[])"1 = {HasStep@5351} "HasStep([name.eq(hercules)])"2 = {VertexStep@5463} "VertexStep(OUT,[battled],edge)"3 = {HasStep@5355} "HasStep([time.eq(12)])"4 = {DedupGlobalStep@5354} "DedupGlobalStep"5 = {EdgeVertexStep@5356} "EdgeVertexStep(IN)"6 = {VertexStep@5514} "VertexStep(IN,[pet],vertex)"7 = {HasStep@5359} "HasStep([age.eq(4000)])"

JanusGraph Step Strategy

获取GraphStep算子,也就是V()、E()等对应产生的算子; 将Gremlin的GraphStep算子转换为图库自身的JanusGraphStep算子对象;

JanusGraphStep算子对象中包含查询图库获取数据的lambda语句;在下一部分执行查询中通过调用get()来进行图库数据查询!

调整后的算子链:

steps = {ArrayList@5291}  size = 70 = {JanusGraphStep@5630} "JanusGraphStep([],[name.eq(hercules)])"1 = {JanusGraphVertexStep@5610} "JanusGraphVertexStep([time.eq(12)])"2 = {DedupGlobalStep@5341} "DedupGlobalStep"3 = {EdgeVertexStep@5343} "EdgeVertexStep(IN)"4 = {JanusGraphVertexStep@5611} "JanusGraphVertexStep(IN,[pet],vertex)"5 = {NoOpBarrierStep@5513} "NoOpBarrierStep(2500)"6 = {HasStep@5346} "HasStep([age.eq(4000)])"

其他

策略共19种,每一种都有自己的作用;优化算子链、优化调整index的使用、count语句的优化、repeat多度路径查询的优化等等

查询语句优化部分,对用户自定义的gremlin语句进行正确性验证优化查询过程的算子链两个作用;

最终,执行到第优化后的算子链为:

steps = {ArrayList@5271}  size = 70 = {JanusGraphStep@6302} "JanusGraphStep([],[name.eq(hercules)])"1 = {JanusGraphVertexStep@6124} "JanusGraphVertexStep([time.eq(12)])"2 = {DedupGlobalStep@5354} "DedupGlobalStep"3 = {EdgeVertexStep@5356} "EdgeVertexStep(IN)"4 = {JanusGraphVertexStep@6125} "JanusGraphVertexStep(IN,[pet],vertex)"5 = {NoOpBarrierStep@5857} "NoOpBarrierStep(2500)"6 = {HasStep@5359} "HasStep([age.eq(4000)])"

扩展:

Gremlin查询语句执行过程,把执行语句转为由多个step组成对应的算子链,经过优化策略优化调整转化为确定最终可执行算子链。

Gremlin语言执行过程:

  • [D] ecoration-应用级的迭代逻辑上迭代策略
  • [O]ptimization在ThinkPop图架构级别上高效迭代策略
  • [P]rovider optimization 从系统,语言,驱动级别优化
  • [F]inalization 对以上策略做调整确定最终执行策略
  • [V]erification:迭代策略做迭代引擎的验证
    如下图所示:

2.3 执行算子链

执行算子链的触发时机在最终语句时进行触发;也就是我们示例语句中下述语句的hasNext()

plutoVertex.hasNext()

对于hasNext的源码伪代码如下:

    @Overridepublic boolean hasNext() {if (null != this.nextEnd)return true;else {try {while (true) {this.nextEnd = this.processNextStart();if (null != this.nextEnd.get() && 0 != this.nextEnd.bulk())return true;elsethis.nextEnd = null;}} catch (final NoSuchElementException e) {return false;}}}

对于数据执行调用get()触发lambda表达式内容;

针对不同的算子针对中间结果进行顺序遍历;

三:源码分析

源码分析已经push到github:https://github.com/YYDreamer/janusgraph

四:总结

整体流程如下:

图解Janusgraph系列-查询图数据过程源码分析相关推荐

  1. Android服务查询完整过程源码分析

    Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...

  2. Android 数据Parcel序列化过程源码分析

    在Android系统中,所有的服务都必须注册到ServiceManger中,当客户进程需要请求某一服务时,首先从服务管家ServiceManger中查找出该服务,然后通过RPC远程调用的方式使用该服务 ...

  3. SpringBoot2 | SpringBoot启动流程源码分析(一)

    首页 博客 专栏·视频 下载 论坛 问答 代码 直播 能力认证 高校 会员中心 收藏 动态 消息 创作中心 SpringBoot2 | SpringBoot启动流程源码分析(一) 置顶 张书康 201 ...

  4. 【SRIO】5、Xilinx RapidIO核例子工程源码分析

    目录 一.软件平台与硬件平台 二.打开例子工程 三.例子工程详解 3.1 工程概述 3.2 工程结构 3.3 工程分析 四.工程源码分析 3.1 顶层模块srio_example_top.v源码分析 ...

  5. 5.Xilinx RapidIO核例子工程源码分析

    https://www.cnblogs.com/liujinggang/p/10091216.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:V ...

  6. Android服务注册完整过程源码分析

    前面从不同片段分析了Android的Binder通信机制,本文结合前面介绍的内容,对整个Android的Binder通信过程进行一次完整的分析.分析以AudioService服务的注册过程为例. 由于 ...

  7. 【高速接口-RapidIO】5、Xilinx RapidIO核例子工程源码分析

    期待大家的一键三连,爱你们!!! 总目录:总目录(经验分享) 献上链接: [高速接口-RapidIO]2.RapidIO串行物理层的包与控制符号 [高速接口-RapidIO]3.RapidIO串行物理 ...

  8. Activity启动流程源码分析(基于Android N)

    Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...

  9. Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析

    Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析 在NTM系列文章(二)中,我们已经成功运行了一个ntm工程的源代码.在这一章中,将对它的源码实现进行分析. ...

最新文章

  1. 解决select 下拉框选择器 input输入框 、时间日期选择器el-date-picker 赋值后,出现无法修改选中更改问题
  2. STM32F4+Wi-Fi+EDP 向 OneNet 上传数据
  3. 前端学习(1261):接口调用fetch方法
  4. mysql导入dat文件_MySql导入和抽取大数量级文件数据
  5. DrawerLayout和NavigationView的简单实用
  6. 人工神经网络理论、设计及应用_红层软岩大直径素混凝土置换桩复合地基设计理论及应用研究——以成都 ICON云端项目为例...
  7. 黑客攻击行为特征分析 反攻击技术综合性分析报告
  8. dubbo分布式事务解决方案_阿里架构师谈:高并发+分布式+秒杀+微服务+性能优化...
  9. 用matlab设计模糊控制器
  10. 遥控器终结者——万能红外遥控器开发方案详解来了!
  11. 各种带有物理学特点的把妹法[转]
  12. winform之修改图标
  13. VFP控制Excel插入图表
  14. 华三防火墙Reth链路冗余技术
  15. “钢铁侠”把特斯拉送上太空,可你知道发射卫星有多难吗?
  16. 计算机系统文件夹打不开,电脑系统的文件夹打不开可以试下这两个办法
  17. Java 基础学习记录
  18. asp上传文件到ftp服务器,ASP.NET 中使用 FTP 上传文件
  19. libapache2-mod-auth-mysql_軟件包libapache2-mod-auth-mysql不可用
  20. JNI程序开发入门之高端大气上档次的Hello World

热门文章

  1. Altium Designer Summer 09绘制3D封装库
  2. 读《七人分粥》悟管理之道
  3. 大学计算机专业英语期末考试,河南大学计算机专业英语试题
  4. UE4使用贴花(Decal)
  5. Oozie-4.1.0-cdh5.5.2 安装部署使用
  6. 区块链的架构,特点和优势
  7. AVS2参考软件的运行
  8. eclipse快速创建无参和有参构造函数:
  9. IE和谷歌浏览器区分
  10. 使用Python构建参数化FNN(一)——构建可自定义结构的FNN