图数据库NebulaGraph的Java 数据解析实践与指导详解

目录
  • 愉快、干净的 Java 交互环境
  • 通过调用 execute 方法获得不太容易懂的 ResultSet 对象
  • 直接返回 JSON 的 executeJson 方法
  • 结论

愉快、干净的 Java 交互环境

如何快速、即时、符合直觉地去处理 Nebula Java Client 中的数据解析?读这一篇就够了。

图数据库 NebulaGraph 的论坛和微信群里,有不少用户问及了 Java 客户端数据解析的问题。在本文教你一种简单的方式同返回结果交互,快速、即时地拿到解析数据。

本文最为关键步骤之一,便是用几行代码,准备一个干净的交互式 NebulaGraph Java REPL 环境。

多亏了 Java-REPL,我们可以很方便地(像 iPython 那样)去实时交互地调试、分析 NebulaGraph Java 客户端。

下面,开始实操。

先用 Docker 镜像准备环境:

docker pull albertlatacz/java-repl
docker run --rm -it \
    --network=nebula-net \
    -v ~:/root \
    albertlatacz/java-repl \
    bash
apt update -y && apt install ca-certificates -y
wget https://dlcdn.apache.org/maven/maven-3/3.8.6/binaries/apache-maven-3.8.6-bin.tar.gz --no-check-certificate
tar xzvf apache-maven-3.8.6-bin.tar.gz
wget https://github.com/vesoft-inc/nebula-java/archive/refs/tags/v3.0.0.tar.gz
tar xzvf v3.0.0.tar.gz
cd nebula-java-3.0.0/
../apache-maven-3.8.6/bin/mvn dependency:copy-dependencies
../apache-maven-3.8.6/bin/mvn -B package -Dmaven.test.skip=true
java -jar ../javarepl/javarepl.jar

在执行完上面的 java -jar ../javarepl/javarepl.jar 之后,我们就进入了交互式的 Java Shell(REPL)。我们不用再做编译、执行、print 这样的慢反馈来调试和研究我们的代码了,是不是很方便?

root@a2e26ba62bb6:/javarepl/nebula-java-3.0.0# java -jar ../javarepl/javarepl.jar
Welcome to JavaREPL version 428 (Java HotSpot(TM) 64-Bit Server VM, Java 1.8.0_111)
Type expression to evaluate, :help for more options or press tab to auto-complete.
Connected to local instance at http://localhost:43707
java> System.out.println("Hello, World!");
Hello, World!
java>

首先我们在 java> 提示符下,这些来把必须的类路径和导入:

:cp /javarepl/nebula-java-3.0.0/client/target/client-3.0.0.jar
:cp /javarepl/nebula-java-3.0.0/client/target/dependency/fastjson-1.2.78.jar
:cp /javarepl/nebula-java-3.0.0/client/target/dependency/slf4j-api-1.7.25.jar
:cp /javarepl/nebula-java-3.0.0/client/target/dependency/slf4j-log4j12-1.7.25.jar
:cp /javarepl/nebula-java-3.0.0/client/target/dependency/commons-pool2-2.2.jar
:cp /javarepl/nebula-java-3.0.0/client/target/dependency/log4j-1.2.17.jar
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.vesoft.nebula.ErrorCode;
import com.vesoft.nebula.client.graph.NebulaPoolConfig;
import com.vesoft.nebula.client.graph.data.CASignedSSLParam;
import com.vesoft.nebula.client.graph.data.HostAddress;
import com.vesoft.nebula.client.graph.data.ResultSet;
import com.vesoft.nebula.client.graph.data.SelfSignedSSLParam;
import com.vesoft.nebula.client.graph.data.ValueWrapper;
import com.vesoft.nebula.client.graph.net.NebulaPool;
import com.vesoft.nebula.client.graph.net.Session;
import java.io.UnsupportedEncodingException;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.*;

我们可以从这 Java 环境连接到 NebulaGraph。在下面的例子中,我用了自己的 graphd 的 IP 和端口作为例子:

NebulaPoolConfig nebulaPoolConfig = new NebulaPoolConfig();
nebulaPoolConfig.setMaxConnSize(10);
List<HostAddress> addresses = Arrays.asList(new HostAddress("192.168.8.127", 9669));
NebulaPool pool = new NebulaPool();
pool.init(addresses, nebulaPoolConfig);
Session session = pool.getSession("root", "nebula", false);

通过调用 execute 方法获得不太容易懂的 ResultSet 对象

刚接触 NebulaGraph Java 客户端的大家一定对这个 ResultSet 对象有些愁。别担心,借助我们的环境,十分钟把它搞通。这里我们执行一个简单的返回 vertex 顶点的结果看看:

ResultSet resp = session.execute("USE basketballplayer;MATCH (n:player) WHERE n.name==\"Tim Duncan\" RETURN n");

我们可以参考 ResultSet 的代码:client/graph/data/ResultSet.java

其实可以先不看,跟着教程往下走。一般来说,查询结果都是二维表,ResultSet 针对行和列提供了常见的处理方法。通常,我们会获取每一行结果,再解析它,而关键的问题是每一个值要怎么处理。

java> resp.isSucceeded()
java.lang.Boolean res9 = true
java> resp.rowsSize()
java.lang.Integer res16 = 1
java> rows = resp.getRows()
java.util.ArrayList rows = [Row (
  values : [
    <Value vVal:Vertex (
        vid : <Value sVal:70 6c 61 79 65 72 31 30 30>,
        tags : [
          Tag (
              name : 70 6C 61 79 65 72,
              props : {
                [B@5264a468 : <Value iVal:42>
                [B@496b8e10 : <Value sVal:54 69 6d 20 44 75 6e 63 61 6e>
              }
            )
        ]
      )>
  ]
)]
java> row0 = resp.rowValues(0)
java.lang.Iterable<com.vesoft.nebula.client.graph.data.ValueWrapper> res10 = ColumnName: [n], Values: [("player100" :player {name: "Tim Duncan", age: 42})]

回到本次 query 语句,它其实是在返回一个 vertex 顶点:

(root@nebula) [basketballplayer]&gt; match (n:player) WHERE n.name == "Tim Duncan" return n
+----------------------------------------------------+
| n                                                  |
+----------------------------------------------------+
| ("player100" :player{age: 42, name: "Tim Duncan"}) |
+----------------------------------------------------+
Got 1 rows (time spent 2116/44373 us)

通过上面的几个方法,我们其实能够获得这个顶点的值:

v = Class.forName("com.vesoft.nebula.Value")
v.getDeclaredMethods()

然而,这个 com.vesoft.nebula.Value 的值的类提供的方法特别原始,这也是让大家犯愁数据解析的原因。所以,在这个教程中最重要的一个带走的经验(除了利用 REPL 之外)就是:非必要不要去取这个原始的类,我们应该去取得 ValueWrapper 封装之后的值!!!

注意:其实我们有更轻松地方法,就是用 executeJson 直接获得 JSON string。别担心,会在后面提到,不过这个方法要 2.6 之后才支持。

那么问题来了,如何使用 ValueWrapper 封装呢?其实答案已经在上面了,大家可以回去看看,resp.rowValues(0) 的类型正是 ValueWrapper 的可迭代对象!

所以,正确打开方式是迭它!迭它!迭它!其实这个就是代码库里的 GraphClientExample 的一部分例子了,我们把它迭代取出来,放到 wrappedValueList 里慢慢把玩:

import java.util.ArrayList;
import java.util.List;
List<ValueWrapper> wrappedValueList = new ArrayList<>();
for (int i = 0; i < resp.rowsSize(); i++) {
    ResultSet.Record record = resp.rowValues(i);
    for (ValueWrapper value : record.values()) {
        wrappedValueList.add(value);
        if (value.isLong()) {
            System.out.printf("%15s |", value.asLong());
        }
        if (value.isBoolean()) {
            System.out.printf("%15s |", value.asBoolean());
        }
        if (value.isDouble()) {
            System.out.printf("%15s |", value.asDouble());
        }
        if (value.isString()) {
            System.out.printf("%15s |", value.asString());
        }
        if (value.isTime()) {
            System.out.printf("%15s |", value.asTime());
        }
        if (value.isDate()) {
            System.out.printf("%15s |", value.asDate());
        }
        if (value.isDateTime()) {
            System.out.printf("%15s |", value.asDateTime());
        }
        if (value.isVertex()) {
            System.out.printf("%15s |", value.asNode());
        }
        if (value.isEdge()) {
            System.out.printf("%15s |", value.asRelationship());
        }
        if (value.isPath()) {
            System.out.printf("%15s |", value.asPath());
        }
        if (value.isList()) {
            System.out.printf("%15s |", value.asList());
        }
        if (value.isSet()) {
            System.out.printf("%15s |", value.asSet());
        }
        if (value.isMap()) {
            System.out.printf("%15s |", value.asMap());
        }
    }
    System.out.println();
}

上边这些很丑的 if 就是关键了,我们知道 query 的返回值可能是多种类型的,他们分为:

  • 图语义的:点、边、路径
  • 数据类型:String,日期,列表,集合…等等

这里的关键是,我们要使用 ValueWrapper 为我们准备好 asXxx 方法。如果这个值是一个顶点,那么这个 Xxx 就是 Node。同理如果是边的话,这个 Xxx 就是 Relationship。

所以,我给大家看看咱们这个返回点结果的情况下的 asNode() 方法:

java> v = wrappedValueList.get(0)
com.vesoft.nebula.client.graph.data.ValueWrapper v = ("player100" :player {name: "Tim Duncan", age: 42})
java> v.asNode()
com.vesoft.nebula.client.graph.data.Node res16 = ("player100" :player {name: "Tim Duncan", age: 42})
java> node = v.asNode()
com.vesoft.nebula.client.graph.data.Node node = ("player100" :player {name: "Tim Duncan", age: 42})

顺便说一下,借助于 Java 的反射 reflection,我们可以在这个交互程序里做类似于 Python 里 dir() 的事情:实时地去获取一个类支持的方法。像这样,省去了查代码的时间。

java> rClass=Class.forName("com.vesoft.nebula.client.graph.data.ResultSet")
java.lang.Class r = class com.vesoft.nebula.client.graph.data.ResultSet
java> rClass.getDeclaredMethods()
java.lang.reflect.Method[] res20 = [public java.util.List com.vesoft.nebula.client.graph.data.ResultSet.getColumnNames(), public int com.vesoft.nebula.client.graph.data.ResultSet.rowsSize(), public com.vesoft.nebula.client.graph.data.ResultSet$Record com.vesoft.nebula.client.graph.data.ResultSet.rowValues(int), public java.util.List com.vesoft.nebula.client.graph.data.ResultSet.colValues(java.lang.String), public java.lang.String com.vesoft.nebula.client.graph.data.ResultSet.getErrorMessage(), public boolean com.vesoft.nebula.client.graph.data.ResultSet.isSucceeded(), public int com.vesoft.nebula.client.graph.data.ResultSet.getErrorCode(), public java.lang.String com.vesoft.nebula.client.graph.data.ResultSet.getSpaceName(), public int com.vesoft.nebula.client.graph.data.ResultSet.getLatency(), public com.vesoft.nebula.graph.PlanDescription com.vesoft.nebula.client.graph.data.ResultSet.getPlanDesc(), public java.util.List com.vesoft.nebula.client.graph.data.ResultSet.getRows(), public java.lang.String com.vesoft.nebula.client.graph.data.ResultSet.getComment(), public java.lang.String com.vesoft.nebula.client.graph.data.ResultSet.toString(), public boolean com.vesoft.nebula.client.graph.data.ResultSet.isEmpty(), public java.util.List com.vesoft.nebula.client.graph.data.ResultSet.keys()]

这样:

java> nodeClass=Class.forName("com.vesoft.nebula.client.graph.data.Node")
java.lang.Class nodeClass = class com.vesoft.nebula.client.graph.data.Node
java> nodeClass.getDeclaredMethods()
java.lang.reflect.Method[] res20 = [public boolean com.vesoft.nebula.client.graph.data.Node.hasTagName(java.lang.String), public boolean com.vesoft.nebula.client.graph.data.Node.hasLabel(java.lang.String), public java.util.List com.vesoft.nebula.client.graph.data.Node.tagNames(), public java.util.HashMap com.vesoft.nebula.client.graph.data.Node.properties(java.lang.String) throws java.io.UnsupportedEncodingException, public java.util.List com.vesoft.nebula.client.graph.data.Node.labels(), public boolean com.vesoft.nebula.client.graph.data.Node.equals(java.lang.Object), public java.lang.String com.vesoft.nebula.client.graph.data.Node.toString(), public java.util.List com.vesoft.nebula.client.graph.data.Node.values(java.lang.String), public int com.vesoft.nebula.client.graph.data.Node.hashCode(), public com.vesoft.nebula.client.graph.data.ValueWrapper com.vesoft.nebula.client.graph.data.Node.getId(), public java.util.List com.vesoft.nebula.client.graph.data.Node.keys(java.lang.String) throws java.io.UnsupportedEncodingException]

看到这里,大家应该体会到封装了 ValueWrapper 的好处了吧?它提供了方便的符合直觉的方法,对于 Node 类型来说,它提供了 tagNames()properties()labels() 等等非常好用的方法:

java> node.properties("player")
java.util.HashMap res11 = {name="Tim Duncan", age=42}
java> node.tagNames()
java.util.ArrayList res12 = [player]
java> node.labels()
java.util.ArrayList res13 = [player]
java> node.values("player")
java.util.ArrayList res14 = [42, "Tim Duncan"]

我们这里只展示了顶点数据类型的处理、解析方式(RETURN n),像其他的数据类型比如边(edge)、路径(path)或者地理数据、时间数据,用这种方式(看有什么方法,再交互地去试试方法怎么用)也是一样的,对吧?

直接返回 JSON 的 executeJson 方法

最后,好消息是:从 v2.6 开始,NebulaGraph 可以直接返回 JSON 的 String 了,我们上面的纠结也都不是必要的了:

java> String resp_json = session.executeJson("USE basketballplayer;MATCH (n:player) WHERE n.name==\"Tim Duncan\" RETURN n");
java.lang.String resp_json = "
{
   "errors":[
      {
         "code":0
      }
   ],
   "results":[
      {
         "spaceName":"basketballplayer",
         "data":[
            {
               "meta":[
                  {
                     "type":"vertex",
                     "id":"player100"
                  }
               ],
               "row":[
                  {
                     "player.age":42,
                     "player.name":"Tim Duncan"
                  }
               ]
            }
         ],
         "columns":[
            "n"
         ],
         "errors":{
            "code":0
         },
         "latencyInUs":4761
      }
   ]
}
"

我相信大家肯定比我更擅长处理 JSON 的结果了哈~~

结论

  • 如果你有条件(v2.6 及其以上版本)用 JSON,情况会很容易,甚至你都不太需要本文的方法,不过本文可能会让你的交互环境更容易;
  • 如果你不得不和 ResultSet 打交道,记得用 ValueWrapper。因为我们可以用 asNode()asRelationship()asPath(),封装之后的值比原始的值可爱太多了!
    • 通过 REPL 工具,结合 Java 的 reflection 加上源代码本身,分析数据的处理将变得异常顺滑。

Happy Graphing!

谢谢你读完本文 (///▽///)

NebulaGraph Desktop,Windows 和 macOS 用户安装图数据库的绿色通道,10s 拉起搞定海量数据的图服务。通道传送门:c.nxw.so/9ShUq

GitHub 阅读 使用 http://c.nxw.so/8yTlk

以上就是图数据库NebulaGraph的Java 数据解析实践与指导详解的详细内容,更多关于 Java解析图数据库NebulaGraph的资料请关注我们其它相关文章!

(0)

相关推荐

  • java连接postgresql数据库代码及maven配置方式

    目录 java连接postgresql代码及maven配置 maven依赖 Springboo连接数据库通用代码 创建连接并执行业务逻辑 数据库通用类 maven配置 java连接postgresql代码及maven配置 postgresql数据库有默认数据库用户postgres,密码安装库时自己输入: 当然也可以连接其他用户: maven依赖 db2依赖 <dependency>     <groupId>org.apache.commons</groupId>  

  • Java MongoDB数据库连接方法梳理

    目录 前言 一.MongoDB简介 特点 缺点 MongoDB 应用场景 二.连接MongoDB前的准备工作 下载 创建工程并且导入jar包 java连接MongoDB数据库 增加数据 修改数据 删除一条数据 查询数据 前言 本节将介绍如何设置和使用MongoDB 驱动程序,通过 java实现与MongoDB服务端的通信功能,用户可以在此基础上进行各种Java程序的开发,例如最基本的增删改查操作. 一.MongoDB简介 通过启动mongo进程如Shell环境访问数据库外,MongoDB还提供了

  • Java通过数据库表生成实体类详细过程

    目录 项目背景 项目代码 使用说明 配置相关 swagger操作 目前的缺点 项目背景 最近在做的项目,涉及到数据库的操作了,之前做的是直接调用接口,不用做存库操作. 因此要增加大量特殊格式的实体类.比如我们用的是 JPA ,要增加一些注解,额外还有 lombok 的一些注解. 所以,这个项目的目的是,根据数据库中的某张表,生成与之对应的实体类.使用的技术是Freemarker 模板引擎,自定义了实体的模板. 最终的目标是,生成实体类的内容即可.有需要的朋友,可以改改,直接生成java文件.我这

  • java操作gaussDB数据库的实现示例

    本文主要介绍了java操作gaussDB数据库的实现示例,分享给大家,具体如下: package com.shiwusuo.ReadHdfsToClickHouse.gauss import java.sql.{Connection, DriverManager} import java.util.Properties import org.apache.spark.sql.DataFrame import scala.collection.mutable.ArrayBuffer; object

  • 使用Java实现先查询缓存再查询数据库

    目录 前言 栗子 思路分析 原理介绍 代码实现 项目代码 配置文件 项目结构 SpringBootQueryApplication.java Goods.java CacheSelector.java CacheService.java CacheServiceImpl.java GoodsService.java GoodsServiceImpl.java BaseUtil.java (核心类) 用法 第一次从数据中查询 第二次从缓存中查询 前言 在访问量大的时候,为了提高查询效率,我们会将数

  • Java实现同步枚举类数据到数据库

    本文实例为大家分享了Java同步枚举类数据到数据库的具体实现代码,供大家参考,具体内容如下 1.需求说明: 我们在开发中常常会用到数据字典,后端程序中也会经常用到(一般是用枚举类来存储),然而我们数据库中也会维护一个数据字典的数据,便于前端做数据显示时的处理,有一个问题就是,如果字典项发生变化后,我们需要修改枚举类和数据库的字典数据,要修改两次,还要面临二者不一致的风险. 所以这里的一个决绝方案就是自动读取枚举类的数据并更新到数据库,本文只讲枚举类数据的提取. 2.首先创建一个描述枚举类型的注解

  • JAVA DOM解析XML文件过程详解

    这篇文章主要介绍了JAVA DOM解析XML文件过程详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 代码如下 import java.io.IOException; import javax.xml.parsers.*; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NamedNodeMap; import org.w3c.dom.No

  • C#实现json格式数据解析功能的方法详解

    本文实例讲述了C#实现json格式数据解析功能的方法.分享给大家供大家参考,具体如下: 来写写json的解析吧 首先添加web引用 System.Web.Extensions 路径 c:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\v3.5\System.Web.Extensions.dll 接下来就是两个函数了,一个是根据key来获取,一个是根据index来获取 public static bool GetValue

  • Postgresql删除数据库表中重复数据的几种方法详解

    一直使用Postgresql数据库,有一张表是这样的: DROP TABLE IF EXISTS "public"."devicedata"; CREATE TABLE "public"."devicedata" ( "Id" varchar(200) COLLATE "pg_catalog"."default" NOT NULL, "DeviceId&qu

  • 使用Java构造和解析Json数据的两种方法(详解二)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面接着介绍用org.json构造和解析Json数据的方法

  • 使用Java构造和解析Json数据的两种方法(详解一)

    JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式,采用完全独立于语言的文本格式,是理想的数据交换格式.同时,JSON是 JavaScript 原生格式,这意味着在 JavaScript 中处理 JSON数据不须要任何特殊的 API 或工具包. 在www.json.org上公布了很多JAVA下的json构造和解析工具,其中org.json和json-lib比较简单,两者使用上差不多但还是有些区别.下面首先介绍用json-lib构造和解析Json数据的方法

  • Java使用JSONPath解析JSON完整内容详解

    JsonPath是一种简单的方法来提取给定JSON文档的部分内容. JsonPath有许多编程语言,如Javascript,Python和PHP,Java. JsonPath提供的json解析非常强大,它提供了类似正则表达式的语法,基本上可以满足所有你想要获得的json内容. maven中添加jar包 JsonPath可在Central Maven存储库中找到. Maven用户将其添加到您的POM. com.jayway.jsonpath json-path 2.2.0 操作符 操作 说明 $

  • Java操作Excel文件解析与读写方法详解

    目录 一.概述 二.Apache POI 三.XSSF解析Excel文件 1.Workbook(Excel文件) 2.Sheet(工作簿) 3.Row(数据行) 4.Cell(单元格) 四.超大Excel文件读写 1.使用POI写入 2.使用EasyExcel 一.概述 在应用程序的开发过程中,经常需要使用 Excel 文件来进行数据的导入或导出.所以,在通过Java语言实现此 类需求的时候,往往会面临着Excel文件的解析(导入)或生成(导出). 在Java技术生态圈中,可以进行Excel文件

  • Java编程Post数据请求和接收代码详解

    这两天在做http服务端请求操作,客户端post数据到服务端后,服务端通过request.getParameter()进行请求,无法读取到数据,搜索了一下发现是因为设置为text/plain模式才导致读取不到数据 urlConn.setRequestProperty("Content-Type","text/plain; charset=utf-8"); 若设置为以下方式,则通过request.getParameter()可以读取到数据 urlConn.setReq

  • Java中lombok的@Builder注解的解析与简单使用详解

    Lombok中@Builder用法 1.建造者模式简介:Builder 使用创建者模式又叫建造者模式.简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程. 2.注解类Builder.java注释: * The builder annotation creates a so-called 'builder' aspect to the class that is annotated or the class  * that contains a mem

  • Java图形化界面设计之布局管理器之BorderLayout案例详解

    边界布局管理器把容器的的布局分为五个位置:CENTER.EAST.WEST.NORTH.SOUTH.依次对应为:上北(NORTH).下南(SOUTH).左西(WEST).右东(EAST),中(CENTER),如下图所示. 特征: l  可以把组件放在这五个位置的任意一个,如果未指定位置,则缺省的位置是CENTER. l  南.北位置控件各占据一行,控件宽度将自动布满整行.东.西和中间位置占据一行;若东.西.南.北位置无控件,则中间控件将自动布满整个屏幕.若东.西.南.北位置中无论哪个位置没有控件

随机推荐