图解Janusgraph系列-查询图数据过程源码分析
图解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个执行策略类,包含:
- Connective Strategy:连接策略
- Match Predicate Strategy:匹配谓词策略
- Filter Ranking Strategy:过滤排名策略
- Inline Filter Strategy:内联过滤策略
- Incident To Adjacent Strategy:相邻策略事件
- Adjacent To Incident Strategy:邻近事件策略
- Early Limit Strategy:早期限制策略
- Count Strategy:计数策略
- Repeat Unroll Strategy:重复展开策略
- Path Retraction Strategy:路径收回策略
- Lazy Barrier Strategy:惰性屏障策略
- Adjacent Vertex Has Id Optimizer Strategy:相邻顶点有Id优化器策略
- Adjacent Vertex Is Optimizer Strategy:相邻顶点是优化器策略
- Adjacent Vertex Filter Optimizer Strategy:相邻顶点过滤器优化器策略
- JanusGraph Local Query Optimizer Strategy:JanusGraph局部查询优化器策略
- JanusGraph Step Strategy:JanusGraph步骤策略
- JanusGraphIo Registration Strategy:JanusGraphIo注册策略
- Profile Strategy:配置文件策略
- 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")
满足以下三个条件:
- has算子的前置算子为vertex算子(也就是上述out()产生的算子类型)
- 前置的vertex算子返回的类型时edge类型,也就是outE、inE、bothE这三种
- 前置的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系列-查询图数据过程源码分析相关推荐
- Android服务查询完整过程源码分析
Android服务注册完整过程源码分析中从上到下详细分析了Android系统的服务注册过程,本文同样针对AudioService服务来介绍Android服务的查询过程. 客户端进程数据发送过程 pri ...
- Android 数据Parcel序列化过程源码分析
在Android系统中,所有的服务都必须注册到ServiceManger中,当客户进程需要请求某一服务时,首先从服务管家ServiceManger中查找出该服务,然后通过RPC远程调用的方式使用该服务 ...
- SpringBoot2 | SpringBoot启动流程源码分析(一)
首页 博客 专栏·视频 下载 论坛 问答 代码 直播 能力认证 高校 会员中心 收藏 动态 消息 创作中心 SpringBoot2 | SpringBoot启动流程源码分析(一) 置顶 张书康 201 ...
- 【SRIO】5、Xilinx RapidIO核例子工程源码分析
目录 一.软件平台与硬件平台 二.打开例子工程 三.例子工程详解 3.1 工程概述 3.2 工程结构 3.3 工程分析 四.工程源码分析 3.1 顶层模块srio_example_top.v源码分析 ...
- 5.Xilinx RapidIO核例子工程源码分析
https://www.cnblogs.com/liujinggang/p/10091216.html 一.软件平台与硬件平台 软件平台: 操作系统:Windows 8.1 64-bit 开发套件:V ...
- Android服务注册完整过程源码分析
前面从不同片段分析了Android的Binder通信机制,本文结合前面介绍的内容,对整个Android的Binder通信过程进行一次完整的分析.分析以AudioService服务的注册过程为例. 由于 ...
- 【高速接口-RapidIO】5、Xilinx RapidIO核例子工程源码分析
期待大家的一键三连,爱你们!!! 总目录:总目录(经验分享) 献上链接: [高速接口-RapidIO]2.RapidIO串行物理层的包与控制符号 [高速接口-RapidIO]3.RapidIO串行物理 ...
- Activity启动流程源码分析(基于Android N)
Activity启动流程源码分析 一个Activity启动分为两种启动方式,一种是从Launcher界面上的图标点击启动,另一种是从一个Activity中设置按钮点击启动另外一个Activity.这里 ...
- Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析
Neural Turing Machines-NTM系列(三)ntm-lasagne源码分析 在NTM系列文章(二)中,我们已经成功运行了一个ntm工程的源代码.在这一章中,将对它的源码实现进行分析. ...
最新文章
- 解决select 下拉框选择器 input输入框 、时间日期选择器el-date-picker 赋值后,出现无法修改选中更改问题
- STM32F4+Wi-Fi+EDP 向 OneNet 上传数据
- 前端学习(1261):接口调用fetch方法
- mysql导入dat文件_MySql导入和抽取大数量级文件数据
- DrawerLayout和NavigationView的简单实用
- 人工神经网络理论、设计及应用_红层软岩大直径素混凝土置换桩复合地基设计理论及应用研究——以成都 ICON云端项目为例...
- 黑客攻击行为特征分析 反攻击技术综合性分析报告
- dubbo分布式事务解决方案_阿里架构师谈:高并发+分布式+秒杀+微服务+性能优化...
- 用matlab设计模糊控制器
- 遥控器终结者——万能红外遥控器开发方案详解来了!
- 各种带有物理学特点的把妹法[转]
- winform之修改图标
- VFP控制Excel插入图表
- 华三防火墙Reth链路冗余技术
- “钢铁侠”把特斯拉送上太空,可你知道发射卫星有多难吗?
- 计算机系统文件夹打不开,电脑系统的文件夹打不开可以试下这两个办法
- Java 基础学习记录
- asp上传文件到ftp服务器,ASP.NET 中使用 FTP 上传文件
- libapache2-mod-auth-mysql_軟件包libapache2-mod-auth-mysql不可用
- JNI程序开发入门之高端大气上档次的Hello World