人之初,性本鸽。

大家好,我叫储惠龙(实名上网),你可以叫我小龙人,00 后一枚。目前从事后端开发工作。

今天给大家带来一个简单的为 NebulaGraph 提供 GraphQL 查询支持的 DEMO,为什么是简单的,因为本来想完成更多工作再给大家介绍的,但是上个月太忙加上下个月更忙,但是我又很想白嫖一下 Nebula 官方的奖品,所以就赶紧端上来了。

体验 NebulaGraphQL

先上项目地址:https://github.com/Dragonchu/NebulaGraphQL

GraphQL 是什么

先简单介绍一下 GraphQL,https://graphql.cn/ 详细的信息官方介绍得都很清晰。说一下我的理解,GraphQL 并不是对标 Cypher 这种查询语言,而是对标 REST 的一种 API 设计风格

所以,严格意义上,不是说使用 GraphQL 查询图数据库,而是使用一种 GraphQL 风格的 API 查询图数据库,或者说是将 Cypher 封装了一样。这个本质工作和大家做应用开发时,基于 NebulaGraph 写一些通过的 REST 接口是一样的。

API 查询示例

本文的测试数据集使用的 NebulaGraph 官方的 basketballplayer 数据集https://docs.nebula-graph.io/2.0/basketballplayer-2.X.ngql

举个例子,如果我想“根据科比的名字得到科比的全部信息”,可能会使用下面这样的 nGQL 语句:

LOOKUP ON player WHERE player.name == "Kobe Bryant" YIELD id(vertex) as vertexId | FETCH PROP ON player $-.vertexId YIELD properties(vertex);

虽然说 nGQL 已经很方便阅读了,但是如果让一个完全 0 基础的萌新来看也是看不懂的,并且这个语句的返回值是不明确的,至少没有办法从查询看到结果。而返回值的解析一直也是很多人的痛苦

那么,来看看使用 GraphQL 查询同一场景会是什么情况。

查询语句会是:

{
players(name:"Kobe Bryant"){
name
age
}
}

返回结果是:

{
players=[{name=Kobe Bryant, age=40}]
}

看看这优雅的查询和返回结果,想必我不多说,大家也都看得懂。这真的是

其实上面说了那么多,就是官方对 GraphQL 的总结:描述你的数据、请求你所要的数据、得到可预测的结果

上述的查询在 NebulaGraphQL 中已经实现了,同时还支持通过 VertexID 查询数据(好吧,我也就实现了这两种)。

NebulaGraphQL 简单入门

NebulaGraphQL 是一个 Java 库,旨在应用层提供使用 GraphQL 语法查询 NebulaGraph 图数据库中数据的能力。

在项目中使用 NebulaGraphQL 非常简单,因为 NebulaGraphQL 本身只想做一个简单的工具库,未来如果想直接集成到 MVC 框架可能会再起一个 NebulaGraphQL-Spring 之类的项目(画大饼中……)。所以 NebulaGraphQL 的使用和 nebula-java 是几乎完全一致的。

使用示例:

//创建一个config
GraphqlSessionPoolConfig graphqlSessionPoolConfig = new GraphqlSessionPoolConfig(
Lists.newArrayList(graphdAddress),
Lists.newArrayList(metadAddress),
spaceName, username, password);
//创建一个连接池
GraphqlSessionPool pool = new GraphqlSessionPool(graphqlSessionPoolConfig);
//执行语句
ExecutionResult executionResult = pool.execute("{players(age:32){name\nage}}");
//获取结果
System.out.println(executionResult.getData().toString());

其实 GraphSessionPool 内部就是使用的 nebula-java 的 SessionPool,所有配置都和使用官方提供的连接池一致,唯一的区别是需要额外提供 metad 的连接地址。这是因为 NebulaGraphQL 是通过 metad 来自动构建 Schema 的,NebulaGraphQL 会在创建连接池时自动创建 Schema。

NebulaGraphQL 的实现

NebulaGraphQL 主要是基于 graphql-java实现的。而使用 graphql-java,大家可以根据自己的项目定义自己的 GraphQL 的 Schema。不过,NebulaGraphQL 想尽可能地提供一些通用功能,并且一定是根据 NebulaGraph 的 Schema 自动构建的。

在创建 GraphqlSessionPool 时,NebulaGraphQL 通过连接 NebulaGraphQL 的 metad 将 NebulaGraph 中的元数据信息构造成 GraphQL 的 Schema 信息。这一部分是关键难题。目前,我仅仅做了如下的变换:

  1. 对于 NebulaGraph 中所有的 Tag,都会构造一个对应的 GraphQL 的可查询对象。
  2. 每一个 Tag 都会有一个同名的根据 ID 获取信息的查询。举例来说,对于 player 这个 tag,会生成一个查询 player,这个查询的参数是 vertexID,会根据 vertexID 获取到信息。
  3. 每一个 Tag 都会有一个在名称后加 -s 的查询。举例来说,对于 player 这个 tag,会生成一个查询 players,这个查询的参数是任意的属性。如果 player 上有 age,name,country 这些属性,在查询参数中可以传入这三种属性的任意组合,NebulaGraphQL 查询时会将这些参数进行“与” AND 语义的构造,再获取相关顶点。对于用户没有指定的参数,默认为 null(这是一个已知的问题,如果目的就是查 null 会有问题)。

测试数据集上自动生成的 GraphQL 的 Schema 示例:

type Query {
player(
"Vertex ID"
ID: ID
): player
players(age: Int = null, name: String = null): [player]!
team(
"Vertex ID"
ID: ID
): team
teams(name: String = null): [team]!
}
type player {
age: Int
name: String
}
type team {
name: String
}

简单讲解一下,Query 是 GraphQL 的查询入口,其中的 player, players, team, teams 都是自动生成的查询,可以当作查询语句。

player 是根据 VertexID 查询并返回一个 player,player 后面没有 ! 标识符,说明可能查询结果为空。players 查询有两个参数,对应着 player 这个 tag 的两个属性 age 和 name,这两个参数的类型都从 NebulaGraph 中的数据类型映射到了 GraphQL 的数据类型,默认值都为 null,返回值是一个列表。列表后的 !,说明一定返回一个列表,但是其中的 player 后没有 ! 标识符,指的是可能返回一个空列表。

使用 players 查询,参数可以指定 age 或者 name,或者 age 和 name 一起指定。

下面的 player 和 team 两个 type 就表示了这两个对象有什么属性,可以在查询时指定返回的属性,NebulaGraphQL 在返回结果时就只会提供查询需要的属性。

每一个 GraphQL 的查询会有一个绑定的 DataFetcher 对象,该对象中实现的就是如何将 GraphQL 语法映射成 nGQL 语句,并执行插叙返回结果。而返回结果会映射到自定义对象上,这里使用了我另一个小工具NebulaResultBoot 将执行结果映射到自定的对象上后,我们就可以在未来实现应用层的缓存,当然这里也有一个潜在的问题:每一次查询都要求获取到每一个点边的所有属性,这部分未来需要考虑优化。

当然,NebulaGraphQL 的目标不只是简单将 nGQL 映射到 GraphQL 这么简单,因为 GraphQL 除了查询简单这个很明朗的特点,还可以更轻松的支持权限管理,以及通过 DataLoader 机制在应用层实现一层缓存。不过,我这边目前也没有研究的很透彻,如果有大佬愿意加入一起实现那就最好不过了。

为了方便大家贡献(主要是我懒),NebulaGraphQL 的开发测试已经完成了一部分的容器化了,将代码库克隆到本地后,本地只需要有 Docker,然后在仓库根目录下运行

docker-compose -f docker-compose.dev.yml up --build

就可以看到在自动跑测试了,会在本地启动 NebulaGraph 集群并自动插入测试的数据,后续会继续优化这方面的流程。

小结

NebulaGraphQL 提供了更简单的查询语句,这个查询语句的构造应该是让前端直接提供的,GraphQL 的优势之一就是可以让前端选择自己需要的数据从而避免“接口地狱”,可能会有人认为这相当于让前端直接访问数据库了。是的,我觉得这个理解也确实没问题,这也是有人反对 GraphQL 的理由,不过这里就不继续讨论了。

但是使用 GraphQL 有一个潜在优势,也就是可以更轻松的将图数据库和关系型数据库整合在一起。当然如果只是使用图数据库的话,那使用 NebulaGraphQL 至少也能方便做一些简单的数据查询与测试。

对于 NebulaGraphQL 在实际生产中的价值目前我也没有体验过,因为纯属兴趣写着玩儿,如果大家有想法,欢迎在评论区交流。

当 GraphQL 遇上图数据库,便有了更方便查询数据的方式的更多相关文章

  1. Android数据库专家秘籍(七)经验LitePal查询艺术

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/40153833 经过了多篇文章的学习,我们已经把LitePal中的绝大部分内容都掌握 ...

  2. iOS开发中的4种数据持久化方式【二、数据库 SQLite3、Core Data 的运用】

                   在上文,我们介绍了ios开发中的其中2种数据持久化方式:属性列表.归档解档.本节将继续介绍另外2种iOS持久化数据的方法:数据库 SQLite3.Core Data 的运 ...

  3. SQL点滴17—使用数据库引擎存储过程,系统视图查询,DBA,BI开发人员必备基础知识

    原文:SQL点滴17-使用数据库引擎存储过程,系统视图查询,DBA,BI开发人员必备基础知识 在开发过程中会遇到需要弄清楚这个数据库什么时候建的,这个数据库中有多少表,这个存储过程长的什么样子等等信息 ...

  4. 国产多维数据库 NeuralCube!中国人自己的大数据底层核心技术!

    商业转载请联系作者获得授权,非商业转载请注明出处. 提到‘数据库’,首先被想到的肯定是Oracle.DB2.SQL Server.MySql这些传统的关系型数据库.数据库的概念是非常宽泛的,除了上述的 ...

  5. 完爆Facebook/GraphQL,APIJSON全方位对比解析(三)-表关联查询

    相关阅读: 完爆Facebook/GraphQL,APIJSON全方位对比解析(一)-基础功能 完爆Facebook/GraphQL,APIJSON全方位对比解析(二)-权限控制 自APIJSON发布 ...

  6. mysql(数据库,sql语句,普通查询)

    第1章 数据库 1.1 数据库概述 l 什么是数据库 数据库就是存储数据的仓库,其本质是一个文件系统,数据按照特定的格式将数据存储起来,用户可以对数据库中的数据进行增加,修改,删除及查询操作. l 什 ...

  7. 关系型数据库与HBase的数据储存方式差别

    现在Bigtable型(列族)数据库应用越来越广,功能也非常强大. 可是非常多人还是把它当做关系型数据库在使用,用原来关系型数据库的思维建表.存储.查询. 本文以hbase举例讲述数据模式的变化. 传 ...

  8. MySQL数据库:7、SQL常用查询语句

    Python基础之MySQL数据库 目录 Python基础之MySQL数据库 一.SQL语句常用查询方法 前期数据准备 1.基本查询 2.编写SQL语句的小技巧 3.查询之where筛选 3.1.功能 ...

  9. 用struts2标签如何从数据库获取数据并在查询页面显示。最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变量。

    最近做一个小项目,需要用到struts2标签从数据库查询数据,并且用迭代器iterator标签在查询页面显示,可是一开始,怎么也获取不到数据,想了许久,最后发现,是自己少定义了一个变量,也就是var变 ...

  10. [原]php远程odbc连接sqlsvr数据库,自定义端口,命名实例的连接方式

    远程odbc连接sqlsvr数据库,自定义端口,命名实例的连接方式,默认如果不修改的话sqlsvr的端口号是1433,默认实例名就是机器名,,如果既用了命名实例,又改了默认端口,改怎么连接数据库呢? ...

随机推荐

  1. Rendezvous hashing算法介绍

    Rendezvous hashing Rendezvous hashing用于解决分布式系统中的分布式哈希问题,该问题包括三部分: Keys:数据或负载的唯一标识 Values:消耗资源的数据或负载 ...

  2. element-ui表格展开行每次只能展开一行

    element-ui表格展开行每次只能展开一行 <template> <el-table :data="tableData" :expand-row-keys=& ...

  3. 对象中是否有某一个属性是否存在有三种方法 in hasOwnProperty Object.hasOwn

    如何看某个对象中没有某一个属性 如果我们要检测对象是否拥有某一属性,可以用in操作符 var obj= { name: '类老师', age: 18, school: '家具' }; console. ...

  4. Go复合类型之数组类型

    Go复合类型之数组 @ 目录 Go复合类型之数组 一.数组(Array)介绍 1.1 基本介绍 1.2 数组的特点 二.数组的声明与初始化 2.1 数组声明 2.2 常见的数据类型声明方法 2.3 数 ...

  5. ILRuntime的TestCase

    基于ILRuntime 1.6.3版本,在ILRuntime中提供测试用例,建议在下载ILRuntime之后先跑一遍官方的测试用例,对比自己使用ILRuntime的性能和官方数据是否一致 测试工具 测 ...

  6. 英伟达系列显卡大解析B100、H200、L40S、A100、A800、H100、H800、V100如何选择,含架构技术和性能对比带你解决疑惑

    英伟达系列显卡大解析B100.H200.L40S.A100.A800.H100.H800.V100如何选择,含架构技术和性能对比带你解决疑惑 近期,AIGC领域呈现出一片繁荣景象,其背后离不开强大算力 ...

  7. (C语言)课后题之计算器

    #include <stdio.h> void main() { //定义两个算术变量,四个运算结果变量 int a,b,sum,sub,mul,mod; double div; prin ...

  8. 关于SUPPLEMENTAL_LOG_DATA_MIN的设置问题

    Oracle数据库开启附加日志,用于Logminer或基于Logminer的一些操作. 客户咨询关于开启附加日志,SUPPLEMENTAL_LOG_DATA_MIN显示为啥是implicit,如何改成 ...

  9. go Printf 语句的占位符 Format

    func main() { var a uint8 = 12 var b = "wokao" fmt.Printf("查看类型:%T\n", a) //查看类型 ...

  10. Linux shell 判断变量是否包含某个字符串的几种方法

    方法一:利用grep查找 strA="long string" strB="string" result=$(echo $strA | grep "$ ...