暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

关于图数据库(Neo4j)你应该知道的一切

2487

图数据库(Graph Database)是一种非关系型数据库,它使用图结构来存储和查询数据。开源图数据库的代表产品是 Neo4j,今天就以 Neo4j 为例,介绍一下图数据库的基本概念、设计模式、查询方法以及部分最佳实践。

首先 Neo4j 是一个图数据库管理系统,应该是知名度最广的开源图数据库,由「Neo4j, Inc.」公司开发。

图数据库和 Neo4j由于图数据库应用没有关系数据库广泛,造成同类产品比较少, Neo4j 又占据了开源和先发的优势, 2010 年 2 月就发布了 1.0 版本,导致很长一段时间内,说起图数据库,大家只知道 Neo4j。

从我们之前维护的数据库知识图谱中可以看到,图数据库分类下还是有很多产品的:

图数据库(排名不分先后)

名字由来

Google Group 上有一个帖子问『Neo4j 这个名字是是什么意思』, 4j 肯定是 『for Java』的意思, Neo 有人说可能是参考黑客帝国的角色 Neo。

I think "Neo" is a reference to the Matrix character.

也有人说是「物之新生」或者「物联引擎」:

"Neo" has also been transcribed to "New Energy for Objects" and "Network Engine for Objects" :)

我也只是好奇八卦一下,名字怎么来的并不影响咱们后面的使用。

Neo4j vs AuraDB vs Cypher

「Neo4j Inc」 公司的数据库产品叫 《Neo4j Graph Database[1]》也就是我们常说的 Neo4j 数据库, 也叫 Neo4j DBMS

Neo4j DBMS

除了 Neo4j 数据库,Neo4j 公司还开发了新的产品 《Neo4j AuraDB[2]》。这是一个完全托管的云服务,相当于 Neo4j DBMS 的 SaaS 版本。

Neo4j AuraDB

Neo4j 公司另外一个最重要的产品就是 《Cypher[3]》, 这是 Neo4j 数据库的查询语言,他的作用就相当于 SQL 之于关系数据库。

Cypher

下载安装

如果大家想在本地开发测试使用 Neo4j 那就不要在官网点击「免费试用」, 直接打开Neo4j 下载中心[4], 点击下载即可:

Neo4j 下载中心

不过注意, Neo4j Desktop 虽然把所有的工具都包含进去了,非常方便,但是它并不适合生产环境使用

Neo4j Desktop is not suited for production environments.

安装过程我就不说了,选择合适的版本一路点下去就行。安装之后就有 Neo4j 程序的启动图标。

生产环境生产环境安装 Neo4j 我建议使用容器安装,把数据文件挂载出来,这样的话安装和运维都比较简单,而且稳定性也高。可以参考 Neo4j 容器安装文档[5]来进行操作。

软件使用

打开 Desktop 就可以看到应用程序界面:

左上角的三个图标分别是「项目」、「数据库」和「应用」。

第一个功能是「项目」,Neo4j Desktop 安装好之后会自带一个默认的样例项目,里面有一个电影数据库。

Desktop 样例项目

点击启动按钮可以启动这个数据库,启动之后我们看到数据库的状态是 Active

启动电影样例数据库

点击右上角的 『Open』 按钮,会默认新窗口打开 Neo4pj Browser,这是一个 GUI 的客户端工具,你可以通过它查看数据库链接信息、服务状态,还可以执行命令。

我们先从左侧收藏里面执行下 Hello World
,然后返回数据:

命令如下,你也可以拷贝或者手输命令在执行框里面点击执行。具体 Cypher 代码语法含义我们等到后面讲 Cypher 语言的时候再讲。

// Hello World!
CREATE (database:Database {name:"Neo4j"})-[r:SAYS]->(message:Message {name:"Hello World!"}) RETURN database, message, r

上述代码创建了一个具有 Database
 标签的节点,一个 Message
 标签的节点,以及一个 SAYS
 类型的边,并返回。

语法解释()
 表示一个节点,()-[]->()
 表示一个边

:
 冒号前面表示变量,冒号后面来表示 节点标签 或者 边类型

图数据库

为什么用图数据库

现实中的很多场景天然就是以图的概念存储的,比如人和人之间的关系,每个人有自己的定义和属性,人与人之间会有各种可能的关系。

任何人之间的关系

这样的场景用传统的关系型数据库存储的话,无论是从数据结构设计,还是最终的查询性能上都有很多的损耗,此时图数据库就能很好的满足这些场景。

图数据库的优点图数据库可以在大图中深度和广度的恒定时间遍历,在 浏览深层结构 时架构的优越性表现尤其明显。图数据库还可以灵活动态的增加新的关系和属性,并且没有太多的维护和性能损耗。

图数据库的组成

像我们之前《构建个人知识图谱》文章里提到的一样,图数据库有三个概念来表示数据:节点关系属性。节点和关系又组成了经典的三元组关系:节点-关系-节点,节点和关系上都可以有自己的属性。

图数据库的基本构成(图片来自 Neo4j 网站)

节点

节点可以用 labels
 标记, 比如动物
软件
公司
网站
书籍
 等都可以作为 「标签」,在《构建个人知识图谱》系列中我们称之为「类型」。。

不同的标签在 Neo4j 的工具会被显示成不同的颜色。

节点可以保存任意数量的属性,当然也可以没有属性。比如张三
这个节点,可能有 姓名
性别
名族
年龄
等众多属性。

作为数据库节点还可以有一些约束性质的属性。比如 姓名不能为空
身份证号不能重复
等。

关系

Neo4j 的关系是有方向的,用于连接两个节点。关系也可以有自己的类型属性

每个节点的关系数量也是不受限制的,而且我们还可以通过正向或者逆向的方向来查找节点。

数据建模

我们动手开始所有项目和数据库之前,第一件事情肯定是建模。所谓建模就是描述清楚你要做事情的结构, 有哪些数据、数据之间的结构是什么。对这些模型胸有成竹,后面做事情才可以游刃有余。

白板数据建模法

Neo4j 官方推荐白板建模法,白板建模法:就是直接在白板(黑板当然也行)上绘制想要的模型和关系,然后照搬的图数据库中即可。

比如我们想围绕李小龙先生进行一个图谱数据建设,第一步就可以列出实体:

我后面直接用绘图软件画图来模拟白板。

绘制实体节点

然后绘制好连线,可能是这样子:

绘制节点之间的关系

一般来说,主要的连线在白板上绘制实体的时候已经绘制好了。

下一步,添加标签和节点属性,我们用不同颜色表示不同类型的节点(标签),一个节点可能有多种类型标签, 同时完善节点和关系的属性,当然别忘了关系也有自己的关系类型哦。

标记节点属性

最后一步就是把我们刚刚梳理的关系原样录入图数据库了。

建模步骤

我们再画一个例子,一步步看下怎么样从零开始图数据库的建模。

场景描述

首先我们描述下我们的需求,这次我们换个需求,关注下西游记:

金角大王和银角大王两个妖怪是兄弟俩,他们都想吃唐僧这个和尚的肉。

提取节点

比如可以从上面的场景描述中提取如下节点:

  • • 金角大王

  • • 银角大王

  • • 唐僧

如何识别节点场景中的每一个实体都是一个节点,比如金角大王
银角大王
这两个妖怪唐僧
 是个 和尚妖怪
以及和尚
这个职业,在我们的场景中更偏向是一种类型,这类节点、和类型(/属性)定位不清楚的名词否作为节点我们后面单独讨论。

提取节点标签

前面提到,一个节点可能有多个标签,这些标签可以为节点进行分类或分组。比如按照对按照职业或者身份进行分组,可能有「教师」、 「学生」、「演员」、「工程师」、「历史学家」等等很多标签,同时一个人也可以兼顾不同的身份。

金角大王和银角大王两个妖怪是兄弟俩,他们都想吃唐僧这个和尚的肉。

这样的话,上面场景我们可以提取出如下标签:

  • • 妖怪

  • • 和尚

为什么使用标签在图数据库中,标签就相当于一个集合,我们在查询的时候可以快速使用标签类划分查询范围,让查询更容易编写以及更加高效。通常情况下,我们的查询也都是在集合内,而不是整个图。

定义关系

现在我们有了数据主体,但是这些主体目前还是孤零零的个体,因为还缺少了图数据库中的一个重要部分:『关系』。

关系允许我们链接两个节点,它有一个源节点、带箭头的方向以及一个目标节点,同时关系也有自己的名字(动作),以及他们自己的属性。

金角大王和银角大王两个妖怪是兄弟俩,他们都想吃唐僧这个和尚的肉。

在上面这个场景中我们可以找到关系:

  • • 金角大王和银角大王是兄弟

  • • 他们想吃唐僧

「师兄弟」和「想吃唐僧」这两个动作就是我们说的关系了。

定义属性

简单来说,我们已经完成了图数据库的定义,因为画出来的图已经有模有样了。但是有时候为了存储更多的信息,更准确的描述实体,我们往往还需要定义属性。

属性是可以存储在节点或关系上的数据的名称/值对。常见的数据类型都可以作为属性值:

  • • 字符

  • • 整数、小数

  • • 布尔

  • • 日期和时间

我们对于节点本身的任何疑问都可以变成属性来设计:

  • • 金角大王几岁了

  • • 银角大王身高多少

  • • 金角大王的真身是什么

  • • 唐僧是男是女

  • • 唐僧多少斤

  • • 唐僧生日是哪天

  • • 唐僧什么时候当和尚的

  • • ...

只要你有问有答,他就可以作为一个属性。

创建模型

创建模型需要使用 Cypher 语句,刚刚的西游记三人组我们使用 MERGE
 字据来创建数据模型。在 Neo4j Browser 中运行以下代码:

MERGE (j:Monster {name: '金角大王'})
    ON CREATE set j.age = 3100
MERGE (y:Monster {name: '银角大王'})
  ON CREATE set y.age = 2800
MERGE (t:Monk {title: '唐僧'})
  ON CREATE set t.height = 182
MERGE (j)-[rel1:IS_BROTHER]->(y)
  ON CREATE SET rel1.since = '-2023'
MERGE (j)-[rel2:WANT_EAT]->(t)
  ON CREATE SET rel2.desire = 4, rel2.possibility = 0
MERGE (y)-[rel3:WANT_EAT]->(t)
  ON CREATE SET rel3.desire = 3, rel3.possibility = 0

语法解释MERGE ... ON CREATE ...
 表示查找节点,如果不存在则创建并设置属性

可以看到提示创建成功:

节点和关系创建成功

接着我们执行查询验证下:

// Get some data
MATCH (n1)-[r]->(n2) RETURN r, n1, n2 LIMIT 25

语法解释MATCH ()-[]->() RETURN ...
 表示查找所有关系,并返回关系中的节点和边

可以看到查询结果:

查询结果

点击「金角大王」节点,可以看到节点标签和属性(其他节点以同样):金角大王是一个 Monster
,年龄是 3100
 岁,名字叫金角大王

节点标签和属性

图数据的删除因为 Neo4j 不允许出现损坏的边,也就是不能允许边只有一个端点。所以我们再删除数据的时候必须先删除边,再删除节点

我们先删除妖怪想吃唐僧
 这些边:

MATCH (n:Monster)-[r:WANT_EAT]->() 
DELETE r

提示成功删除两条边:

然后删除唐僧这个和尚,执行下面语句:

MATCH (n:Monk {title: '唐僧'})
DELETE n

唐僧删除成功

以及兄弟这条边:

MATCH (n:Monster)-[r:IS_BROTHER]->() 
DELETE r

删除所有妖怪:

MATCH (n:Monster)
DELETE n

中文标签

我们刚才用 Monk
 Moster
 WANT_EAT
 IS_BROWSER
 这些英文单词来表示节点和边,有人可能耗时是不是标签只能用英文?

当然不是,中文标签 Neo4j 也是支持的。

用英文标签纯粹是为了在写查询和程序的时候可以不切换输入法,并且大小写可以区分边和、节点。

我们改一下建模语句,先创建节点河边:

MERGE (j:妖怪 {姓名: '金角大王'})
  ON CREATE set j.年龄 = 3100
MERGE (y:妖怪 {姓名: '银角大王'})
  ON CREATE set y.年龄 = 2800
MERGE (t:和尚 {姓名: '唐僧'})
  ON CREATE set t.身高 = 182
MERGE (j)-[rel1:是兄弟]->(y)
  ON CREATE SET rel1.自从 = '-2023'
MERGE (j)-[rel2:想吃]->(t)
  ON CREATE SET rel2.程度 = 4, rel2.可能性 = 0
MERGE (y)-[rel3:想吃]->(t)
  ON CREATE SET rel3.程度 = 3, rel3.可能性 = 0

然后查询返回:

// Query
MATCH (n1)-[r]->(n2) RETURN r, n1, n2 LIMIT 25

完全中文的节点

但是这样有一个问题,Neo4j 不知道哪个节点是节点名称,所以建议 title
 和 name
 属性使用英文,其他的属性可以根据需要使用中文或者英文。当然你也可以在 Neo4j Browser 里面进行设置节点名称使用哪个属性。

模型设计

我们熟悉了基本的建模流程之后,再回来考虑刚刚抛出的问题:节点上的内容应该怎么建模?

我们前面有一个例子是说「妖怪」和「和尚」这两个身份,当时我们是使用标签来表示的。其实还有两种表示方法:使用属性节点

使用属性表示的时候,他么都有一个统一的属性名字在区分,我们再扩展一下身份,唐僧不仅是谁和尚还是人,看看怎么表示:

使用属性表示身份

当然也可以用节点来表示:

使用节点表示身份

这三种方式没有优劣,最终还是看你对这些数据的使用和组织方式,怎么维护和查询方便就怎么定义。

复杂的数据结构

看到这里,你可能觉得你已经掌握了 Neo4j 图数据库的建模,足矣应对你的使用需求。

大多数时候是这样的,但是我还是有几个小技巧来教给你。

超边和中间点

还是拿西游记中金角大王和唐僧的故事来举例,如果我们要查询下他们什么时候初次见面的怎么办?

这个时候我们就可以用「超边」或者说是「中间节点」这个技术来进行建模。

使用中间节点进行建模

日期事件

我们扩展一下,这个中间节点不只有一次,或者是周期性发生的,比如学生每个学期的开学、打工人每天的打卡, 西游记里面唐僧的十次转世投胎,而对于「开学」、「打开」、「转世」这些事情有公共的属性,但是每次发生又有独立的属性。

此时我们可以把这个事件本身作为一个节点,每次事件发生也作为一个节点,就既能满足通用型查询、又能满足个性事件查询。

索引

图数据库和关系数据库一样也有索引的概念。合理的运用索引能很好的提升搜索性能。Neo4j 可以为节点和边创建索引,支持下面索引类型:

  • • 范围索引

  • • Lookup 索引

  • • 文本索引

  • • 点索引

  • • 全文索引。

对索引有兴趣的可以查看官方的 索引和搜索性能[6]文档。

结语

图数据库很强大,我们做设计的时候对其基本概念有一个完整的了解很有必要,了解了这些之后再我们应用的时候一些更高级的概念时, 结合我们之前的工程知识就变得信手拈来了, 比如 Cypher 查询、子查询、条件匹配、函数的使用、查询调优等等。

最后希望大家都能在图数据库的世界里尽情遨游。

引用链接

[1]
 Neo4j Graph Database: https://neo4j.com/product/neo4j-graph-database/
[2]
 Neo4j AuraDB: https://neo4j.com/cloud/platform/aura-graph-database/
[3]
 Cypher: https://neo4j.com/product/cypher-graph-query-language/
[4]
 Neo4j 下载中心: https://neo4j.com/download/
[5]
 Neo4j 容器安装文档: https://neo4j.com/docs/operations-manual/current/docker/introduction/
[6]
 索引和搜索性能: https://neo4j.com/docs/cypher-manual/current/indexes-for-search-performance/


文章转载自向量检索实验室,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论