-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathGraphComputing.html
2713 lines (1874 loc) · 78.4 KB
/
GraphComputing.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>Graph Computing</title>
<meta name="generator" content="emacs-wiki.el" />
<meta http-equiv="Content-Type"
content="text/html; charset=utf-8" />
<link rel="made" href="mailto:cyberpsyche[AT]gmail.com" />
<link rel="home" href="WelcomePage.html" />
<link rel="index" href="WikiIndex.html" />
<link rel="stylesheet" type="text/css" href="main.css"><script type="text/javascript">
function showToolTip(machaine) {
if (machaine != "") {
document.getElementById('tooltip').innerHTML = machaine;
document.getElementById('tooltipbox').style.visibility = 'visible';
}
}
function hide() {
document.getElementById('tooltip').innerHTML = '';
document.getElementById('tooltipbox').style.visibility = 'hidden';
}
</script>
<div class="menu">
<div class="menuitem">
<a href="WelcomePage.html">Wiki</a>
</div>
<div class="menuitem">
<a href="../workwiki/WelcomePage.html">work</a>
</div>
</div><!-- menu ends here -->
<div id="tooltipbox">
<div id="tooltip"></div>
</div>
</head>
<body>
<h1 id="top">Graph Computing</h1>
<!-- Page published by Emacs Wiki begins here -->
<p>
# title Graph Computing 图计算
</p>
<h2>概念 Terms</h2>
<p>
知识图谱(KG,Knowledge Graph) - KG强调使用图模型中的节点和边来分别描述关联知识的概念和关系,使得知识可以使用图论、图计算、图学习等进行深度查询、关系计算分析和链接预测。
</p>
<p>
知识图谱的核心技术在于图引擎,图引擎包含了图存储、图计算、图算法和图可视化四个核心层级。图存储层实现了将数据以图结构存储于以图模型指导设计的图数据库载体中,同时提供图数据的增删改查操作的能力,根据存储方式不同,图数据库又可以划分为原生图和非原生图;图计算层将复杂而专业的图的迭代式计算过程进行抽象并封装成接口暴露给外部,使得图的计算变得简单易用;图应用建模层承载了场景建模所需的基础图算法和在其基础上构建的自定义模型;图可视化层封装了图高效渲染和友好交互的技术框架。
</p>
<p>
<a href="./images/graph_computing_model.jpeg"><img src="./images/graph_computing_model.jpeg" alt="" /></a>
</p>
<p>
图模型 - 图模型是基于图结构设计的概念模型,主要有资源描述框架(RDFs,Resource Description Frameworks)和属性图(LPGs,Labeled Property Graphs)两种模型。RDFs是W3C制定的用于描述实体/资源的标准数据模型,由节点和弧组成,适用于离线分析,主要应用于语义网、文本处理与分析以及学术研究领域;属性图是为了解决互联数据的高效查询、使用和存储问题而被定义的数据模型,由顶点、边(Edge)、标签、关系类型以及属性组成,适用于在线查询,相比于RDFs,属性图因为使用属性折叠了大部分边而获得了较高的查询性能,主要应用于工业实践。目前主流的开源图存储多基于属性图模型,包括Neo4j(企业版于2018年11月已闭源)、<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">TigerGragph</a>、OrientDB、ArangoDB、Azure Cosmos DB等。
</p>
<p>
图存储方式 - 图存储方式是影响图数据处理效率的关键因素之一,根据存储设计是否为图数据进行特定优化,图存储被划分为原生图和非原生图。原生图的优化设计实现了无邻接索引(index-free adjacency),极大地提高了遍历和更新性能,其优化主要包含了三点:1)节点和关系以固定长度进行存储;2)每个节点域包含了指向关系和属性列表第一个元素的ID信息,实现了节点记录的轻量级存储;3)每个关系域包含了指向关系起点和终点的节点ID。主流开源图存储使用原生图设计的相对较少,有Neo4j、<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">TigerGraph</a>等。非原生图的存储多依赖于已有的第三方存储系统和索引系统,如<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">JanusGraph</a>使用Hadoop进行图处理分析,支持扩展Apache Cassandra、Apache HBase、Google Cloud <a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">BigTable</a>等存储系统,和Elasticsearch、Apache Solr等索引系统。
</p>
<p>
图数据库 - 图数据库是基于图模型实现数据创建、读取、更新和删除(CRUD)的实时数据库管理系统。根据数据量级、存储形式、处理和遍历性能进行划分,图数据库分为内存图、磁盘图、分布式图三种类型,处理性能依次递减,所能承载的数据量依次递增。开源的图数据库中,内存图如NetworkX、 iGraph,磁盘图如Neo4j、 OrientDB,分布式图如<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">TigerGraph</a>、<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">JanusGraph</a>和百度<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">HugeGraph</a>等。
</p>
<p>
图的计算模型 - 基于图的算法通常为迭代式计算,每一轮迭代中信息通过边在节点间传递,参与计算的节点集不断变化,直到算法收敛或达到一定的迭代次数。针对此,分布式图计算模型基于超步迭代和栅栏同步的BSP模式进行设计,目前存在两种比较成熟的图计算模型:Pregel和GAS。
</p>
<p>
(1)Pregel模型由Google公司提出,借鉴了<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">MapReduce</a>的思想,采用了中心点编程(Vertex-centric Programming)的图计算模式,让用户将复杂的迭代过程抽象为由一个顶点更新函数所实现的基于顶点的计算和基于边的消息通信。
</p>
<p>
(2)GAS模型由<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">GraphLab</a>提出,将顶点操作抽象成Gather、Apply、Scatter这三个阶段,Gather阶段搜集所有计算节点图数据中某个顶点的相邻边和顶点的数据进行计算,Apply阶段将gather到的数据应用到计算节点上,Scatter阶段将新的节点状态广播给邻居节点。相比Pregel模型的消息通信范式的同步模式,GAS模型更偏向共享内存风格的异步模式。
</p>
<p>
知识图谱可视化 - 知识图谱可视化是对海量网络数据进行高效而直观地渲染,专业的图可视化框架有<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">KeyLines</a>、Gephi、Linkurious、Cytoscape、Centrifuge,Tom Sawyer等,上述框架不仅针对图数据渲染优化了性能,还提供了一定的网络分析能力。主流的前端可视化框架,如D3、Echarts等也提供了对图形数据可视化的支持,但对图的适配性不及专业的框架。
</p>
<h2>图应用</h2>
<p>
在应用初级阶段,知识图谱发力的核心内容:一是将多源异构数据有机融合为一张关联图谱,二是将反映整体的网络特征提炼到单个实体信息中,实现关系特征从无到有的跨越。
</p>
<p>
完整性 - 知识图谱面向关系密集型数据,相较于其他数据挖掘技术更要求数据的完整性。若关联关系数据不全或图数据量过小,图将无法连通而形成一个个孤立的子图孤岛,会导致信息无法传播,基于此的关联分析可能退化成毫无价值的应用。
</p>
<p>
精准性 - 知识图谱建模的精准性同时依赖于对模型的业务特征和技术方法的深刻理解和实践经验,一是知识图谱数据建模的模型,即如何定义知识本体,例如,在异构图和同构图的选择上,是选择构建全量数据的异构图,还是针对特殊场景对实体和属性进行简化、对关系进行折叠的同构图;在关系定义尤其是抽象关系定义上,如何在无限制的关系定义中概括出对应用有价值的关系,包括实体间的共同爱好、共同购买关系、在特殊时段的联系、处理后获得的某种特征上的相似性等;二是数据分析的模型,如何对业务逻辑进行概括并映射成图能理解处理的模式,如担保风险中的担保链、担保圈、平台担保,映射到图分析中环形路径查找、担保社区发现和高维度节点算法等。
</p>
<p>
应用点: 客户关系梳理、潜在客户挖掘、智能产品推荐。
</p>
<h2>图计算工具</h2>
<p>
igraph - 可用于R, Python, Mathematica and C/C++.
</p>
<p>
networkx - Python图分析库
</p>
<p>
graph-tools - 据测评性能 graph-tools > igraph > networks
</p>
<p>
<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">CausalDiscoveryToolbox</a> - a package for causal inference in graphs and in the pairwise settings for Python>=3.5.
</p>
<p>
agglomcluster - Agglomerative cluster tool based on networkX.
</p>
<p>
PyGSP - Graph Signal Processing in Python
</p>
<h2>图可视化工具</h2>
<p>
plotly
</p>
<p>
graphviz - The Graphviz layout programs take descriptions of graphs in a simple text language, and make diagrams in useful formats, such as images and SVG for web pages; PDF or Postscript for inclusion in other documents; or display in an interactive graph browser. Graphviz has many useful features for concrete diagrams, such as options for colors, fonts, tabular node layouts, line styles, hyperlinks, and custom shapes.
</p>
<p>
<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">GraphvizAnim</a> - a tool to create simple animated graph visualizations; it is just a proof of concept, aimed mainly at teaching purposes. It is based on Graphviz for the graph rendering part and on <a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">ImageMagick</a> for the animated gif generation.
</p>
<h2>图数据库</h2>
<p>
Neo4j - 分为企业版与社区版,图查询语言为 Cypher.
</p>
<p>
GraphX - 分布式图处理框架,基于Spark平台提供对图计算和图挖掘简洁易用的而丰富的接口,极大的方便了对分布式图处理的需求。
</p>
<p>
<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">TinkerPop</a> - a graph computing framework for both graph databases (OLTP) and graph analytic systems (OLAP). 提供了实时事务处理(OLAP)和离线数据分析(OLTP)的一套标准化抽象接口,以及“一次编写、到处运行”和协调多机器图遍历能力的图遍历语言Gremlin。目前实现了<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">TinkerPop</a>接口的主流图数据库有Titan、OrientDB、Neo4j、<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">JanusGraph</a>等。
</p>
<p>
<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">JanusGraph</a> - 开源的分布式图数据库
</p>
<p>
<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">HugeGraph</a> - 百度实现的开源分布式图数据库
</p>
<h2>Gremlin</h2>
<h3>边遍历概念</h3>
<h4>顶点为基准的Steps:</h4>
<p>
out(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的OUT方向邻接点(可以是零个<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>,代表所有类型边;也可以一个或多个<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>,代表任意给定<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>的边,下同)
</p>
<p>
in(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的IN方向邻接点
</p>
<p>
both(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的双向邻接点
</p>
<p>
outE(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的OUT方向邻接边
</p>
<p>
inE(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的IN方向邻接边
</p>
<p>
bothE(label): 根据指定的<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">EdgeLabel</a>来访问顶点的双向邻接边
</p>
<h4>边为基准的Steps:</h4>
<p>
outV(): 访问边的出顶点(注意:这里是以边为基准,上述Step均以顶点为基准),出顶点是指边的起始顶点
</p>
<p>
inV(): 访问边的入顶点,入顶点是指边的目标顶点,也就是箭头指向的顶点
</p>
<p>
bothV(): 访问边的双向顶点
</p>
<p>
otherV(): 访问边的伙伴顶点,即相对于基准顶点而言的另一端的顶点
</p>
<h4>Has Step说明</h4>
<p>
在众多Gremlin的语句中,有一大类是filter类型,顾名思义,就是对输入的对象进行条件判断,只有满足过滤条件的对象才可以通过filter进入下一步。
</p>
<p>
has语句是filter类型语句的代表,能够以顶点和边的属性作为过滤条件,决定哪些对象可以通过。has语句包括很多变种:
</p>
<p>
hasLabel(labels…): object的label与labels列表中任何一个匹配就可以通过
</p>
<p>
hasId(ids…): object的id满足ids列表中的任何一个就可以通过
</p>
<p>
has(key, value): 包含属性“key=value”的object通过,作用于顶点或者边
</p>
<p>
has(label, key, value): 包含属性“key=value”且label值匹配的object通过,作用于顶点或者边
</p>
<p>
has(key, predicate): 包含键为key且对应的value满足predicate的object通过,作用于顶点或者边
</p>
<p>
hasKey(keys…): object的属性键包含所有的keys列表成员才能通过,作用于顶点属性
</p>
<p>
hasValue(values…): object的属性值包含所有的values列表成员才能通过,作用于顶点属性
</p>
<p>
has(key): 包含键为key的属性的object通过,作用于顶点或者边
</p>
<p>
hasNot(key): 不包含键为key的属性的object通过,作用于顶点或者边
</p>
<h3>图查询返回结果数限制</h3>
<p>
Gremlin能统计查询结果集中元素的个数,且允许从结果集中做范围截取。假设某个查询操作(如:g.V())的结果集包含8个元素,我们可以从这8个元素中截取指定部分。主要包括:
</p>
<p>
count(): 统计查询结果集中元素的个数;
</p>
<p>
range(m, n): 指定下界和上界的截取,左闭右开。比如range(2, 5)能获取第2个到第4个元素(0作为首个元素,上界为-1时表示剩
余全部);
</p>
<p>
limit(n): 下界固定为0,指定上界的截取,等效于range(0, n),语义是“获取前n个元素”。比如limit(3)能获取前3个元素;
</p>
<p>
tail(n): 上界固定为-1,指定下界的截取,等效于range(count - n, -1),语义是“获取后n个元素”。比如tail(2)能获取最后的2个元素;
</p>
<p>
skip(n): 上界固定为-1,指定下界的截取,等效于range(n, -1),语义是“跳过前n个元素,获取剩余的元素”。比如skip(6)能跳过前6个元素,获取最后2个元素。
</p>
<h3>查询路径path</h3>
<p>
path()返回当前遍历过的所有路径。有时需要对路径进行过滤,只选择没有环路的路径或者选择包含环路的路径,Gremlin针对这种需求提供了两种过滤路径的step:simplePath()和cyclicPath()。
</p>
<p>
// “<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">HugeGraph</a>”顶点到与其有两层关系的顶点的所有路径(只包含顶点)
g.V().hasLabel('software').has('name','HugeGraph') .both().both().path()
</p>
<p>
path(),获取当前遍历过的所有路径.
</p>
<p>
simplePath(),过滤掉路径中含有环路的对象,只保留路径中不含有环路的对象
</p>
<p>
// “<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">HugeGraph</a>”顶点到与其有两层关系的顶点的不含环路的路径(只包含顶点)
g.V().hasLabel('software').has('name','HugeGraph') .both().both().simplePath().path()
</p>
<p>
cyclicPath(),过滤掉路径中不含有环路的对象,只保留路径中含有环路的对象
</p>
<p>
// “<a class="nonexistent" href="mailto:cyberpsyche[AT]gmail.com">HugeGraph</a>”顶点到与其有两层关系的顶点的包含环路的路径(只包含顶点)
g.V().hasLabel('software').has('name','HugeGraph') .both().both().cyclicPath().path()
</p>
<h3>循环操作</h3>
<p>
循环操作是指多次执行某一部分语句,用于语句需要重复运行的场景,比如“查找朋友的朋友的朋友”,可以直接使用循环操作来完成即“查找3层朋友”,下面对具体的循环相关的Step进行说明:
</p>
<p>
repeat(): 指定要重复执行的语句,如repeat(out('friend'))
</p>
<p>
times(): 指定要重复执行的次数,如执行3次repeat(out('friend')).times(3)
</p>
<p>
// 访问某个顶点的OUT邻接点(1次) // 注意'okram'是顶点的id
g.V('okram').repeat(out()).times(1)
</p>
<p>
g.V('okram').out().out().out() 与 g.V('okram').repeat(out()).times(3) 结果相同
</p>
<p>
until(): 指定循环终止的条件,如一直找到某个名字的朋友为止
</p>
<p>
注意:until()与 times()是互斥的,两个语句无法同时存在于同一个循环中。
</p>
<p>
注意:until()放在repeat()之前或之后的顺序是会影响逻辑的,放前面表示先判断再执行,放后面表示先执行后判断。请对比如下两个语句的执行结果:
</p>
<p>
g.V('okram').repeat(out()).until(hasLabel('person')).path()
</p>
<p>
g.V('okram').until(hasLabel('person')).repeat(out()).path()
</p>
<p>
repeat(out('friend')).until(has('name','xiaofang'))
</p>
<p>
emit(): 指定循环语句的执行过程中收集数据的条件,每一步的结果只要符合条件则被收集,不指定条件时收集所有结果
</p>
<p>
// 查询顶点'okram'的所有OUT可达点的路径 // 且必须满足是'person'类型的点
g.V('okram') .repeat(out()) .emit(hasLabel('person')) .path()
</p>
<p>
注意:emit()放在repeat()之前或之后的顺序是会影响结果的,放前面表示先收集再执行,放后面表示先执行后收集。请对比如下两个语句的执行结果:
</p>
<p>
g.V('okram').repeat(out()).emit(hasLabel('person')).path()
</p>
<p>
g.V('okram').emit(hasLabel('person')).repeat(out()).path()
</p>
<p>
注意:emit()与until()搭配使用时,是“或”的关系而不是“与”的关系,满足两者间任意一个即可。
</p>
<p>
loops(): 当前循环的次数,可用于控制最大循环次数等,如最多执行3次repeat(out('friend')).until(loops().is(3)).path()
</p>
<p>
// 查询顶点'okram'到顶点'Gremlin'之间的路径
// 且之间只相差2跳的距离
// 其中的and()是指两个条件都满足
g.V('okram')
.repeat(out())
.until(has('name', 'Gremlin')
.and().loops().is(2))
<blockquote>
<p>
.path()
</p>
</blockquote>
<p>
查找子树
</p>
<p>
// 查找从一个节点出发,到 // 叶子节点结束的所有路径 // 这些路径的集合为一颗子树(子图)
g.V('okram') .repeat(out()) .until(outE().count().is(0)) .path()
</p>
<p>
查找两点之间的最短路径
</p>
<p>
// 已知两个顶点'okram'和'javeme',
// 通过任意关系来找到这两点之间的路径
// 且限制了最大深度为3
// 若存在那么第一条结果即是最短路径
g.V('okram')
.repeat(bothE().otherV().simplePath())
.until(hasId('javeme').and().loops().is(lte(3)))
.hasId('javeme')
<blockquote>
<p>
.path()
注意:bothE().otherV()一般等价于both(),但是在这里有一些差别,后者仅仅返回路径中的顶点信息,前者会把路径中的边信息也返回。
</p>
</blockquote>
<h3>查询结果排序</h3>
<p>
Gremlin允许对查询的结果进行排序输出,可以指定按某个属性的升序、降序或是随机序的方式输出。排序方式可以通过单独的order()或者order().by(...)指定
</p>
<p>
order()会将结果以升序输出
</p>
<p>
注:order()单独使用时,必须保证遍历器(traverser)中的元素是可排序的,在 java 里就是必须实现java.lang.Comparable接口,否则会抛出异常。
</p>
<p>
联合使用order().by(...) Step,传入排序方式,一般用于遍历器中的元素是属性时:
</p>
<p>
order().by(incr): 将结果以升序输出,这也是默认的排序方式;
</p>
<p>
order().by(decr): 将结果以降序输出;
</p>
<p>
order().by(shuffle): 将结果以随机序输出,每次执行结果顺序都可能不一样。
</p>
<p>
使用 order().by(…) step 但是 by() 传递的仅是一个排序方式的参数时,也必须保证遍历器(traverser)中的元素是可排序的。
</p>
<p>
联合使用order().by(...) Step,传入属性和排序方式,用于遍历器中的元素是顶点或边时:
</p>
<p>
order().by(key): 将结果按照元素属性key的值升序排列,与order().by(key, incr)等效;
</p>
<p>
order().by(key, incr): 将结果按照元素属性key的值升序排列;
</p>
<p>
order().by(key, decr): 将结果按照元素属性key的值降序排列;
</p>
<p>
order().by(key, shuffle): 将结果按照元素属性key的值随机序排列,每次执行结果顺序都可能不一样。
</p>
<p>
注:by()step不是一个真正的step,而是一个“step modulator”,与此类似的还有as()和option()step。通过by()step可以为某些step添加traversal、function、comparator等,通常的使用方式是step().by()…by(),某些step只能添加一个by(),而有一些可以添加任意数量的by()step。
</p>
<p>
// 以升序输出所有顶点的"name"属性值
g.V().values('name').order().by(incr)
</p>
<p>
// 以降序输出所有顶点的"name"属性值
<blockquote>
<p>
g.V().values('name').order().by(decr)
</p>
</blockquote>
<p>
// 以随机序输出所有顶点的"name"属性值
<blockquote>
<p>
g.V().values('name').order().by(shuffle)
</p>
</blockquote>
<p>
// 将"person"类型的顶点按照"age"升序(默认)排列,并获取"age"属性 g.V().hasLabel('person').order().by('age').values('age')
</p>
<p>
// 将"person"类型的顶点按照"age"降序排列输出,并获取"age"属性 g.V().hasLabel('person').order().by('age', decr).values('age')
</p>
<h3>数据分组与去重</h3>
<p>
group(): 对结果集进行分组,可通过by(property)来指定根据什么维度进行分组,可称维度为分组键;如果不指定维度则以元素id作为分组键,相当于重复的元素被分为一组。每一组由分组键+组内元素列表构成。如果有需要也可对每一组的元素列表进行reduce操作,依然使用by()语句,如by(count())对组内元素计数。
</p>
<p>
groupCount(): 对结果集进行分组,并统计每一组的元素个数。每一组由分组键+组内元素数量构成。
</p>
<p>
dedup(): 去除结果集中相同的元素,可通过by(property)来指定根据什么维度进行去重。
</p>
<p>
by(): 语义上一般指“根据什么维度”,与上述语句配合使用,如group().by()、dedup().by()等。
</p>
<p>
// 根据年龄进行分组
g.V().hasLabel('person').group().by('age')
</p>
<p>
// 根据年龄进行分组 // 并统计各个年龄的人数
g.V().hasLabel('person') .group().by('age').by(count())
</p>
<p>
// 根据顶点类别进行分组 // 并统计各个类别的数量
g.V().group().by(label).by(count())
</p>
<p>
// 根据年龄进行分组计数
<blockquote>
<p>
g.V().hasLabel('person') .groupCount().by('age')
</p>
</blockquote>
<p>
// 查看所有人当中有哪几种年龄 // 人之间的年龄是可能有重复的, // 通过dedup去除掉重复的年龄
g.V().hasLabel('person') .values('age').dedup()
</p>
<p>
// 从各个年龄的人中选出一个代表
g.V().hasLabel('person').dedup().by('age')
</p>
<p>
// 根据地域分组,并得到各个组的平均年龄
g.V().hasLabel('person').group() .by('addr').by(values('age').mean())
</p>
<p>
// 拥有相同数量边的顶点作为一组 // 并获取每一组的顶点数量 // 结果相当于:拥有m条边的顶点有n个
g.V().groupCount().by(bothE().count())
</p>
<h3>条件和过滤</h3>
<p>
where()就是用来过滤遍历过程中当前阶段的对象。另一方面,predicate就是过滤时使用的判断条件,包括关系运算和区间判断等,只有满足判断条件的对象才能通过进入下一轮或者作为结果。
</p>
<p>
where()常与select()或者match()配合使用,也可以单独使用。
以下是predicate的说明:
</p>
<pre class="example">Predicate Description
eq(object) 传入的对象等于目标object?
neq(object) 传入的对象不等于目标object?
lt(number) 传入的数字小于目标number?
lte(number) 传入的数字小于或等于目标number?
gt(number) 传入的数字大于目标number?
gte(number) 传入的数字大于或等于目标number?
inside(low,high) 传入的数字大于low且小于high?
outside(low,high) 传入的数字小于low或者大于high?
between(low,high) 传入的数字大于等于low且小于high?
within(objects…) 传入的对象等于目标对象列表objects中的任意一个?
without(objects…) 传入的对象不等于目标对象列表objects中的任何一个?
同时,逻辑运算and()、or()或者not()作用于predicate会产生一个新的predicate
</pre>
<p>
predicate可以通过test()来获得boolean值
</p>
<p>
// (3 == 2)
eq(2).test(3)
</p>
<table class="ewiki-table" border="2" cellpadding="5">
<thead>
<tr>
<th>// ('d' == 'a'</th><th>'d' == 'b'</th><th>'d' == 'c')</th>
</tr>
</thead>
</table>
within('a','b','c').test('d')
</p>
<p>
// (3 > 1 && 3 < 4)
<blockquote>
<p>
inside(1,4).test(3)
</p>
</blockquote>
<p>
// not()作用于neq(),等价于eq()
<blockquote>
<p>
not(neq(2))
</p>
</blockquote>
<p>
// and()连接的predicate,是一个新的predicate
within(1,2,3).and(not(eq(2))).test(3)
</p>
<p>
// or()连接的predicate,是一个新的predicate
inside(1,4).or(eq(5)).test(3)
</p>
<p>
where()单独使用有三种使用方式:
</p>
<pre class="example">• where(P)
• where(String, P)
• where(Traversal)
<example>
// 查看“zhoney”的合作伙伴 // where(P)方式
g.V('zhoney').as('a') .out('created').in('created') .where(neq('a'))
// “spmallette”开发过不止一个软件的合作伙伴
// where(Traversal)方式
g.V('spmallette').out('created').in('created')
.where(out('created').count().is(gt(1)))
.values('name')
where()可以与by()语句配合使用,表示用by(property)指定的属性进行predicate判断
// 查询”被别人认识“ // 且认识自己的人的年龄大于自己的年龄的人
g.V().as('a') .out('knows').as('b') .where('a', gt('b')).by('age')
where()与as()+select()配合使用
as()可以为某一阶段的对象添加标签,select()则可以通过标签获取对象。因此as()+select()可以在某个step处得到历史信息。
// 查看“zhoney”的合作伙伴,并将“zhoney”及其合作伙伴的名字以map输出
// select().where()方式
g.V('zhoney').as('a')
.out('created').in('created').as('b')
.select('a','b').by('name')
.where('a',neq('b'))
where()与match()配合使用,match()可以保证满足某种模式的对象通过。
// 查看“zhoney”的合作伙伴,并将“zhoney”及其合作伙伴的名字以map输出
// match().where()方式
g.V('zhoney').match(__.as('a').out('created').as('b'),
__.as('b').in('created').as('c')).
where('a', neq('c'))
.select('a','c').by('name')
filter()有三种用法:
<example>
• lambda方式,filter{it.get()…}
• Traversal方式,filter(Traversal)
• 特定filter step方式
</pre>
<p>
// 查找图中的“person”顶点 // lambda方式
g.V().filter {it.get().label() == 'person'}
</p>
<p>
// 查找图中的“person”顶点 // Traversal方式
<blockquote>
<p>
g.V().filter(label().is('person'))
</p>
</blockquote>
<p>
// 查找图中的“person”顶点 // 特定filter step方式
g.V().hasLabel('person')
</p>
<h3>逻辑运算</h3>
<p>
Gremlin支持在遍历器上加上逻辑运算进行过滤,只有满足该逻辑条件的元素才会进入下一个遍历器中。
</p>
<p>
下面讲解实现上述功能的具体Step:
</p>
<p>
is():可以接受一个对象(能判断相等)或一个判断语句(如:P.gt()、P.lt()、P.inside()等),当接受的是对象时,原遍历器中的元素必须与对象相等才会保留;当接受的是判断语句时,原遍历器中的元素满足判断才会保留,其实接受一个对象相当于P.eq();
</p>
<p>
and():可以接受任意数量的遍历器(traversal),原遍历器中的元素,只有在每个新遍历器中都能生成至少一个输出的情况下才会保留,相当于过滤器组合的与条件;
</p>
<p>
or():可以接受任意数量的遍历器(traversal),原遍历器中的元素,只要在全部新遍历器中能生成至少一个输出的情况下就会保留,相当于过滤器组合的或条件;
</p>
<p>
not():仅能接受一个遍历器(traversal),原遍历器中的元素,在新遍历器中能生成输出时会被移除,不能生成输出时则会保留,相当于过滤器的非条件。
</p>
<p>
and()和or()还可以放在where()中以中缀符的形式出现。
</p>
<p>
// 筛选出顶点属性“age”等于28的属性值,与`is(P.eq(28))`等效
g.V().values('age').is(28)
</p>
<p>
// 筛选出顶点属性“age”大于等于28的属性值
g.V().values('age').is(gte(28))
</p>
<p>
// 筛选出顶点属性“age”属于区间(27,29)的属性值
g.V().values('age').is(inside(27, 29))
注:P.inside(a, b)是左开右开区间(a,b)
</p>
<p>
// 筛选出由两个或两个以上的人参与创建(“created”)的顶点 // 注意:这里筛选的是顶点 g.V().where(__.in('created').count().is(gt(2))).values('name')
</p>
<p>
// 筛选出有创建者(“created”)的年龄(“age”)在20~29之间的顶点 g.V().where(__.in('created').values('age').is(between(20, 29))).values('name')
</p>
<p>
and(),逻辑与
</p>
<p>
// 所有包含出边“supports”的顶点的名字“name”
g.V().and(outE('supports')).values('name')
</p>
<p>
// 所有包含出边“supports”和“implements”的顶点的名字“name”
<blockquote>
<p>
g.V().and(outE('supports'), outE('implements')).values('name')
</p>
</blockquote>
<p>
// 包含边“created”并且属性“age”为28的顶点的名字“name”
g.V().and(outE('created'), values('age').is(28)).values('name')
</p>
<p>
中缀符写法
</p>
<p>
// 包含边“created”并且属性“age”为28的顶点的名字“name”
g.V().where(outE('created')
.and()
.values('age').is(28))
<blockquote>
<p>
.values('name')
</p>
</blockquote>
<p>
or(),逻辑或
</p>
<p>
// 所有包含出边“supports”的顶点的名字“name”
g.V().or(outE('supports')).values('name')
注:只有一个条件时,and()与or()的效果一样的。
</p>
<p>
// 所有包含出边“supports”或“implements”的顶点的名字“name”
g.V().or(outE('supports'), outE('implements')).values('name')
</p>
<p>
// 包含边“created”或属性“age”为28的顶点的名字“name”
g.V().or(outE('created'), values('age').is(28)).values('name')
</p>
<p>
中缀符写法
// 包含边“created”或属性“age”为28的顶点的名字“name”
g.V().where(outE('created')
.or()
.values('age').is(28))
<blockquote>
<p>