NebulaGraph 内核所自带的数据结构其实已经很丰富了,比如 List、Set、Map、Duration、DataSet 等等,但是我们平时在建表和数据写入的时候,可以用到的数据结构其实比较有限,复杂结构目前仅支持以下几种:

enum PropertyType {
UNKNOWN = 0,
... // 基础类型 TIMESTAMP = 21,
DURATION = 23,
DATE = 24,
DATETIME = 25,
TIME = 26, GEOGRAPHY = 31,
} (cpp.enum_strict)

所以,有时候因为业务需求,我们需要能存入一些定制化的数据类型,比如机器学习中经常用到的 Embedding 的数据类型,工程上经常会有直接存储二进制数据 Binary 的需求,这就需要开发者自己去添加一个数据类型来满足自己的业务开发需求。

本文将手把手教你如何在 NebulaGraph 中增加一种数据类型,直到可以建表使用并插入对应数据以及查询。

下面我们以一个简单的二进制类型 Binary 的添加步骤来讲解整个流程。

1.命令设计

在实现新增 Binary 类型之前我们先想好要用怎么样的命令去使用这个类型,我们可以参考 NebulaGraph 已有的数据类型的使用

1.1 schema 创建命令

// 创建点表
create tag player(name string, image binary)
// 创建边表
create edge team(name string, logo binary)

上面我们设计新建 schema 时使用 binary 关键字来表示设置二进制类型的属性字段。

1.2 插入数据

这里有一个问题就是,命令只能以字符串形式传输,所以我们如果通过命令来插入二进制数据的话,就需要转码。这里我们以选用 Base64 编码为例。

insert vertex player values "p1":("jimmy", binary("0s=W9d...."))

我们在插入命令里面同样以一个 binary 关键字来表示插入的是二进制数据的字符串而不是普通的字符串

1.3 查询数据

其实正常的设计,或者现有的 NebulaGraph 代码上面来看,查询语句并不需要做改变,直接按照像读其他数据一样读取 Binary 字段就可以了,只是这里我们需要考虑一个问题,客户端没适配的话怎么办?像 nebula-console、nebula-java、nebula-cpp 这些客户端,我们暂时没法一一去适配新增的类型,所以为了测试的时候使用 nebula-console 能够正常读取到数据,我们需要提供转换函数,将新增的 Binary 类型转换为现有客户端能读取的数据格式。

fetch prop on player "p1" yield base64(player.image) as img_base64

这里我们定义了一个 base64() 的转换函数,将存储的二进制数据再以 Base64的格式输出。(兜兜转转回到原点了(:≡)

定义好命令之后,我们来看看怎么实现这些内容,首先我们需要实现这个 Binary 的数据结构。

2. 定义数据结构

在服务端 C++ 代码中,我们可以以一个 Bytes 数组来表示二进制的数据结构

struct Binary {
std::vector<std::byte> values; Binary() = default;
Binary(const Binary &) = default;
Binary(Binary &&) noexcept = default;
explicit Binary(std::vector<std::byte> &&vals);
explicit Binary(const std::vector<std::byte> &l); // 用于直接从命令行的字符串中解析出二进制
explicit Binary(const std::string &str); ... // 其他接口
};

一个简单的数据结构定义好之后,我们需要将这个结构添加到 Value 的 union 中

Value 这个数据结构在 Value.cpp 中定义,它是 nebula 中所有数据结构的一个基类表示,每个新增的数据结构想要和之前其他数据结构一起混用的话,需要在 Value.cpp 里面对各个接口做适配。

这个 Value 的数据结构里面有很多的接口定义,像赋值构造、符号重载、toString、toJson、hash 等接口,都需要去适配。

好在这不是什么难事,参考其他类型的实现就行。唯一要注意的是要细心

2.1 定义 thrift 的数据结构

因为我们的数据结构还需要进行网络传输,所以我们还需要定义 thrift 文件里面的结构类型并实现序列化能力。

// 新增的数据类型
struct Binary {
1: list<byte> values;
} (cpp.type = "nebula::Binary") // 在Value union中增加Binary类型
union Value {
1: NullType nVal;
2: bool bVal;
3: i64 iVal;
4: double fVal;
5: binary sVal;
6: Date dVal;
7: Time tVal;
8: DateTime dtVal;
9: Vertex (cpp.type = "nebula::Vertex") vVal (cpp.ref_type = "unique");
10: Edge (cpp.type = "nebula::Edge") eVal (cpp.ref_type = "unique");
11: Path (cpp.type = "nebula::Path") pVal (cpp.ref_type = "unique");
12: NList (cpp.type = "nebula::List") lVal (cpp.ref_type = "unique");
13: NMap (cpp.type = "nebula::Map") mVal (cpp.ref_type = "unique");
14: NSet (cpp.type = "nebula::Set") uVal (cpp.ref_type = "unique");
15: DataSet (cpp.type = "nebula::DataSet") gVal (cpp.ref_type = "unique");
16: Geography (cpp.type = "nebula::Geography") ggVal (cpp.ref_type = "unique");
17: Duration (cpp.type = "nebula::Duration") duVal (cpp.ref_type = "unique");
18: Binary (cpp.type = "nebula::Binary") btVal (cpp.ref_type = "unique");
} (cpp.type = "nebula::Value")

另外我们还需要在 common.thrift 文件中的 PropertyType 该枚举中增加一个 BINARY 类型。

enum PropertyType {
UNKNOWN = 0,
... // 基础类型 TIMESTAMP = 21,
DURATION = 23,
DATE = 24,
DATETIME = 25,
TIME = 26, GEOGRAPHY = 31,
BINARY = 32,
} (cpp.enum_strict)

2.2 实现 Binary 的 thrift rpc 格式的序列化

这里的代码就不展示了,同样可以参考其他类型的实现。最相近的可以参考 src/common/datatypes/ListOps-inl.h 的实现

3. 命令行实现

数据结构定义好之后,我们可以开始命令行的实现,首先打开 src/parser/scanner.lex,我们需要新增一个关键字 Binary

"BINARY"                    { return TokenType::KW_BINARY; }

接着打开 src/parser/parser.yy 文件,将关键字声明一下:

$token KW_BINARY

为了尽量减少命令行的影响,我们将 Binary 关键字添加到非保留关键字的集合中:

unreserved_keyword
...
| KW_BINARY { $$ = new std::string("binary"); }

接下来我们要将Binary关键字添加到建表命令的词法树中:

type_spec
...
| KW_BINARY {
$$ = new meta::cpp2::ColumnTypeDef();
$$->type_ref() = nebula::cpp2::PropertyType::BINARY;
}

最后我们实现插入命令:

constant_expression
...
| KW_BINARY L_PAREN STRING R_PAREN {
$$ = ConstantExpression::make(qctx->objPool(), Value(Binary(*$3)));
delete $3;
}

就这样,我们就简单实现了上面命令设计里面的创建 binary schema 和插入 binary 数据的命令。

4. storaged 服务的读写适配

上面我们搞定了数据结构定义和 rpc 序列化以及命令行适配,一个新增的数据结构通过命令创建后,由 grapd 服务接收到请求并传输给 storaged 服务端。然而 storaged 服务端存储实际的数据是经过编码之后的 string,我们需要为这个新增的数据结构写一个编解码的代码逻辑。

4.1 RowWriterV2 写适配

在代码文件 src/codec/RowWriterV2.cpp 中,有以下几个函数需要适配的。

RowWriterV2::RowWriterV2(RowReader& reader) // 构造函数中适配新增的类似

WriteResult RowWriterV2::write(ssize_t index, const Binary& v) // 新增一个Binary的编码写入函数

这里我直接将 Bytes 数组写入 String 中

WriteResult RowWriterV2::write(ssize_t index, const Binary& v) noexcept {
return write(index, folly::StringPiece(reinterpret_cast<const char*>(v.values.data()), v.values.size()));
}

4.2 RowReaderV2 读适配

在代码文件 src/codec/RowReaderV2.cpp 中,同样有以下函数需要适配

Value RowReaderV2::getValueByIndex(const int64_t index) const {
...
case PropertyType::VID: {
// This is to be compatible with V1, so we treat it as
// 8-byte long string
return std::string(&data_[offset], sizeof(int64_t));
}
case PropertyType::FLOAT: {
float val;
memcpy(reinterpret_cast<void*>(&val), &data_[offset], sizeof(float));
return val;
}
case PropertyType::DOUBLE: {
double val;
memcpy(reinterpret_cast<void*>(&val), &data_[offset], sizeof(double));
return val;
}
... // code here
case PropertyType::BINARY: {
...
}
}

需要注意的是:读和写必须映射上,怎么写的就怎么读。

至此,在 NebulaGraph 里新增一个数据类型的流程就结束了。

看看效果


感谢你的阅读 (///▽///)

如何给图数据库 NebulaGraph 新增一种数据类型,以 Binary 为例的更多相关文章

  1. 从一个 issue 出发,带你玩图数据库 NebulaGraph 内核开发

    如何 build NebulaGraph?如何为 NebulaGraph 内核做贡献?即便是新手也能快速上手,从本文作为切入点就够了. NebulaGraph 的架构简介 为了方便对 NebulaGr ...

  2. ChatGPT 加图数据库 NebulaGraph 预测 2022 世界杯冠军球队

    一次利用 ChatGPT 给出数据抓取代码,借助 NebulaGraph 图数据库与图算法预测体坛赛事的尝试. 作者:古思为 蹭 ChatGPT 热度 最近因为世界杯正在进行,我受到这篇 Cambri ...

  3. 2 万字 + 20张图| 细说 Redis 九种数据类型和应用场景

    作者:小林coding 计算机八股文网(操作系统.计算机网络.计算机组成.MySQL.Redis):https://xiaolincoding.com 大家好,我是小林. 我们都知道 Redis 提供 ...

  4. JanusGraph 图数据库安装小记 ——以 JanusGraph 0.3.0 为例

    由于近期项目中有使用图数据的需求,经过对比,我们选择尝试使用 JanusGraph.本篇小记记录了我们安装 JanusGraph 以及需要一起集成的 Cassandra + Elasticsearch ...

  5. 图数据库对比:Neo4j vs Nebula Graph vs HugeGraph

    本文系腾讯云安全团队李航宇.邓昶博撰写 图数据库在挖掘黑灰团伙以及建立安全知识图谱等安全领域有着天然的优势.为了能更好的服务业务,选择一款高效并且贴合业务发展的图数据库就变得尤为关键.本文挑选了几款业 ...

  6. neo4j(图数据库)是什么?

    不多说,直接上干货! 作为一款强健的,可伸缩的高性能数据库,Neo4j最适合完整的企业部署或者用于一个轻量级项目中完整服务器的一个子集存在. 它包括如下几个显著特点: 完整的ACID支持 高可用性 轻 ...

  7. Neo4j图数据库从入门到精通

    目录 第一章:介绍 Neo4j是什么 Neo4j的特点 Neo4j的优点 第二章:安装 1.环境 2.下载 3.开启远程访问 4.启动 第三章:CQL 1.CQL简介 2.Neo4j CQL命令/条款 ...

  8. Nebula Graph 技术总监陈恒:图数据库怎么和深度学习框架进行结合?

    引子 Nebula Graph 的技术总监在 09.24 - 09.30 期间同开源中国·高手问答的小伙伴们以「图数据库的设计和实践」为切入点展开讨论,包括:「图数据库的存储设计」.「图数据库的计算设 ...

  9. Nebula 架构剖析系列(零)图数据库的整体架构设计

    Nebula Graph 是一个高性能的分布式开源图数据库,本文为大家介绍 Nebula Graph 的整体架构. 一个完整的 Nebula 部署集群包含三个服务,即  Query Service,S ...

  10. Nebula 架构剖析系列(一)图数据库的存储设计

    摘要 在讨论某个数据库时,存储 ( Storage ) 和计算 ( Query Engine ) 通常是讨论的热点,也是爱好者们了解某个数据库不可或缺的部分.每个数据库都有其独有的存储.计算方式,今天 ...

随机推荐

  1. 淘宝商品详情 API的使用说明

    淘宝平台提供了 API 接口可以用于获取淘宝商品详情信息.通过 API 接口,我们可以获取到商品的基本信息.价格.评论及评价等详细信息.以下是使用说明: 获取淘宝API账号 在获取淘宝商品详情 API ...

  2. 斜率优化DP 学习笔记

    斜率优化 DP 适用情况 适用于求解最优解(最大.最小)问题. 上凸壳与下凸壳 求解步骤 对于任意状态转义方程,设 \(A_i\),\(B_i\),使状态转移方程转化为 \(f_i = \min(f_ ...

  3. vue2和vue3使用echarts时无数据,怎么显示暂无数据图片或文字

    一开始也经历了用v-if和v-show,v-show的话echarts还会留出暂无数据图片的位置,导致echarts变形,v-if在加载和不加载切换时,dom会获取不到:后来也是在网上找的方法,时间有 ...

  4. 记一次 .NET 某拍摄监控软件 卡死分析

    一:背景 1. 讲故事 今天本来想写一篇 非托管泄露 的生产事故分析,但想着昨天就上了一篇非托管文章,连着写也没什么意思,换个口味吧,刚好前些天有位朋友也找到我,说他们的拍摄监控软件卡死了,让我帮忙分 ...

  5. heygen模型接口 简单使用 java版

    HeyGen - AI Spokesperson Video Creator  官网地址 Create a video (heygen.com) api地址 简介: 公司最近对ai方面业务比较感兴趣了 ...

  6. PDFPlumber使用入门

    目录 背景 教程开始 应用场景 安装 命令行使用 可选参数 Python包 简单样例 读取PDF pdfplumber.PDF类 pdfplumber.Page类 对象(Object) chars / ...

  7. PostgreSQL学习笔记-1.基础知识:创建、删除数据库和表格

    PostgreSQL 创建数据库 PostgreSQL 创建数据库可以用以下三种方式:1.使用 CREATE DATABASE SQL 语句来创建.2.使用 createdb 命令来创建.3.使用 p ...

  8. 基于 Python 和 Vue 的在线评测系统

    基于 Docker,真正一键部署 前后端分离,模块化编程,微服务 ACM/OI 两种比赛模式.实时/非实时评判 任意选择 丰富的可视化图表,一图胜千言 支持 Template Problem,可以添加 ...

  9. Stable Diffusion

     Stable Diffusion  ...using diffusers Stable Diffusion is a text-to-image latent diffusion model cre ...

  10. 手敲,Ascend算子开发入门笔记分享

    本文分享自华为云社区<Ascend算子开发入门笔记>,作者: JeffDing . 基础概念 什么是Ascend C Ascend C是CANN针对算子开发场景推出的编程语言,原生支持C和 ...