Gremlin 图遍历机和语言简介

原创 gremlin

Gremlin 是Apache TinkerPop 框架下的图遍历语言。Gremlin 是一种函数式数据流语言,可以使得用户使用简洁的方式表述复杂的属性图(property graph)的遍历或查询。每个 Gremlin 遍历由一系列步骤(可能存在嵌套)组成,每一步都在数据流(data stream)上执行一个原子操作。

Gremlin包括三个基本的操作:

  • map-step - 对数据流中的对象进行转换
  • filter-step - 对数据流中的对象就行过滤
  • sideEffect-step - 对数据流进行计算统计

场景应用

以下是Gremlin在一些场景中的具体应用:

查找 Gremlin 朋友的朋友

g.V().has("name","gremlin").
  out("knows").
  out("knows").
  values("name")

查找那些由两个朋友共同创建的项目

g.V().match(
  as("a").out("knows").as("b"),
  as("a").out("created").as("c"),
  as("b").out("created").as("c"),
  as("c").in("created").count().is(2)).
  select("c").by("name")

给出 Gremlin 的所有上司,直至 CEO

g.V().has("name","gremlin").
  repeat(in("manages")).
  until(has("title","ceo")).
  path().by("name")

获得 Gremlin 合作者的头衔分布

g.V().has("name","gremlin").as("a").
  out("created").in("created").
  where(neq("a")).
  groupCount().by("title")

获取 Gremlin 购买产品的相关产品列表并排序

g.V().has("name","gremlin").
  out("bought").aggregate("stash").
  in("bought").out("bought").
  where(not(within("stash"))).
  groupCount().order(local).by(values,decr)

获取排名前十的中心人物

g.V().hasLabel("person").
  pageRank().
  by("friendRank").
  by(outE("knows")).
  order().by("friendRank",decr).
  limit(10)

OLTP 和 OLAP 遍历

  • 一次编写,到处运行

Gremlin 遵循“一次编写,到处运行”的设计哲学。这意味着不仅所有的 TinkerPop 启用的图形系统都能执行 Gremlin 遍历,而且每个 Gremlin 遍历都可以被评估为实时数据库查询或批处理查询。(前者被称为在线交易流程(OLTP),后者被称为在线分析流程(OLAP))。

  • 协调多种图遍历

这种普遍性是由 Gremlin 遍历机实现的。这种分布式、基于图形的虚拟机了解如何协调多机器图遍历的执行。好处是,用户不需要学习数据库查询语言和域特定的 BigData 分析语言(例如 Spark DSL,MapReduce 等)。Gremlin 是构建基于图的应用程序所必要的,其余一切都交给 Gremlin 遍历机处理。

命令式和声明式遍历

Gremlin 遍历可以以命令式(程序式)方式,声明性(描述性)方式编写,也可以包含命令性和声明性的混合方式编写。

命令式编写方式

获得Gremlin合作者的上司名字分布:

g.V().has("name","gremlin").as("a").
  out("created").in("created").
  where(neq("a")).
  in("manages").
  groupCount().by("name")

一个命令式的 Gremlin 遍历告诉运行器如何执行遍历中的每一步;然后,遍历器分裂到所有的“Gremlin”的合作者(去除 Gremlin 自己);下一步,遍历器走到“Gremlin”合作者的上司(managers),最终根据上司的名字进行统计分发。

之所以是命令式的 Gremlin 遍历,就是它明确地、程序化地告诉遍历器“去这里,然后去那里”。

声明式编写方式

以下使用声明式编写方式实现了同样的结果:

g.V().match(
  as("a").has("name","gremlin"),
  as("a").out("created").as("b"),
  as("b").in("created").as("c"),
  as("c").in("manages").as("d"),
  where("a",neq("c"))).
  select("d").
  groupCount().by("name")

声明式的 Gremlin 遍历并不能告诉遍历器执行它们的步骤的顺序,而是允许每个遍历器从一个(可能嵌套的)模式的集合中选择一个模式来执行。

然而,声明遍历具有额外的好处,它不仅利用了编译时查询计划器(如命令式遍历),而且还是一个运行时查询计划器,根据每个模式的历史统计信息选择下一个执行哪个遍历模式 - 有利于那些倾向于减少/过滤大多数数据的模式。

用户可以选择上述提出的方式编写自己的遍历语句。不管怎样,用户的遍历语句都会根据具体的执行引擎和遍历策略 traversal strategies 被重写。Gremlin 为用户提供灵活性表达自己的查询的;图系统也针对具体启用 TinkerPop 的数据系统进行有效地评估图遍历提供了灵活性。

无缝嵌入主语言

统一主开发语言和图查询语言

经典数据库查询语言(如SQL)被认为与最终在生产环境中使用的编程语言截然不同。因此,经典数据库要求开发人员既要编写主编程语言,还要编写数据库相应的查询语言。Gremlin 统一了这个划分,因为遍历可以用支持功能组合和嵌套(主要编程语言都支持)的任何编程语言编写。因此,用户的 Gremlin 遍历可以使用应用程序语言(主语言,Host language)编写,并受益于主语言及其工具(例如类型检查,语法高亮,点完成等)所提供的优点。目前存在各种 Gremlin 语言变体,包括:Gremlin-Java,Gremlin-Groovy,Gremlin-Python,Gremlin-Scala 等。

示例程序

比较以下两种方式,高低立判:

public class GremlinTinkerPopExample {
  public void run(String name, String property) {
    Graph graph = GraphFactory.open("...");
    GraphTraversalSource g = graph.traversal();

    double avg = g.V().has("name",name).
               out("knows").out("created").
               values(property).mean().next();

    System.out.println("Average rating: " + avg);
  }
}

另外一种:

public class SqlJdbcExample {
  public void run(String name, String property) {
    Connection connection = DriverManager.getConnection("...")
    Statement statement = connection.createStatement();
    ResultSet result = statement.executeQuery(
      "SELECT AVG(pr." + property + ") as AVERAGE FROM PERSONS p1" +
        "INNER JOIN KNOWS k ON k.person1 = p1.id " +
        "INNER JOIN PERSONS p2 ON p2.id = k.person2 " +
        "INNER JOIN CREATED c ON c.person = p2.id " +
        "INNER JOIN PROJECTS pr ON pr.id = c.project " +
        "WHERE p.name = '" + name + "'");
    System.out.println("Average rating: " + result.next().getDouble("AVERAGE")
  }
}

翻译参考自官方文档:Apache TinkerPop™ The Gremlin Graph Traversal Machine and Language

如果觉得这对你有用,请随意赞赏,给与作者支持
评论 0
最新评论