Gremlin 和 nGQL 对比

Gremlin 介绍

Gremlin 是 Apache ThinkerPop 框架下的图遍历语言。Gremlin 可以是声明性的也可以是命令性的。虽然 Gremlin 是基于 Groovy 的,但具有许多语言变体,允许开发人员以 Java、JavaScript、Python、Scala、Clojure 和 Groovy 等许多现代编程语言原生编写 Gremlin 查询。

nGQL 介绍

Nebula Graph 的查询语言为 nGQL, 是一种类 SQL 的声明型的文本查询语言。相比 SQL,nGQL 具有如下特点:

  • 类 SQL,易学易用
  • 可扩展
  • 关键词大小写不敏感
  • 支持图遍历
  • 支持模式匹配
  • 支持聚合运算
  • 支持图计算
  • 支持分布式事务(开发中)
  • 无嵌入支持组合语句,易于阅读

基本概念对比

名称GremlinnGQL
vertex, nodevertexvertex
edge, relationshipedgeedge
vertex typelabeltag
edge typelabeledge type
vertex idvidvid
edge ideid

Gremlin 和 nGQL 均使用唯一标识符标记点和边。在 Nebula Graph 中,用户可以使用指定标识符、哈希或 uuid 函数自动生成标识符。

图基本操作

名称GremlinnGQL
新建图空间g = TinkerGraph.open().traversal()CREATE SPACE gods
查看点类型g.V().label()SHOW TAGS
插入指定类型点g.addV(String vertexLabel).property()INSERT VERTEX <tag_name> (prop_name_list) VALUES <vid>:(prop_value_list)
插入指定类型边g.addE(String edgeLabel).from(v1).to(v2).property()INSERT EDGE <edge_name> ( <prop_name_list> ) VALUES <src_vid> -> <dst_vid>: ( <prop_value_list> )
删除点g.V(<vid>).drop()DELETE VERTEX <vid>
删除边g.E(<vid>).outE(<type>).where(otherV().is(<vid>))drop()DELETE EDGE <edge_type> <src_vid> -> <dst_vid>
更新点属性g.V(<vid>).property()UPDATE VERTEX <vid> SET <update_columns>
查看指定点g.V(<vid>)FETCH PROP ON <tag_name> <vid>
查看指定边g.E(<src_vid> >> <dst_vid>)FETCH PROP ON <edge_name> <src_vid> -> <dst_vid>
沿指定点查询指定边g.V(<vid>).outE( <edge>)GO FROM <vid> OVER <edge>
沿指定点反向查询指定边g.V(<vid>).in( <edge>)GO FROM <vid> OVER <edge> REVERSELY
沿指定点查询指定边 N 跳g.V(<vid>).repeat(out(<edge>)).times(N)GO N STEPS FROM <vid> OVER <edge>
返回指定两点路径g.V(<vid>).repeat(out()).until(<vid>).path()FIND ALL PATH FROM <vid> TO <vid> OVER *

示例查询

本节中的示例使用了 Janus Graph 的示例图 The Graphs of Gods。该图结构如下图所示。此处使用属性图模型描述罗马万神话中诸神关系。

image

  • 插入数据
  1. # 插入点
  2. nebula> INSERT VERTEX character(name, age, type) VALUES hash("saturn"):("saturn", 10000, "titan"), hash("jupiter"):("jupiter", 5000, "god");
  3. gremlin> saturn = g.addV("character").property(T.id, 1).property('name', 'saturn').property('age', 10000).property('type', 'titan').next();
  4. ==>v[1]
  5. gremlin> jupiter = g.addV("character").property(T.id, 2).property('name', 'jupiter').property('age', 5000).property('type', 'god').next();
  6. ==>v[2]
  7. gremlin> prometheus = g.addV("character").property(T.id, 31).property('name', 'prometheus').property('age', 1000).property('type', 'god').next();
  8. ==>v[31]
  9. gremlin> jesus = g.addV("character").property(T.id, 32).property('name', 'jesus').property('age', 5000).property('type', 'god').next();
  10. ==>v[32]
  11. # 插入边
  12. nebula> INSERT EDGE father() VALUES hash("jupiter")->hash("saturn"):();
  13. gremlin> g.addE("father").from(jupiter).to(saturn).property(T.id, 13);
  14. ==>e[13][2-father->1]
  • 删除数据
  1. nebula> DELETE VERTEX hash("prometheus");
  2. gremlin> g.V(prometheus).drop();
  • 更新数据
  1. nebula> UPDATE VERTEX hash("jesus") SET character.type = 'titan';
  2. gremlin> g.V(jesus).property('age', 6000);
  • 查看数据
  1. nebula> FETCH PROP ON character hash("saturn");
  2. ===================================================
  3. | character.name | character.age | character.type |
  4. ===================================================
  5. | saturn | 10000 | titan |
  6. ---------------------------------------------------
  7. gremlin> g.V(saturn).valueMap();
  8. ==>[name:[saturn],type:[titan],age:[10000]]
  • 查询 hercules 的祖父
  1. nebula> LOOKUP ON character WHERE character.name == 'hercules' | \
  2. -> GO 2 STEPS FROM $-.VertexID OVER father YIELD $$.character.name;
  3. =====================
  4. | $$.character.name |
  5. =====================
  6. | saturn |
  7. ---------------------
  8. gremlin> g.V().hasLabel('character').has('name','hercules').out('father').out('father').values('name');
  9. ==>saturn
  • 查询 hercules 的父亲
  1. nebula> LOOKUP ON character WHERE character.name == 'hercules' | \
  2. -> GO FROM $-.VertexID OVER father YIELD $$.character.name;
  3. =====================
  4. | $$.character.name |
  5. =====================
  6. | jupiter |
  7. ---------------------
  8. gremlin> g.V().hasLabel('character').has('name','hercules').out('father').values('name');
  9. ==>jupiter
  • 查询年龄大于 100 的人物
  1. nebula> LOOKUP ON character WHERE character.age > 100 YIELD character.name, character.age;
  2. =========================================================
  3. | VertexID | character.name | character.age |
  4. =========================================================
  5. | 6761447489613431910 | pluto | 4000 |
  6. ---------------------------------------------------------
  7. | -5860788569139907963 | neptune | 4500 |
  8. ---------------------------------------------------------
  9. | 4863977009196259577 | jupiter | 5000 |
  10. ---------------------------------------------------------
  11. | -4316810810681305233 | saturn | 10000 |
  12. ---------------------------------------------------------
  13. gremlin> g.V().hasLabel('character').has('age',gt(100)).values('name');
  14. ==>saturn
  15. ==>jupiter
  16. ==>neptune
  17. ==>pluto
  • 查询和 pluto 一起居住的人物
  1. nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | \
  2. GO FROM $-.place OVER lives REVERSELY YIELD $$.character.name AS cohabitants;
  3. ===============
  4. | cohabitants |
  5. ===============
  6. | pluto |
  7. ---------------
  8. | cerberus |
  9. ---------------
  10. gremlin> g.V(pluto).out('lives').in('lives').values('name');
  11. ==>pluto
  12. ==>cerberus
  • 从一起居住的人物中排除 pluto 本人
  1. nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \
  2. $$.character.name != "pluto" YIELD $$.character.name AS cohabitants;
  3. ===============
  4. | cohabitants |
  5. ===============
  6. | cerberus |
  7. ---------------
  8. gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name');
  9. ==>cerberus
  • Pluto 的兄弟们
  1. # where do pluto's brothers live?
  2. nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS brother | \
  3. GO FROM $-.brother OVER lives YIELD $$.location.name;
  4. ====================
  5. | $$.location.name |
  6. ====================
  7. | sky |
  8. --------------------
  9. | sea |
  10. --------------------
  11. gremlin> g.V(pluto).out('brother').out('lives').values('name');
  12. ==>sky
  13. ==>sea
  14. # which brother lives in which place?
  15. nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS god | \
  16. GO FROM $-.god OVER lives YIELD $^.character.name AS Brother, $$.location.name AS Habitations;
  17. =========================
  18. | Brother | Habitations |
  19. =========================
  20. | jupiter | sky |
  21. -------------------------
  22. | neptune | sea |
  23. -------------------------
  24. gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name');
  25. ==>[god:jupiter, place:sky]
  26. ==>[god:neptune, place:sea]

高级查询

图探索

  1. # gremlin 版本
  2. gremlin> Gremlin.version();
  3. ==>3.3.5
  4. # 返回所有点
  5. gremlin> g.V();
  6. ==>v[1]
  7. ==>v[2]
  8. ...
  9. nebula> # Coming soon
  10. # 统计点数
  11. gremlin> g.V().count();
  12. ==>12
  13. nebula> # Coming soon
  14. # 按照点边类型统计点边个数
  15. gremlin> g.V().groupCount().by(label);
  16. ==>[character:9,location:3]
  17. gremlin> g.E().groupCount().by(label);
  18. ==>[mother:1,lives:5,father:2,brother:6,battled:3,pet:1]
  19. nebula> # Coming soon
  20. # 返回所有边
  21. gremlin> g.E();
  22. ==>e[13][2-father->1]
  23. ==>e[14][2-lives->3]
  24. ...
  25. nebula> # Coming soon
  26. # 查询所有点类型
  27. gremlin> g.V().label().dedup();
  28. ==>character
  29. ==>location
  30. nebula> SHOW TAGS;
  31. ==================
  32. | ID | Name |
  33. ==================
  34. | 15 | character |
  35. ------------------
  36. | 16 | location |
  37. ------------------
  38. # 查询所有边类型
  39. gremlin> g.E().label().dedup();
  40. ==>father
  41. ==>lives
  42. ...
  43. nebula> SHOW EDGES;
  44. ================
  45. | ID | Name |
  46. ================
  47. | 17 | father |
  48. ----------------
  49. | 18 | brother |
  50. ----------------
  51. ...
  52. # 查询所有点的属性
  53. gremlin> g.V().valueMap();
  54. ==>[name:[saturn],type:[titan],age:[10000]]
  55. ==>[name:[jupiter],type:[god],age:[5000]]
  56. ...
  57. nebula> # Coming soon
  58. # 查询 character 点属性
  59. gremlin> g.V().hasLabel('character').valueMap();
  60. ==>[name:[saturn],type:[titan],age:[10000]]
  61. ==>[name:[jupiter],type:[god],age:[5000]]
  62. ...

边的遍历

名称GremlinnGQL
指定点沿指定边的出点out(\GO FROM \ OVER \
指定点沿指定边的入点in(\GO FROM \ OVER \ REVERSELY
指定点沿指定边的双向点both(\GO FROM \ OVER \ BIDIRECT
  1. # 访问某个点沿某条边的 OUT 方向邻接点
  2. gremlin> g.V(jupiter).out('brother');
  3. ==>v[8]
  4. ==>v[5]
  5. nebula> GO FROM hash("jupiter") OVER brother;
  6. ========================
  7. | brother._dst |
  8. ========================
  9. | 6761447489613431910 |
  10. ------------------------
  11. | -5860788569139907963 |
  12. ------------------------
  13. # 访问某个点沿某条边的 IN 方向邻接点
  14. gremlin> g.V(jupiter).in('brother');
  15. ==>v[5]
  16. ==>v[8]
  17. nebula> GO FROM hash("jupiter") OVER brother REVERSELY;
  18. =======================
  19. | brother._dst |
  20. =======================
  21. | 4863977009196259577 |
  22. -----------------------
  23. | 4863977009196259577 |
  24. -----------------------
  25. # 访问某个点沿某条边的双向邻接点
  26. gremlin> g.V(jupiter).both('brother');
  27. ==>v[8]
  28. ==>v[5]
  29. ==>v[5]
  30. ==>v[8]
  31. nebula> GO FROM hash("jupiter") OVER brother BIDIRECT;
  32. =======================
  33. | brother._dst |
  34. =======================
  35. | 6761447489613431910 |
  36. ------------------------
  37. | -5860788569139907963|
  38. | 4863977009196259577 |
  39. -----------------------
  40. | 4863977009196259577 |
  41. -----------------------
  42. # 2度 out 查询
  43. gremlin> g.V(hercules).out('father').out('lives');
  44. ==>v[3]
  45. nebula> GO FROM hash("hercules") OVER father YIELD father._dst AS id | \
  46. GO FROM $-.id OVER lives;
  47. ========================
  48. | lives._dst |
  49. ========================
  50. | -1121386748834253737 |
  51. ------------------------

has 条件过滤

名称GremlinnGQL
通过 ID 来过滤点hasId(\)FETCH PROP ON \
通过 label 和属性的名字和值过滤点和边has(\LOOKUP \ | \ WHERE \
  1. # 查询 ID 为 saturn 的点
  2. gremlin> g.V().hasId(saturn);
  3. ==>v[1]
  4. nebula> FETCH PROP ON * hash("saturn");
  5. ==========================================================================
  6. | VertexID | character.name | character.age | character.type |
  7. ==========================================================================
  8. | -4316810810681305233 | saturn | 10000 | titan |
  9. --------------------------------------------------------------------------
  10. # 查询 tag 为 character 且 name 属性值为 hercules 的点
  11. gremlin> g.V().has('character','name','hercules').valueMap();
  12. ==>[name:[hercules],type:[demigod],age:[30]]
  13. nebula> LOOKUP ON character WHERE character.name == 'hercules' YIELD character.name, character.age, character.type;
  14. =========================================================================
  15. | VertexID | character.name | character.age | character.type |
  16. =========================================================================
  17. | 5976696804486077889 | hercules | 30 | demigod |
  18. -------------------------------------------------------------------------

返回结果限制

名称GremlinnGQL
指定返回结果行数limit()LIMIT
获取后 n 个元素tail()ORDER BY \ DESC LIMIT
跳过前 n 个元素skip()LIMIT \
  1. # 查询前两个点
  2. gremlin> g.V().has('character','name','hercules').out('battled').limit(2);
  3. ==>v[9]
  4. ==>v[10]
  5. nebula> GO FROM hash('hercules') OVER battled | LIMIT 2;
  6. =======================
  7. | battled._dst |
  8. =======================
  9. | 530133512982221454 |
  10. -----------------------
  11. | -695163537569412701 |
  12. -----------------------
  13. # 查询最后一个点
  14. gremlin> g.V().has('character','name','hercules').out('battled').values('name').tail(1);
  15. ==>cerberus
  16. nebula> GO FROM hash('hercules') OVER battled YIELD $$.character.name AS name | ORDER BY name | LIMIT 1;
  17. ============
  18. | name |
  19. ============
  20. | cerberus |
  21. ------------
  22. # 跳过第 1 个元素并返回一个元素
  23. gremlin> g.V().has('character','name','hercules').out('battled').values('name').skip(1).limit(1);
  24. ==>hydra
  25. nebula> GO FROM hash('hercules') OVER battled YIELD $$.character.name AS name | ORDER BY name | LIMIT 1,1;
  26. =========
  27. | name |
  28. =========
  29. | hydra |
  30. ---------

路径查询

名称GremlinnGQL
所有路径path()FIND ALL PATH
不包含环路simplePath()\
只包含环路cyclicPath()\
最短路径\FIND SHORTEST PATH

注意: Nebula Graph 需要起始点和终点方可返回路径, Gremlin 仅需要起始点。

  1. # pluto 点到与其有直接关联的出边点的路径
  2. gremlin> g.V().hasLabel('character').has('name','pluto').out().path();
  3. ==>[v[8],v[12]]
  4. ==>[v[8],v[2]]
  5. ==>[v[8],v[5]]
  6. ==>[v[8],v[11]]
  7. # 查询点 pluto 到点 jupiter 的最短路径
  8. nebula> LOOKUP ON character WHERE character.name== "pluto" YIELD character.name AS name | \
  9. FIND SHORTEST PATH FROM $-.VertexID TO hash("jupiter") OVER *;
  10. ============================================================
  11. | _path_ |
  12. ============================================================
  13. | 6761447489613431910 <brother,0> 4863977009196259577
  14. ------------------------------------------------------------

多度查询

名称GremlinnGQL
指定重复执行的语句repeat()N STEPS
指定重复执行的次数times()N STEPS
指定循环终止的条件until()\
指定收集数据的条件emit()\
  1. # 查询点 pluto 出边邻点
  2. gremlin> g.V().hasLabel('character').has('name','pluto').repeat(out()).times(1);
  3. ==>v[12]
  4. ==>v[2]
  5. ==>v[5]
  6. ==>v[11]
  7. nebula> LOOKUP ON character WHERE character.name== "pluto" YIELD character.name AS name | \
  8. GO FROM $-.VertexID OVER *;
  9. ================================================================================================================
  10. | father._dst | brother._dst | lives._dst | mother._dst | pet._dst | battled._dst |
  11. ================================================================================================================
  12. | 0 | -5860788569139907963 | 0 | 0 | 0 | 0 |
  13. ----------------------------------------------------------------------------------------------------------------
  14. | 0 | 4863977009196259577 | 0 | 0 | 0 | 0 |
  15. ----------------------------------------------------------------------------------------------------------------
  16. | 0 | 0 | -4331657707562925133 | 0 | 0 | 0 |
  17. ----------------------------------------------------------------------------------------------------------------
  18. | 0 | 0 | 0 | 0 | 4594048193862126013 | 0 |
  19. ----------------------------------------------------------------------------------------------------------------
  20. # 查询点 hercules 到点 cerberus 之间的路径
  21. # 循环的终止条件是遇到名称是 cerberus 的点
  22. gremlin> g.V().hasLabel('character').has('name','hercules').repeat(out()).until(has('name', 'cerberus')).path();
  23. ==>[v[6],v[11]]
  24. ==>[v[6],v[2],v[8],v[11]]
  25. ==>[v[6],v[2],v[5],v[8],v[11]]
  26. ...
  27. nebula> # Coming soon
  28. # 查询点 hercules 的所有出边可到达点的路径
  29. # 且终点必须是 character 类型的点
  30. gremlin> g.V().hasLabel('character').has('name','hercules').repeat(out()).emit(hasLabel('character')).path();
  31. ==>[v[6],v[7]]
  32. ==>[v[6],v[2]]
  33. ==>[v[6],v[9]]
  34. ==>[v[6],v[10]]
  35. ...
  36. nebula> # Coming soon
  37. # 查询两点 pluto 和 saturn 之间的最短路径
  38. # 且最大深度为 3
  39. gremlin> g.V('pluto').repeat(out().simplePath()).until(hasId('saturn').and().loops().is(lte(3))).hasId('saturn').path();
  40. nebula> FIND SHORTEST PATH FROM hash('pluto') TO hash('saturn') OVER * UPTO 3 STEPS;
  41. =================================================================================================
  42. | _path_ |
  43. =================================================================================================
  44. | 6761447489613431910 <brother,0> 4863977009196259577 <father,0> -4316810810681305233
  45. -------------------------------------------------------------------------------------------------

查询结果排序

名称GremlinnGQL
升序排列order().by()ORDER BY
降序排列order().by(decr)ORDER BY DESC
随机排列order().by(shuffle)\
  1. # 查询 pluto 的兄弟并按照年龄降序排列
  2. gremlin> g.V(pluto).out('brother').order().by('age', decr).valueMap();
  3. ==>[name:[jupiter],type:[god],age:[5000]]
  4. ==>[name:[neptune],type:[god],age:[4500]]
  5. nebula> GO FROM hash('pluto') OVER brother YIELD $$.character.name AS Name, $$.character.age as Age | ORDER BY Age DESC;
  6. ==================
  7. | Name | Age |
  8. ==================
  9. | jupiter | 5000 |
  10. ------------------
  11. | neptune | 4500 |
  12. ------------------

Group By

名称GremlinnGQL
对结果集进行分组group().by()GROUP BY
去除相同元素dedup()DISTINCT
对结果集进行分组并统计groupCount()GROUP BY COUNT

注意: GROUP BY 函数只能与 YIELD 语句一起使用。

  1. # 根据点类别进行分组并统计各个类别的数量
  2. gremlin> g.V().group().by(label).by(count());
  3. ==>[character:9,location:3]
  4. nebula> # Coming soon
  5. # 查询点 jupiter 出边邻点,使用 name 分组并统计
  6. gremlin> g.V(jupiter).out().group().by('name').by(count());
  7. ==>[sky:1,saturn:1,neptune:1,pluto:1]
  8. nebula> GO FROM hash('jupiter') OVER * YIELD $$.character.name AS Name, $$.character.age as Age, $$.location.name | \
  9. GROUP BY $-.Name YIELD $-.Name, COUNT(*);
  10. ======================
  11. | $-.Name | COUNT(*) |
  12. ======================
  13. | | 1 |
  14. ----------------------
  15. | pluto | 1 |
  16. ----------------------
  17. | saturn | 1 |
  18. ----------------------
  19. | neptune | 1 |
  20. ----------------------
  21. # 查找点 jupiter 出边到达的点并去重
  22. gremlin> g.V(jupiter).out().hasLabel('character').dedup();
  23. ==>v[1]
  24. ==>v[8]
  25. ==>v[5]
  26. nebula> GO FROM hash('jupiter') OVER * YIELD DISTINCT $$.character.name, $$.character.age, $$.location.name;
  27. ===========================================================
  28. | $$.character.name | $$.character.age | $$.location.name |
  29. ===========================================================
  30. | pluto | 4000 | |
  31. -----------------------------------------------------------
  32. | neptune | 4500 | |
  33. -----------------------------------------------------------
  34. | saturn | 10000 | |
  35. -----------------------------------------------------------
  36. | | 0 | sky |
  37. -----------------------------------------------------------

where 条件过滤

名称GremlinnGQL
where 条件过滤where()WHERE

过滤条件对比:

名称GremlinnGQL
等于eq(object)==
不等于neq(object)!=
小于lt(number)<
小于等于lte(number)<=
大于gt(number)>
大于等于gte(number)>=
判断值是否在指定的列表中within(objects…​)udf_is_in()
  1. gremlin> eq(2).test(3);
  2. ==>false
  3. nebula> YIELD 3 == 2;
  4. ==========
  5. | (3==2) |
  6. ==========
  7. | false |
  8. ----------
  9. gremlin> within('a','b','c').test('d');
  10. ==>false
  11. nebula> YIELD udf_is_in('d', 'a', 'b', 'c');
  12. ======================
  13. | udf_is_in(d,a,b,c) |
  14. ======================
  15. | false |
  16. ----------------------
  1. # 找出 pluto 和谁住并排队他本人
  2. gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name');
  3. ==>cerberus
  4. nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \
  5. $$.character.name != "pluto" YIELD $$.character.name AS cohabitants;
  6. ===============
  7. | cohabitants |
  8. ===============
  9. | cerberus |
  10. ---------------

逻辑运算

名称GremlinnGQL
Isis()==
Notnot()!=
Andand()AND
Oror()OR
  1. # 查询年龄大于 30 的人物
  2. gremlin> g.V().values('age').is(gte(30));
  3. ==>10000
  4. ==>5000
  5. ==>4500
  6. ==>30
  7. ==>45
  8. ==>4000
  9. nebula> LOOKUP ON character WHERE character.age >= 30 YIELD character.age;
  10. ========================================
  11. | VertexID | character.age |
  12. ========================================
  13. | -4316810810681305233 | 10000 |
  14. ---------------------------------------–
  15. | 4863977009196259577 | 5000   |
  16. ---------------------------------------–
  17. | -5860788569139907963 | 4500   |
  18. ---------------------------------------–
  19. | 5976696804486077889 | 30   |
  20. ---------------------------------------–
  21. | -6780323075177699500 | 45   |
  22. ---------------------------------------–
  23. | 6761447489613431910 | 4000 |
  24. ---------------------------------------–
  25. # 查询名称为 pluto 且年龄为 4000 的人物
  26. gremlin> g.V().has('name','pluto').and().has('age',4000);
  27. ==>v[8]
  28. nebula> LOOKUP ON character WHERE character.name == 'pluto' AND character.age == 4000;
  29. =======================
  30. | VertexID |
  31. =======================
  32. | 6761447489613431910 |
  33. -----------------------
  34. # 逻辑非的用法
  35. gremlin> g.V().has('name','pluto').out('brother').not(values('name').is('neptune')).values('name');
  36. ==>jupiter
  37. nebula> LOOKUP ON character WHERE character.name == 'pluto' YIELD character.name AS name | \
  38. GO FROM $-.VertexID OVER brother WHERE $$.character.name != 'neptune' YIELD $$.character.name;
  39. =====================
  40. | $$.character.name |
  41. =====================
  42. | jupiter |
  43. ---------------------

统计运算

名称GremlinnGQL
求和sum()SUM()
最大值max()MAX()
最小值min()MIN()
平均值mean()AVG()

Nebula Graph 统计运算必须同 GROUP BY 一起使用。

  1. # 计算所有 character 的年龄的总和
  2. gremlin> g.V().hasLabel('character').values('age').sum();
  3. ==>23595
  4. nebula> # Coming soon
  5. # 计算所有 character 的 brother 出边数的总和
  6. gremlin> g.V().hasLabel('character').map(outE('brother').count()).sum();
  7. ==>6
  8. nebula> # Coming soon
  9. # 返回所有 character 的年龄中的最大值
  10. gremlin> g.V().hasLabel('character').values('age').max();
  11. ==>10000
  12. nebula> # Coming soon

路径选取与过滤

  1. # 从路径中选取第 1 步和第 3 步的结果作为最终结果
  2. gremlin> g.V(pluto).as('a').out().as('b').out().as('c').select('a','c');
  3. ==>[a:v[8],c:v[3]]
  4. ==>[a:v[8],c:v[1]]
  5. ...
  6. nebula> # Coming soon
  7. # 通过 by() 指定选取的维度
  8. gremlin> g.V(pluto).as('a').out().as('b').out().as('c').select('a','c').by('name');
  9. ==>[a:pluto,c:sky]
  10. ==>[a:pluto,c:saturn]
  11. ...
  12. nebula> # Coming soon
  13. # 从 map 中选择指定 key 的值
  14. gremlin> g.V().valueMap().select('name').dedup();
  15. ==>[saturn]
  16. ==>[jupiter]
  17. ...
  18. nebula> # Coming soon

分支

  1. # 查找所有类型为 'character' 的点
  2. # name 属性为 'jupiter' 的点输出其 age 属性
  3. # 否则输出点的 name 属性
  4. gremlin> g.V().hasLabel('character').choose(values('name')).option('jupiter', values('age')).option(none, values('name'));
  5. ==>saturn
  6. ==>5000
  7. ==>neptune
  8. ...
  9. # Lambda
  10. gremlin> g.V().branch {it.get().value('name')}.option('jupiter', values('age')).option(none, values('name'));
  11. ==>saturn
  12. ==>5000
  13. ...
  14. # Traversal
  15. gremlin> g.V().branch(values('name')).option('jupiter', values('age')).option(none, values('name'));
  16. ==>saturn
  17. ==>5000
  18. # Branch
  19. gremlin> g.V().choose(has('name','jupiter'),values('age'),values('name'));
  20. ==>saturn
  21. ==>5000
  22. # 基于 if then 进行分组
  23. gremlin> g.V().hasLabel("character").groupCount().by(values("age").choose(
  24. is(lt(40)),constant("young"),
  25. choose(is(lt(4500)),
  26. constant("old"),
  27. constant("very old"))));
  28. ==>[young:4,old:2,very old:3]

Nebula Graph 尚无类似功能。

合并

coalesce() 可以接受任意数量的遍历器(traversal),按顺序执行,并返回第一个能产生输出的遍历器的结果。

optional() 只能接受一个遍历器(traversal),如果该遍历器能产生一个结果,则返回该结果,否则返回调用 optionalStep 的元素本身。

union() 可以接受任意数量的遍历器,并能够将各个遍历器的输出合并到一起。

  1. # 如果类型为 monster 则返回类型否则返回 'Not a monster'
  2. gremlin> g.V(pluto).coalesce(has('type','monster').values('type'),constant("Not a monster"));
  3. ==>Not a monster
  4. # 按优先级寻找到点 jupiter 的以下边和邻接点,找到一个就停止
  5. # 1、brother 出边和邻接点
  6. # 2、father 出边和邻接点
  7. # 3、father 入边和邻接点
  8. gremlin> g.V(jupiter).coalesce(outE('brother'), outE('father'), inE('father')).inV().path().by('name').by(label);
  9. ==>[jupiter,brother,pluto]
  10. ==>[jupiter,brother,neptune]
  11. # 查找点 pluto 的 father 出点,如果没有就返回 pluto 自己
  12. gremlin> g.V(pluto).optional(out('father')).valueMap();
  13. ==>[name:[pluto],type:[god],age:[4000]]
  14. # 寻找点 pluto 的出 father 点,邻接 brother 点,并将结果合并,最后打印出路径
  15. gremlin> g.V(pluto).union(out('father'),both('brother')).path();
  16. ==>[v[8],v[2]]
  17. ==>[v[8],v[5]]

Nebula Graph 尚无类似功能。

结果聚集与展开

  1. # 收集第 1 步的结果到集合 x 中
  2. # 注意:不影响后续结果
  3. gremlin> g.V(pluto).out().aggregate('x');
  4. ==>v[12]
  5. ==>v[2]
  6. ...
  7. # 通过 by() 指定聚集的维度
  8. gremlin> g.V(pluto).out().aggregate('x').by('name').cap('x');
  9. ==>[tartarus,jupiter,neptune,cerberus]
  10. # 查询与 pluto 的两度 OUT 邻居
  11. # 并收集这些到 x 集合里面
  12. # 最终以 name 属性展示其邻居
  13. gremlin> g.V(pluto).out().aggregate('x').out().aggregate('x').cap('x').unfold().values('name');
  14. ==>tartarus
  15. ==>tartarus
  16. ...

Nebula Graph 尚无类似功能。

模式匹配

match() 语句为图查询提供了一种基于模式匹配的方式,以便用更具描述性的方式进行图查询。match()语句通过多个模式片段 traversal fragments 来进行模式匹配。这些 traversal fragments 中会定义一些变量,只有满足所有用变量表示的约束的对象才能够通过。

  1. # 对每一个点,用以下模式去匹配,满足则生成一个 map<String, Object>,不满足则过滤掉
  2. # 模式1:a 为沿 father 出边指向 jupiter 的点
  3. # 模式2:b 对应当前点 jupiter
  4. # 模式3:c 对应创建 jupiter 的 brother 年龄为 4000 的 点
  5. gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c'));
  6. ==>[a:v[6],b:v[2],c:v[8]]
  7. # match() 语句可以与 select() 语句配合使用,从 Map<String, Object> 中选取部分结果
  8. gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c')).select('a', 'c').by('name');
  9. ==>[a:hercules,c:pluto]
  10. # match() 语句可以与 where() 语句配合使用,过滤结果
  11. gremlin> g.V().match(__.as('a').out('father').has('name', 'jupiter').as('b'), __.as('b').in('brother').has('age', 4000).as('c')).where('a', neq('c')).select('a', 'c').by('name');
  12. ==>[a:hercules,c:pluto]

随机过滤

sample() 接受一个整数值,从前一步的遍历器中采样(随机)出最多指定数目的结果。

coin() 字面意思是抛硬币过滤,接受一个浮点值,该浮点值表示硬币出现正面的概率。

  1. # 从所有点的出边中随机选择 2 条
  2. gremlin> g.V().outE().sample(2);
  3. ==>e[15][2-brother->5]
  4. ==>e[18][5-brother->2]
  5. # 从所点的 name 属性中随机选取 3 个
  6. gremlin> g.V().values('name').sample(3);
  7. ==>hercules
  8. ==>sea
  9. ==>jupiter
  10. # 从所有的 character 中根据 age 随机选择 3 个
  11. gremlin> g.V().hasLabel('character').sample(3).by('age');
  12. ==>v[1]
  13. ==>v[2]
  14. ==>v[6]
  15. # 与 local 联合使用做随机漫游
  16. # 从点 pluto 出发做 3 次随机漫游
  17. gremlin> g.V(pluto).repeat(local(bothE().sample(1).otherV())).times(3).path();
  18. ==>[v[8],e[26][8-brother->5],v[5],e[18][5-brother->2],v[2],e[13][2-father->1],v[1]]
  19. # 每个点按 0.5 的概率过滤
  20. gremlin> g.V().coin(0.5);
  21. ==>v[1]
  22. ==>v[2]
  23. ...
  24. # 输出所有 location 类点的 name 属性,否则输出 not a location
  25. gremlin> g.V().choose(hasLabel('location'), values('name'), constant('not a location'));
  26. ==>not a location
  27. ==>not a location
  28. ==>sky
  29. ...

结果存取口袋 Sack

包含本地数据结构的遍历器称为口袋。sack() 将数据放入口袋,或者从口袋取出数据。每个遍历器的每个口袋都是通过 withSack() 创建的。

  1. # 创建一个包含常数 1 的口袋,并且在最终取出口袋中的值
  2. gremlin> g.withSack(1).V().sack();
  3. ==>1
  4. ==>1
  5. ...

遍历栅栏 barrier

barrier() 在某个位置插入一个栅栏,以强制该位置之前的步骤必须都执行完成才可以继续往后执行。

  1. # 利用隐式 barrier 计算特征向量中心性
  2. # 包括 groupCount、cap,按照降序排序
  3. gremlin> g.V().repeat(both().groupCount('m')).times(5).cap('m').order(local).by(values, decr);

局部操作 local

通过 Gremlin 进行图遍历通常是当前 step 处理前一 step 传递过来的对象流。很多操作是针对传递过来的对象流中的全部对象进行操作,但也有很多时候需要针对对象流中的单个对象而非对象流中的全部对象进行操作。这种对单个对象的局部操作,可以使用 local() 语句实现。

  1. # 不使用 local()
  2. gremlin> g.V().hasLabel('character').as('character').properties('age').order().by(value,decr).limit(2).value().as('age').select('character', 'age').by('name').by();
  3. ==>[character:saturn,age:10000]
  4. ==>[character:jupiter,age:5000]
  5. # 使用 local()
  6. gremlin> g.V().hasLabel('character').as('character').local(properties('age').order().by(value).limit(2)).value().as('age').select('character', 'age').by('name').by()
  7. ==>[character:saturn,age:10000]
  8. ==>[character:jupiter,age:5000]
  9. ==>[character:neptune,age:4500]
  10. ==>[character:hercules,age:30]
  11. ...
  12. # 查询 monster 的属性 map
  13. gremlin> g.V()hasLabel('character').has('type', 'type').propertyMap();
  14. ==>[name:[vp[name->nemean]],type:[vp[type->monster]],age:[vp[age->20]]]
  15. ==>[name:[vp[name->hydra]],type:[vp[type->monster]],age:[vp[age->0]]]
  16. ==>[name:[vp[name->cerberus]],type:[vp[type->monster]],age:[vp[age->0]]]
  17. # 查询 monster 的属性个数
  18. gremlin> g.V()hasLabel('character').has('type', 'monster').propertyMap().count(local);
  19. ==>3
  20. ==>3
  21. ==>3
  22. # 数目最多的点类型的点数目
  23. gremlin> g.V().groupCount().by(label).select(values).max(local);
  24. ==>9
  25. # 所有点的属性列表中的第一个属性
  26. gremlin> g.V().valueMap().limit(local, 1);
  27. ==>[name:[saturn]]
  28. ==>[name:[jupiter]]
  29. ==>[name:[sky]]
  30. ...
  31. # 不加 local
  32. gremlin> g.V().valueMap().limit(1);
  33. ==>[name:[saturn],type:[titan],age:[10000]]
  34. # 所有点作为一个集合,从中采样 2 个
  35. gremlin> g.V().fold().sample(local,2);
  36. ==>[v[8],v[1]]

执行统计和分析

Gremlin 提供两种语句对执行的查询语句进行统计和分析:

  • explain(),详细描述原始的 Gremlin 语句在编译期是如何转变为最终要执行的 step 集合的
  • profile(),统计 Gremlin 语句执行过程中的每个 step 消耗的时间和通过的对象等统计信息
  1. # explain()
  2. gremlin> g.V().hasLabel('character').explain();
  3. ==>Traversal Explanation
  4. ==========================================================================================
  5. Original Traversal [GraphStep(vertex,[]), HasStep([~label.eq(character)])]
  6. ConnectiveStrategy [D] [GraphStep(vertex,[]), HasStep([~label.eq(character)])]
  7. MatchPredicateStrategy [O] [GraphStep(vertex,[]), HasStep([~label.eq(character)])]
  8. ...
  9. StandardVerificationStrategy [V] [TinkerGraphStep(vertex,[~label.eq(character)])]
  10. Final Traversal [TinkerGraphStep(vertex,[~label.eq(character)])]
  11. # profile()
  12. gremlin> g.V().out('father').profile()
  13. ==>Traversal Metrics
  14. Step Count Traversers Time (ms) % Dur
  15. =============================================================================================================
  16. TinkerGraphStep(vertex,[]) 12 12 0.644 45.66
  17. VertexStep(OUT,[father],vertex) 2 2 0.534 37.83
  18. NoOpBarrierStep(2500) 2 2 0.233 16.51
  19. >TOTAL - - 1.411 -