文章目录
- 第一章 图和Neo4j
- 1.1 图数据库概念
- 1.1.1 图论起源
- 1.1.2 节点-关系及图
- 1.1.3 图数据库
- 1.1.4 图数据库分类
- 1.1.4 图数据库应用场景
- 1.1.5 与关系型数据库对比
- 1.1.6 图数据库优势
- 1.2 Neo4j介绍
- 1.2.1 Neo4j是什么
- 1.2.2 Neo4j特点
- 1.2.3 Neo4j的优势
- 1.2.4 Neo4j的限制
- 1.2.5 Neo4j数据模型
- 1.2.5.1 节点
- 1.1.5.2 属性
- 1.2.5.3 关系
- 1.2.5.4 标签
- 1.2.5.6 社区版和企业版区别
- 第二章 Neo4j安装部署
- 2.1 系统需求
- 2.2 安装部署
- 2.2.1 基于Linux的单机安装部署
- 2.2.2 Windows安装Neo4j
- 2.2.3 Docker安装
- 2.2.4 集群安装部署
- 2.2.4.1 集群介绍
- 2.2.4.2 集群规划
- 2.2.4.3 集群核心配置
- 2.4.4.4 集群部署
- 2.4.4.5 添加Core Server
- 2.4.4.6 添加Read Replica到集群
- 2.4.4.7 移除核心服务
- 第三章 Neo4j操作
- 3.1 Cypher介绍
- 3.2 语法
- 3.3 图数据库操作
- 3.3.1 CREATE命令
- 3.3.2 CREATE创建关系
- 3.3.3 MATCH查询语句
- 3.3.4 RETURN语句
- 3.3.5 LOAD CSV
- 3.3.6 WHERE条件语句
- 3.3.7 IN操作
- 3.3.8 SET子句
- 3.3.9 ORDER BY排序
- 3.3.10 LIMIT && SKIP子句
- 3.3.11 UNION && UNION ALL
- 3.3.12 MERGE合并
- 3.3.13 NULL值
- 3.3.14 DELETE语句
- 3.3.15 REMOVE语句
- 3.4 函数
- 3.4.1 函数介绍
- 3.4.2 判定函数
- 3.4.3 标量函数
- 3.4.4 聚合函数
- 3.4.5 列表函数
- 3.4.6 字符串函数
- 3.4.7 数学函数
- 3.4.8 时间函数
- 3.4.9 空间函数
- 3.4.10 用户自定义函数
- 第四章 Neo4j Admin
- 4.1 备份与恢复
- 4.2 索引管理
- 4.2.1 复合索引限制
- 4.2.2 创建单值属性索引
- 4.2.3 创建复合索引
- 4.2.4 获取所有索引
- 4.2.5 删除单值索引
- 4.2.6 删除复合索引
- 4.2.7 索引的使用
- 4.3 约束管理
- 4.3.1 创建节点唯一约束
- 4.3.2 删除节点唯一属性
- 4.3.2 创建节点属性存在性约束
- 4.3.3 删除节点属性存在性约束
- 4.3.4 创建关系属性存在性约束
- 4.3.5 删除关系属性存在性约束
- 4.4 批量导入
- 第五章 API开发
- 5.1 Java开发介绍
- 5.2 Neo4j原生Java开发
第一章 图和Neo4j
1.1 图数据库概念
1.1.1 图论起源
众所周知,图论起源于一个非常经典的问题——「柯尼斯堡(Königsberg)七桥问题」。在加里宁格勒(Kaliningrad)有七座桥,连接着由普雷戈里亚(Pregolya)河分割而成的两个岛屿和两大陆地。
当时,有一个与柯尼斯堡的桥相关的脑筋急转弯:如何只穿过桥一次而穿过整个城市。下图为柯尼斯堡七座桥的简化图:
大家可以尝试一下,在穿过每座桥仅一次的情况下穿过这个城市。每座桥,意味着所有桥都被穿过;只穿过一次,意味着每座桥不能被穿越两次及以上
。如果你对这一问题有所了解,就知道这不可能。
1738年,瑞典数学家欧拉( Leornhard Euler)解决了柯尼斯堡七桥问题。由此图论诞生,欧拉也成为图论的创始人。
欧拉把问题的实质归于一笔画
问题,即判断一个图是否能够遍历完所有的边(Edge)而没有重复,而柯尼斯堡七桥问题则是一笔画
问题的一个具体情境。欧拉证明这个问题不成立。
满足一笔画
的图满足两个条件:
-
图必须是一个完整图
-
有零个或二个奇数点
欧拉后来以拉丁文正式发表了论文“关于位置几何问题的解法(Solutio problematis ad geometriam situs pertinentis, Commentarii academiae scientiarum Petropolitanae, vol. 8, pp. 128-140, 1741)
,文中详细讨论了七桥问题并作了一些推广。该论文被认为是数学图论、拓扑学和网络科学的发端。
1.1.2 节点-关系及图
-
节点
节点及其关系是图的重要组成元素。在图形理论中,我们可以使用圆表示一个节点 并且可以向里面添加键值对形式的数据,节点如下图所示。
-
关系
关系是节点与节点之间的联系,节点越多,联系越多,关系相对就越复杂。关系通常有方向,分为单向或双向,具体的关系如下图所示:
上图表示:
- Node1到Node3和Node2到Node3都是单向关系
- Node1到Node2关系是双向
-
图
图是节点(属性)和关系连接组成,一个简单的图如下图所示:
图及相关特征很多,主要如下:
- 图表示节点,关系和属性中的数据
- 节点和关系都包含属性
- 关系连接节点
- 属性是键值对
- 节点用圆圈表示,关系用方向键表示
- 关系具有方向:单向和双向
- 每个关系包含“开始节点”或“从节点” 和 “到节点”或“结束节点”
1.1.3 图数据库
一般情况下,我们使用数据库查找事物间的联系的时候,只需要短程关系的查询(两层以内的关联)。当需要进行更长程的,更广范围的关系查询时,就需要图数据库的功能。而随着社交、电商、金融、零售、物联网等行业的快速发展,现实世界的事物之间织起了一张巨大复杂的关系网,传统数据库面对这样复杂关系往往束手无策。因此,图数据库应运而生。
图数据库(Graph database)指的是以图数据结构的形式来存储和查询数据的数据库。其数据模型采用图结构,由节点和关系组成,并可以存储节点和关系的属性,实现复杂关系的存储和查询。是最接近高性能的一种用于存储数据的数据结构方式之一。
1.1.4 图数据库分类
DB-Engines Ranking根据受欢迎程度对数据库管理系统进行排名。该排名每月更新一次。排名地址:https://db-engines.com/en/ranking/graph+dbms
具体分类对比如下:
具体每种数据库介绍:
-
Neo4j(主流)
历史悠久且长期处于图数据库领域的主力地位,其功能强大,性能也不错,单节点的服务器可承载上亿级的节点和关系。社区版最多支持 320 亿个节点、320 亿个关系和 640 亿个属性。
优点:Neo4j有自己的后端存储,不必如同JanusGraph等一样还要依赖另外的数据库存储。 Neo4j在每个节点中存储了每个边的指针,因而遍历时效率相当高。
缺点:企业版付费。开源的社区版本只支持单机,不支持分布式。社区版只能部署成单实例,企业版可以部署成高可用集群,从而可以解决高并发量的问题;不能做集群,单个实例故障时影响系统正常运行。社区版只支持冷备份,即需要停止服务后才能进行备份。 -
OrientDB(不推荐)
OrientDB是第二代分布式图数据库,以混合数据模型为特点,它包括可以在最复杂的场景中使用复制和分片,并以Apache2许可证提供开放源代码。ORIENTDB工作速度快,能够在最常见的硬件上每秒存储220000条记录,并且支持无模式、完整和混合模式,可以使用SQL作为查询语言之一。
优点:ORIENTDB使用身份验证、密码和静态数据加密等方式为所有机密数据提供安全保护。OrientDB为确保更好的性能,最近引入了节点的快速重新同步,即使处理数十亿条记录,遍历速度也不会受到影响。OrientDB 是分布式多模型数据库,支持图数据模型,支持 sharding 机制,大规模查询情况下性能比较好;
缺点:开源版功能部分欠缺。起步较早,最初的时候都是一个单机的图数据库,然后随着用户数据量的不断增加,后期增加了分布式模式,支持集群和副本,但是由于后加的功能,其分布式支持的不是很好。 -
ArangoDB(不推荐)
Arangodb以一种非常创造性和灵活的方式安排数据。数据可以存储为键或值对、图或文档,所有这些都可以通过一种查询语言访问。为了更安全的选择,查询中可以使用声明性模型。用户可以在一个查询中组合不同的模型及其特性的原因是,ArangoDB对所有数据模型都使用相同的核心和相同的查询语言。
优点:Arangodb独特的特性是它能够在一个查询中组合不同的数据模型。这使得其展示方式令人印象深刻且美观。它比其他数据库具有更灵活的扩展性、增强的容错性、大容量的存储能力和更低的成本。arangodb最突出的特性是foxx,这是一个用于编写数据库中以数据为中心的javascript框架。缺点:它们起步比较早,最初的时候都是一个单机的图数据库,然后随着用户数据量的不断增加,后期增加了分布式模式,支持集群和副本,但是经过调研发现,可能是由于后加的功能,他们的分布式支持的不是很好。
-
JanusGraph(推荐)
JanusGraph是可扩展的图数据库,底层依赖于大数据组件,对分布式支持的非常好,也都是完全的开源免费,存储数据模型也都是专为图数据而设计。JanusGraph基于Titan发展而来,包含其所有功能,采用Tikerpop的Gremlin图查询语言,有单独的后端存储,支持Cassandra/HBase/BerkeleyDB等做存储,支持Solr/ES/Lucence等做图索引 支持Spark GraphX/Giraph等图分析计算引擎及Hadoop分布式计算框架 原生支持集成了Tinkerpop系列组件:Gremlin查询语言,Gremlin-Server及Gremlin applications。 采用很友好的Apache2.0协议,支持对接可视化组件如Cytoscape,Gephi plugin for Apache TinkerPop,Graphexp,KeyLines by Cambridge Intelligence,Linkurious
优点:JanusGraph的存储系统依赖于像Cassandra、HBase(HBase又依赖于Zookeeper和HDFS)、BerkelyDB等等这样的存储系统,索引系统依赖于Elasticsearch、Solr、Lucene等等;也基于这些原因,它和大数据生态结合的非常好,可以很好地和Spark结合做一些大型的图计算。所以可以很好的和spark的大数据平台进行结合,并且能够支持实时图遍历和分析查询
缺点:其存储需要依赖于其他存储系统,JanusGraph使用HBase作为底层存储系统,而HBase又依赖于Zookeeper和HDFS,另外JanusGraph的索引又依赖于ES,所以想要搭建一套完整的JanusGraph,需要同时搭建维护好几套系统,维护成本非常大。另一问题就是稳定性,根据经验来看,系统越复杂,依赖系统越多,整体可控性就越差,稳定性风险就越大。并且三方的一些工具也存在一些问题,所以要用肯定要基于底层(读写)进行性能优化。 -
HugeGraph(推荐)
百度基于JanusGraph开源了HugeGraph,增加了很多特性,提高了易用性及性能,增加了一些图分析算法。实现了Apache ThinkerPop 3框架,支持Gremlin图查询语言。HugeGraph支持多用户并行操作,输入Gremlin查询语句,并及时得到图查询结果。也可以再用户程序中调用hugeGraph API进行图分析或查询。
优点:HugeGraph可以与Spark GraphX进行链接,借助Spark GraphX图分析算法(如PageRank、Connected Components、Triangle Count等)对HugeGraph的数据进行分析挖掘。HugeGraph还针对图数据库的高频应用(例如:ShortestPath、k-out、k-neighbor等)做了特定性能优化,并且为用户提供更为高效的使用体验
缺点:基于JanusGraph开源,存在和JanusGraph同样的问题,维护成本高。 -
Dgraph(推荐)
dgraph 是基于 golang 开发的开源的分布式图数据库。诞生时间不长, 发展却很迅速,从设计之初就考虑了分布式和扩展性,所以对分布式支持的非常好。
优点:Dgraph 不依赖与任何第三方系统,只有一个 Dgraph 可执行文件,只需在启动时通过参数指定是 Zero(管理节点)还是 Alpha(数据节点)即可,Dgraph 会自动组成集群,运维部署非常简单。Dgraph维护成本低很多。Dgraph 和 JanusGraph 性能差不多,但复杂查询下,Dgraph 性能远高于 JanusGraph。同时,Dgraph 的写入性能也整体高于 janusGraph。
缺点:比如还不支持多重边、一个集群只支持一个图、与大数据生态兼容不足等,这些都需要靠后期不断完善。 -
TigerGraph(不推荐)
TigerGraph是一个目前业界先进的企业级图数据库。系统完全闭源。部分查询算法开源。分为开发版和企业版。开发版免费,但功能受限,比如单点只能构建一个图。企业面收费,支持大规模集群,顶点表数量不受限制。
优点:TigerGraph可以通过GSQL实现类存储过程的算法封装,而且已经实现了很多图算法,但是语法结构要比Neo4j复杂的多。
缺点:付费图数据库。Neo4j按照cpu收费,TigerGraph按照数据容量(G)来收费,费用较贵。
1.1.4 图数据库应用场景
图数据库广泛应用于社交媒体、金融、物流、医疗、能源等领域。以下是图数据库主要应用场景:
-
社交媒体:图数据库可以对社交网络中的关系和行为进行建模和分析,帮助社交媒体企业更好地了解用户需求和行为,实现精准定向广告和推荐。
-
金融:图数据库可以帮助金融机构识别和预测欺诈行为、洗钱、风险管理等,从而提高金融业务的安全性和可靠性。
-
物流:图数据库可以管理物流中的运输网络和物流信息,实现物流运输过程的可视化、实时监控和优化。
-
医疗:图数据库可以帮助医疗机构分析医疗记录、患者病史、药品治疗效果等数据,优化医疗服务流程,支持医疗决策和疾病预测。
-
能源:图数据库可以帮助能源企业管理能源产业链上的复杂关系和数据,提高能源效率、降低成本、控制风险。
1.1.5 与关系型数据库对比
在需要表示多对多关系时,我们常常需要创建一个关联表来记录不同实体的多对多关系。如果两个实体之间拥有多种关系,那么我们就需要在它们之间创建多个关联表。而在一个图形数据库中,我们只需要标明两者之间存在着不同的关系。如果希望在两个结点集间建立双向关系,我们就需要为每个方向定义一个关系。 也就是说,相对于关系型数据库中的各种关联表,图形数据库中的关系可以通过关系属性这一功能来提供更为丰富的关系展现方式。因此相较于关系型数据库,图形数据库的用户在对现实进行抽象时将拥有一个额外的武器,那就是丰富的关系。
例如,在下面的这个例子中,我们希望在一个社交网络里面找到最大深度为5的朋友的朋友。假设随机算则两个人,是否存在一条路径,使得关联他们的关系长度最多为5?对于一个包含100万人,每人约有50个朋友的社交网络,我们就以典型的开源图数据库Neo4j参与测试,结果明显表明,图数据库是用于关联数据的最佳选择,如下表所示。
深度 | 关系型数据库执行时间(s) | Neo4j执行时间(s) | 返回的记录条数 |
---|---|---|---|
2 | 0.016 | 0.01 | ~2500 |
3 | 30.267 | 0.168 | ~110000 |
4 | 1543.505 | 1.359 | ~600000 |
5 | 未完成 | 2.132 | ~800000 |
通过上表可知,当深度为2时,关系型数据库和Neo4j都是毫秒级差异,Neo4j性能较好,但相差不大。当深度为3时,即朋友的朋友的朋友比较多了,这时候,明显关系型数据库要慢了很多很多,在线系统无法等待和承受30多秒的时间范围。相比之下,Neo4j的响应时间保持相对平滑:执行查询在毫秒和秒级别,相对在线系统来说速度时较快和比较快。
1.1.6 图数据库优势
图数据库相对于其他传统的数据库有很多优势,以下是常见优势:
- 灵活的数据模型:图数据库支持灵活的数据模型,可以存储复杂的实体类型和其之间的关系,如社交网络、地图路线等复杂模型。
- 强大的关系查询能力:图数据库通过树状遍历方式遍历关系,使用广度优先搜索和深度优先搜索算法,提供更快速、更精确的关系查询和分析。
- 高效的数据处理能力:图数据库处理大规模图数据的效率更高,能够对图数据进行快速存储、索引和查询,降低了大数据量和高并发访问时的数据处理成本和时间成本。
- 多语言支持:图数据库支持多种语言,为多类开发者和企业提供了更便利的操作性和接口。
1.2 Neo4j介绍
1.2.1 Neo4j是什么
Neo4j是一个开源的、 无Shcema的、 基于java开发的图形数据库,它将结构化数据存储在图中而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎。程序数据是在一个面向对象的、灵活的网络结构下,而不是严格、静态的表中,但可以享受到具备完全的事务特性、企业级的数据库的所有好处。
Neo4j最初是一个图形数据库,现在已经发展成为一个拥有众多工具、应用程序和库的丰富生态系统。这个生态系统允许您将图形技术与您的工作环境无缝集成。
-
Neo4j Graph Database
Neo4j Graph Database是核心产品,一个原生图形数据库,用于存储和检索有联系的数据。有两个版本,社区版和企业版。有多种方法可以开始使用Neo4j:
- 通过在本地安装
- 通过在云平台上部署(现目前支持:
Neo4j on AWS
、Neo4j on Google Cloud Platform
和Neo4j on Microsoft Azure
) - 使用Neo4j Aura
-
Neo4j Aura
AuraDB和AuraDS是完全托管的云服务产品。Neo4j AuraDB是一个图形数据库服务,即生态体系里面自带的图形数据库服务(
实则就是一个Web UI工具
)。要与Aura实例交互,需要使用Neo4j Workspace—一个基于UI的低代码工具,它可以方便设计数据模型、预览模型、导入数据以及探索和查询数据库。
-
Neo4j Graph Data Science
Neo4j Graph Data Science (GDS)提供了超过65种图形算法,这些算法可以使用Neo4j执行并针对企业工作负载和管道进行优化。GDS帮助您从大数据中获得见解,以回答关键问题并改进预测。GDS是一个图形数据库的数据分析平台,它将机器学习和图形数据库统一到一个工作空间中。
-
Neo4j Tools
Neo4j提供了很多工具,旨在使图形应用程序的学习和开发更容易。
-
Neo4j Desktop:用于使用Neo4j的本地开发环境,无论是使用本地数据库实例还是位于远程服务器上的数据库。免费下载包括Neo4j企业版许可证。
-
Neo4j Browser:在线浏览器接口,查询和查看数据库中的数据。使用Cypher查询语言的基本可视化功能。
-
Neo4j Operations Manager:一个基于UI的工具,允许数据库管理员在企业版中监视、管理和操作所有的Neo4j数据库管理系统。
-
Data Importer:一个无代码工具,允许您从平面文件(.csv和.tsv)加载数据,定义图形模型,将数据映射到它,并将其导入Neo4j数据库。
-
Neo4j Bloom:用于业务用户的可视化工具,不需要任何代码或编程技能即可查看和分析数据。
-
-
Cypher query language
Cypher是一种基于openCypher倡议的开放数据查询语言。它是用于属性图的最成熟和最直观的查询语言。Cypher的特征如下:
-
易于学习
-
可视化与关系逻辑展示
-
安全、可靠、数据丰富
-
开放、灵活
-
-
Connecting to Neo4j
Neo4j为将Neo4j集成到任何工作环境和创建应用程序提供了多种方式。主要如下:
-
Neo4j Drivers:官方支持的驱动程序和社区贡献的连接驱动库。具体支持如下图所示:
-
Neo4j Connectors:整合Spark、Kafka(也被称为Neo4j流)和其他一些商业工具的连接器。BI连接器如下图所示:
-
GraphQL Library:是一个灵活、低代码、开源的JavaScript库,通过利用连接数据的力量,可以快速开发跨平台和移动应用程序的API。作用如下图所示:
-
OGM:Neo4j的对象图映射库。简单示例如下图所示:
-
1.2.2 Neo4j特点
Neo4j特点很多,下面列出其中主要特点:
- SQL就像简单的查询语言Neo4j CQL
- 它遵循属性图数据模型
- 它通过使用Apache Lucence支持索引
- 它支持UNIQUE约束
- 它包含一个用于执行CQL命令的UI:Neo4j数据浏览器
- 它支持完整的ACID(原子性,一致性,隔离性和持久性)规则
- 它采用原生图形库与本地GPE(图形处理引擎)
- 它支持查询的数据导出到JSON和XLS格式
- 它提供了REST API,可以被任何编程语言(如Java,Spring,Scala等)访问
- 它提供了可以通过任何UI MVC框架(如Node JS)访问的Java脚本
- 它支持两种Java API:Cypher API和Native Java API来开发Java应用程序
1.2.3 Neo4j的优势
- 它很容易表示连接的数据
- 检索/遍历/导航更多的连接数据是非常容易和快速的
- 它非常容易地表示半结构化数据
- Neo4j CQL查询语言命令是人性化的可读格式,非常容易学习
- 使用简单而强大的数据模型
- 它不需要复杂的连接来检索连接的/相关的数据,因为它很容易检索它的相邻节点或关系细节没有连接或索引
1.2.4 Neo4j的限制
- 具有支持节点数,关系和属性的限制
- 不支持Sharding
1.2.5 Neo4j数据模型
Neo4j图数据库遵循属性图模型来存储和管理其数据。Neo4j图数据库将其所有数据存储在节点和关系中。我们不需要任何额外的RDBMS数据库或无SQL数据库来存储Neo4j数据库数据。它以图形的形式存储其数据的本机格式。Neo4j使用本机GPE(图形处理引擎)引擎来使用它的本机图存储格式。属性图模型规则:
- 表示节点,关系和属性中的数据
- 节点和关系都包含属性
- 关系连接节点
- 属性是键值对
- 节点用圆圈表示,关系用方向键表示。
- 关系具有方向:单向和双向。
- 每个关系包含“开始节点”或“从节点”和“到节点”或“结束节点”
在Neo4j中,关系也应该是有方向性的。如果我们尝试创建没有方向的关系,那么Neo4j会抛出一个错误消息,“关系应该是方向性的”。图形数据库数据模型的主要组件是:
- 节点
- 关系
- 属性
1.2.5.1 节点
节点是图表的基本单位。 它包含具有键值对的属性,如下所示
这里Node Name =“Employee”,它包含一组属性作为键值对。
1.1.5.2 属性
属性是用于描述图节点和关系的键值对:Key = 值
其中Key是一个字符串,值可以通过使用任何Neo4j数据类型来表示。
1.2.5.3 关系
关系是图形数据库的另一个主要构建块。 它连接两个节点,如下所示。
这里Emp和Dept是两个不同的节点。 “WORKS_FOR”是Emp和Dept节点之间的关系。
因为它表示从Emp到Dept的箭头标记,那么这种关系描述的一样
Emp WORKS_FOR Dept
每个关系包含一个起始节点(From Node)和一个结束节点(To Node), 对于节点又有两种关系:
- 传入关系
- 外向关系
“Emp”是起始节点。
“Dept”是结束节点、端节点。
由于该关系箭头标记表示从“Emp”节点到“Dept”节点的关系,该关系被称为“传入关系”到“Dept”节点,并且“外向关系”到“Emp”节点。
像节点一样,关系也可以包含属性作为键值对。
这里的“WORKS_FOR”关系有一个属性作为键值对ID = 123它代表了这种关系的一个ID。
1.2.5.4 标签
Label将一个公共名称与一组节点或关系相关联。 节点或关系可以包含一个或多个标签。 我们可以为现有节点或关系创建新标签。 我们可以从现有节点或关系中删除现有标签。
从前面的图中,我们可以观察到有两个节点。
左侧节点都有一个标签:“EMP”,而右侧节点都有一个标签:“Dept”。
这两个节点之间的关系,也有一个标签:“WORKS_FOR”
注:
Neo4j将数据存储在节点或关系的属性中。
1.2.5.6 社区版和企业版区别
Neo4j社区版启动程序和安装服务的区别如下:
- 容量:社区版最多支持 320 亿个节点、320 亿个关系和 640 亿个属性,而企业版没有这个限制;
- 并发:社区版只能部署成单实例,不能做集群。而企业版可以部署成高可用集群或因果集群,从而可以解决高并发量的问题;
- 容灾:由于企业版支持集群,部分实例出故障不会影响整个系统正常运行;
- 热备:社区版只支持冷备份,即需要停止服务后才能进行备份,而企业版支持热备,第一次是全量备份,后续是增量备份;
- 性能:社区版最多用到 4 个内核,而企业能用到全部内核,且对性能做了精心的优化;
- 支持:企业版客户能得到 5X10 电话支持(Neo4j 美国电话、邮件,微云数聚电话、微信、邮件);
- 插件:还有企业版可以使用Bloom、ETL这些工具。
第二章 Neo4j安装部署
Neo4j可以安装在许多环境中,适用于不同的环境,因此系统需求在很大程度上取决于软件的使用。
注意:
Neo4j AuraDB是一个完全托管的Neo4j数据库,托管在云中,不需要安装。
Neo4j可以在Docker容器中运行。
2.1 系统需求
Neo4j在物理、虚拟或容器化平台上支持x86_64和ARM架构的系统,具体相关系统需求如下图所示:
系统项 | 最低要求 | 推荐配置 | 备注 |
---|---|---|---|
CPU | Intel Core i3 | Intel Core i7 、 IBM POWER8 | |
Memory | 2G | 16—32GB or more | |
Disk | 10GB SATA | SSD w/ SATA Express, or NVMe | |
FileSystem | EXT4 (or similar) | EXT4、 ZFS | |
Software | OpenJDK 8[1]、 Oracle Java 8 、IBM Java 8 | 11也支持,但任何自定义都应该兼容jdk8 | |
Operating System | Ubuntu 18.04, 16.04, 14.04、Debian 10, 9, 8、CentOS 7, 6、Red Hat Enterprise Linux 7.9+、Amazon Linux 2、Windows Server 2016 | 支持Linux、MacOS、Windows | |
OSArchitectures | x86、OpenPOWER (POWER8) |
2.2 安装部署
Neo4j支持Linux、MacOS、Windows等主流操作系统之上安装,支持单机安装、集群安装部署等常见方式,也支持使用Docker容器进行快速安装部署。
2.2.1 基于Linux的单机安装部署
-
下载Neo4j
-
官方下载:https://neo4j.com/download-center/#community ,包含社区版本和企业版本,也包括rpm、yum源、apt源和压缩包等。
-
国内镜像下载:https://we-yun.com/index.php/blog/releases-56.html ,该地址有一些不完善,也不太建议大家在该地方下载。
注意:
1、Neo4j需要提前准备对应的JDK环境,neo4j 3.5 + 要求Java8;neo4j 4.x某些版本要求Java11;neo4j 5.9+要求Java 17,此处略过安装JDK步骤。
2、本次课程采用neo4j 3.5.35版本,操作系统使用Mac M1宿主机,Centos 7.7操作系统。
-
-
上传安装包到服务器
使用任何工具或任何方式均可,只要能将安装包放置到服务器中即可。
╰$ scp /Users/liyadong/Desktop/neo4j-enterprise-3.5.35-unix.tar.gz qianfeng01:/home
-
解压Neo4j
#解压到/usr/local目录 [root@qianfeng01 neo4j-enterprise-3.5.35]# tar -zxvf /home/neo4j-enterprise-3.5.35-unix.tar.gz -C /usr/local/#进入neo4j目录 [root@qianfeng01 neo4j-enterprise-3.5.35]# cd /usr/local/neo4j-enterprise-3.5.35#列出目录及说明 [root@qianfeng01 neo4j-enterprise-3.5.35]# ll 总用量 180 drwxr-xr-x 3 root root 111 8月 9 2022 bin #neo4j的启停、管理、备份等操作命令 drwxr-xr-x 2 root root 41 8月 18 11:54 certificates drwxr-xr-x 2 997 ssh_keys 62 8月 18 12:08 conf #默认neo4j配置目录 drwxr-xr-x 4 997 ssh_keys 35 8月 18 11:54 data #默认数据存储目录 drwxr-xr-x 2 997 ssh_keys 6 8月 9 2022 import #默认只能从该目录下导入数据文件 drwxr-xr-x 2 root root 12288 8月 18 11:12 lib #默认依赖库目录 -rw-r--r-- 1 997 ssh_keys 126887 8月 9 2022 LICENSES.txt -rw-r--r-- 1 997 ssh_keys 22701 8月 9 2022 LICENSE.txt drwxr-xr-x 2 997 ssh_keys 60 8月 18 11:54 logs #默认日志目录 drwxr-xr-x 2 root root 4096 8月 18 11:54 metrics -rw-r--r-- 1 997 ssh_keys 135 8月 9 2022 NOTICE.txt drwxr-xr-x 2 997 ssh_keys 24 8月 18 11:12 plugins #默认插件存储目录 -rw-r--r-- 1 997 ssh_keys 1676 8月 9 2022 README.txt drwxr-xr-x 2 997 ssh_keys 23 8月 18 12:09 run #默认neo4j的PID存储目录 -rw-r--r-- 1 997 ssh_keys 92 8月 9 2022 UPGRADE.txt
-
配置环境变量
#编辑全局环境变量配置文件 [root@qianfeng01 neo4j-enterprise-3.5.35]# vim /etc/profile #追加如下内容 export NEO4J_HOME=/usr/local/neo4j-enterprise-3.5.35 export PATH=$PATH:$NEO4J_HOME/bin:#使环境变量生效 [root@qianfeng01 neo4j-enterprise-3.5.35]# source /etc/profile#测试环境变量 [root@qianfeng01 neo4j-enterprise-3.5.35]# which neo4j /usr/local/neo4j-enterprise-3.5.35/bin/neo4j
-
Neo4j配置
编辑安装目录下的./conf/neo4j.conf文件,并修改如下内容:
[root@qianfeng01 neo4j-enterprise-3.5.35]# vim ./conf/neo4j.conf#图数据库授权是否开启,表示连接neo4j数据库是否需要密码 dbms.security.auth_enabled=false#监听地址 dbms.connectors.default_listen_address=192.168.10.101# Bolt connector dbms.connector.bolt.enabled=true dbms.connector.bolt.tls_level=OPTIONAL dbms.connector.bolt.listen_address=:7687# HTTP Connector. There can be zero or one HTTP connectors. dbms.connector.http.enabled=true dbms.connector.http.listen_address=:7474# HTTPS Connector. There can be zero or one HTTPS connectors. dbms.connector.https.enabled=true dbms.connector.https.listen_address=:7473
-
启动Neo4j
[root@qianfeng01 neo4j-enterprise-3.5.35]# neo4j start Active database: graph.db Directories in use:home: /usr/local/neo4j-enterprise-3.5.35config: /usr/local/neo4j-enterprise-3.5.35/conflogs: /usr/local/neo4j-enterprise-3.5.35/logsplugins: /usr/local/neo4j-enterprise-3.5.35/pluginsimport: /usr/local/neo4j-enterprise-3.5.35/importdata: /usr/local/neo4j-enterprise-3.5.35/datacertificates: /usr/local/neo4j-enterprise-3.5.35/certificatesrun: /usr/local/neo4j-enterprise-3.5.35/run Starting Neo4j. Started neo4j (pid 27443). It is available at http://192.168.10.101:7474/ There may be a short delay until the server is ready. See /usr/local/neo4j-enterprise-3.5.35/logs/neo4j.log for current status.
注意:
-
UseG1GC错误如下
Error: VM option 'UseG1GC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions. Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.#解决方法,过滤含有"UseG1GC"文件,并删除或者注释对应内容 [root@qianfeng01 neo4j-enterprise-3.5.35]# grep -r -i "UseG1GC" ./ #dbms.jvm.additional=-XX:+UseG1GC
-
-
查看Neo4j服务
[root@qianfeng01 neo4j-enterprise-3.5.35]# jps 30680 CommercialEntryPoint 2814 Jps
-
测试Neo4j的WebUI
访问地址为:http://ip:7474 , 我的地址是:http://192.168.10.101:7474 ,如果启动密码,则默认的用户名和密码均是neo4j
点击Connect,如下图所示:
到此为止,Web UI连接成功。
2.2.2 Windows安装Neo4j
-
Windows控制台启动应用程序
- 选择下载Neo4j合适的ZIP压缩包,比如:
neo4j-enterprise-3.5.35-windows.zip
- 解压对应的压缩包到某个Windows的目录即可
- 进入Neo4j的安装目录
- 执行安装目录下执行命令:
bin\neo4j.bat console
,该方式为前台启动。 - 在控制台中输入Ctrl-C停止服务。
- 选择下载Neo4j合适的ZIP压缩包,比如:
-
制作Neo4j的Window服务
Neo4j也可以作为Windows服务运行。使用bin\neo4j.bat Install -service安装该服务,并使用bin\neo4j.bat start启动它。
-
选择下载Neo4j合适的ZIP压缩包,比如:
neo4j-enterprise-3.5.35-windows.zip
-
解压对应的压缩包到某个Windows的目录即可
-
进入Neo4j的安装目录
-
安装服务,即注册其Neo4j的Window服务,命令为:
bin\neo4j.bat install-service
-
如果发生配置修改等,可以更细服务,命令为:
bin\neo4j.bat update-service
-
重启Neo4j,命令如下:
bin\neo4j.bat restart
-
使用浏览器访问并测试是否可用。
-
2.2.3 Docker安装
Docker可以安装单机,也可以安装集群,总之安装方式都比较简单直接。Docker的安装省略,下面是docker容器下Neo4j的单机版安装部署。
该映像提供的默认配置用于学习Neo4j,但必须对其进行修改以使其适合生产使用。特别是,Neo4j的默认内存分配非常有限(NEO4J_dbms_memory_pagecache_size=512M和NEO4J_dbms_memory_heap_max__size=512M),以允许多个容器在同一台服务器上运行。
有三种方法可以修改配置:
-
设置环境变量。在运行容器时将环境变量传递给容器。示例如下:
docker run \--detach \--publish=7474:7474 --publish=7687:7687 \--volume=$HOME/neo4j/data:/data \--volume=$HOME/neo4j/logs:/logs \--env NEO4J_dbms_memory_pagecache_size=4G \neo4j:3.5
任何配置值都可以通过以下命名方案传递:
-
前缀为NEO4J_。
-
下划线必须写两次:写成_。
-
点号被转换为下划线: . 写成_。
例如,dbms.tx_log.rotation。size可以通过向Docker指定以下参数来设置:
--env NEO4J_dbms_tx__log_rotation_size
-
-
挂载/conf卷。
要对Neo4j配置进行任意修改,请向容器提供一个/conf卷。
docker run \--publish=7474:7474 --publish=7687:7687 \--volume=$HOME/neo4j/data:/data \--volume=$HOME/neo4j/logs:/logs \--volume=$HOME/neo4j/conf:/conf \neo4j:3.5
-
建立一个新的形象。
对于更复杂的映像定制,您可以在此基础上创建一个新映像,也可以将该映像进行配置、上传等处理。
FROM neo4j:3.5
2.2.4 集群安装部署
2.2.4.1 集群介绍
Neo4j的(Causal Clustering)因果集群提供了三个主要特性:
- 安全性:核心服务器为事务处理提供了一个容错平台,当大多数核心服务器正常工作时,该平台仍然可用。
- 可伸缩:Read Replicas为图形查询提供了一个大规模可伸缩的平台,使非常大的图形工作负载能够在广泛分布的拓扑结构中执行。
- 因果一致性:当调用时,保证客户端应用程序至少读取它自己的写入。
总之,集群使得最终用户系统功能齐全,既可以读取数据,也可以写入数据。
从操作的角度来看,将集群视为由两个不同的角色组成是很有用的:Core和Read Replica。如下图所示:
这两个角色在任何生产部署中都是基础的,但它们的管理规模不同,在管理整个集群的容错性和可伸缩性方面承担不同的角色。
Core Servers
核心服务器的主要职责是保护数据。核心服务器通过使用Raft协议复制所有事务来实现这一点。Raft在确认事务提交给最终用户应用程序之前确保数据安全持久。在实践中,这意味着一旦集群中的大多数核心服务器(N/2 +1
)接受了事务,就可以安全地向最终用户应用程序确认提交。
安全要求对写延迟有影响。隐式写入将被最快的多数服务器确认,但随着集群中核心服务器数量的增长,确认写入所需的多数服务器的大小也会增加。
在实践中,这意味着在典型的Core Server集群中有相对较少的机器,足以为特定的部署提供足够的容错能力。这是用公式M = 2F + 1
计算的,其中M是容忍F个故障所需的核心服务器的数量。例如:
-
为了容忍2个核心服务器出现故障,我们需要部署一个由5个核心组成的集群。
-
最小的容错集群,即可以容错1个故障的集群,必须有3个core。
-
也可以创建仅由2个核心组成的因果集群。然而,该集群不会容错;如果2个服务器中的一个发生故障,其余服务器将变为只读。
注意:
如果Core Server集群遭受了足够多的故障,无法再处理写操作,它将变为只读以保证安全性。
Read Replicas
Read Replicas的主要职责是扩展图工作负载(Cypher查询、过程等)。Read Replicas就像核心服务器保护的数据的缓存,但它们不是简单的键值缓存。事实上,Read Replicas是完全成熟的Neo4j数据库,能够实现任意(只读)图查询和过程。
读取副本通过事务日志传送从核心服务器异步复制。Read Replica将定期(通常在ms范围内)轮询核心服务器,以查询自上次轮询以来处理的任何新事务,核心服务器将这些事务发送给Read Replica。许多Read Replicas可以从相对较少的核心服务器中获取数据,从而允许大量的查询工作负载进行扩展。
然而,与核心服务器不同,Read Replicas不参与集群拓扑的决策制定。Read Replicas通常应该以相对较大的数量运行,并被视为一次性的。丢失一个Read Replica并不会影响集群的可用性,只是会损失一部分图查询吞吐量。它不会影响集群的容错能力。
Causal consistency
因果一致性使得写入核心服务器(其中数据是安全的)并从read Replica(其中图形操作向外扩展)读取这些写入成为可能。例如,因果一致性保证创建用户帐户的写操作在该用户随后尝试登录时仍然存在。
2.2.4.2 集群规划
主机名 | ip | 服务 | 备注 |
---|---|---|---|
qianfeng01 | 192.168.10.101 | CORE | 12G/2C |
qianfeng02 | 192.168.10.102 | CORE | 4G/2C |
qianfeng03 | 192.168.10.103 | CORE | 4G/2C |
2.2.4.3 集群核心配置
集群主要配置如下:
配置项 | 描述 |
---|---|
dbms.connectors.default_listen_address | 本机用来侦听传入消息的地址或网络接口。将此值设置为“0.0.0.0”允许Neo4j绑定到任何可用的网络接口。 |
dbms.connectors.default_advertised_address | 其他机器被告知要连接的地址。通常情况下,应该将其设置为完全限定的域名或该服务器的IP地址。 |
dbms.mode | 单个数据库实例的运行模式。对于因果集群,有两种可能的模式:CORE 或READ_REPLICA 。 |
causal_clustering.minimum_core_cluster_size_at_formation | 集群形成时核心机器的最小数量。如果没有此设置定义的内核数量,集群将无法形成,并且通常应该将其配置为完整和固定的数量。 |
causal_clustering.minimum_core_cluster_size_at_runtime | 将存在于共识组中的核心实例的最小数量。 |
causal_clustering.initial_discovery_members | 可用于引导此Core或Read Replica实例的一组初始Core集群成员的网络地址。在默认情况下,初始发现成员以逗号分隔的地址/端口对列表的形式给出,并且发现服务的默认端口为:5000 。最好在所有核心服务器上将此参数设置为相同的值。 |
2.4.4.4 集群部署
-
上传安装包到服务器
使用任何工具或任何方式均可,只要能将安装包放置到服务器中即可。
╰$ scp /Users/liyadong/Desktop/neo4j-enterprise-3.5.35-unix.tar.gz qianfeng01:/home
-
解压Neo4j
#解压到/usr/local目录 [root@qianfeng01 neo4j-enterprise-3.5.35]# tar -zxvf /home/neo4j-enterprise-3.5.35-unix.tar.gz -C /usr/local/#进入neo4j目录 [root@qianfeng01 neo4j-enterprise-3.5.35]# cd /usr/local/neo4j-enterprise-3.5.35
-
Neo4j配置
编辑安装目录下的./conf/neo4j.conf文件,并修改如下内容:
[root@qianfeng01 neo4j-enterprise-3.5.35]# vim ./conf/neo4j.conf#***************************************************************** # Network connector configuration #***************************************************************** dbms.connectors.default_listen_address=0.0.0.0 dbms.connectors.default_advertised_address=qianfeng01#***************************************************************** # Causal Clustering Configuration #***************************************************************** dbms.mode=CORE causal_clustering.minimum_core_cluster_size_at_formation=3 causal_clustering.minimum_core_cluster_size_at_runtime=3 causal_clustering.initial_discovery_members=qianfeng01:5000,qianfeng02:5000,qianfeng03:5000
-
分发Neo4j安装目录到其他服务器
[root@qianfeng01 neo4j-enterprise-3.5.35]# scp -r /usr/local/neo4j-enterprise-3.5.35 qianfeng02:/usr/local/ [root@qianfeng01 neo4j-enterprise-3.5.35]# scp -r /usr/local/neo4j-enterprise-3.5.35 qianfeng03:/usr/local/
-
修改刚分发的Neo4j配置
主要是修改其他服务器的,这儿主要是qianfeng02和qianfeng03服务器的配置。修改的内容都相似,将
dbms.connectors.default_advertised_address
修改成对应的主机名或IP或域名即可。[root@qianfeng02 neo4j-enterprise-3.5.35]# vim ./conf/neo4j.conf dbms.connectors.default_advertised_address=qianfeng02[root@qianfeng03 neo4j-enterprise-3.5.35]# vim ./conf/neo4j.conf dbms.connectors.default_advertised_address=qianfeng03
-
配置环境变量(
规划中的每台服务器都要执行
)#编辑全局环境变量配置文件 [root@qianfeng01 neo4j-enterprise-3.5.35]# vim /etc/profile #追加如下内容 export NEO4J_HOME=/usr/local/neo4j-enterprise-3.5.35 export PATH=$PATH:$NEO4J_HOME/bin:#使环境变量生效 [root@qianfeng01 neo4j-enterprise-3.5.35]# source /etc/profile#测试环境变量 [root@qianfeng01 neo4j-enterprise-3.5.35]# which neo4j /usr/local/neo4j-enterprise-3.5.35/bin/neo4j
-
启动Neo4j
每台服务器都需要独立启动Neo4j,启动顺序无关紧要。
[root@qianfeng01 neo4j-enterprise-3.5.35]# neo4j start[root@qianfeng02 neo4j-enterprise-3.5.35]# neo4j start[root@qianfeng03 neo4j-enterprise-3.5.35]# neo4j start
注意:
-
UseG1GC错误如下
Error: VM option 'UseG1GC' is experimental and must be enabled via -XX:+UnlockExperimentalVMOptions. Error: Could not create the Java Virtual Machine. Error: A fatal exception has occurred. Program will exit.#解决方法,过滤含有"UseG1GC"文件,并删除或者注释对应内容 [root@qianfeng01 neo4j-enterprise-3.5.35]# grep -r -i "UseG1GC" ./ #dbms.jvm.additional=-XX:+UseG1GC
-
-
查看Neo4j服务
[root@qianfeng01 neo4j-enterprise-3.5.35]# jps 14535 CommercialEntryPoint 15067 Jps[root@qianfeng02 neo4j-enterprise-3.5.35]# jps 27808 Jps 27743 CommercialEntryPoint[root@qianfeng03 neo4j-enterprise-3.5.35]# jps 5178 CommercialEntryPoint 5231 Jps
-
查看集群信息
neo4j如果没有对用户和密码做配置,默认用户名和密码都是neo4j。连接成功后将会修改密码。
地址:http://192.168.10.101:7474/browser/
地址:http://192.168.10.102:7474/browser/
地址:http://192.168.10.103:7474/browser/
点击登录后修改密码:
连接成功如下:
在连接成功之后,我们可以连接到任何实例并运行CALL dbms.cluster.overview()来检查集群的状态。这将显示关于集群中每个成员的信息。具体如下图所示:
到此为止,我们的Neo4j集群安装部署成功。
2.4.4.5 添加Core Server
新服务器将与现有集群集成,一旦从同级服务器复制了数据,就可以使用。如果现有集群包含大量数据,则新实例执行复制可能需要一些时间。
如果新服务器打算成为集群的永久成员,最好的做法是更新causal_cluster。集群中所有服务器上的Initial_discovery_members,以包含新服务器。
编辑Neo4j安装目录下的./conf/neo4j.conf文件,并修改如下内容:
[root@qianfeng01 neo4j-enterprise-3.5.35]# vim ./conf/neo4j.conf#*****************************************************************
# Network connector configuration
#*****************************************************************
dbms.connectors.default_listen_address=0.0.0.0
dbms.connectors.default_advertised_address=qianfeng04#*****************************************************************
# Causal Clustering Configuration
#*****************************************************************
dbms.mode=CORE
causal_clustering.minimum_core_cluster_size_at_formation=3
causal_clustering.minimum_core_cluster_size_at_runtime=3
causal_clustering.initial_discovery_members=qianfeng01:5000,qianfeng02:5000,qianfeng03:5000
注意:
- 配置与前面的服务器非常相似。在本例中,新服务器不打算成为集群的永久成员,因此它不包含在causal_cluster .initial_discovery_members中。
- 配置好后可以启动新的核心服务器,并让它将自己添加到现有的集群中。
2.4.4.6 添加Read Replica到集群
通过neo4j.conf提供了与核心服务器类似的初始读副本配置。由于读副本不参与集群仲裁决策,因此它们的配置更短;他们只需要知道一些核心服务器的地址,他们可以绑定到这些服务器,以便发现集群。然后,他们可以选择一个合适的核心服务器,从中复制数据。
编辑Neo4j安装目录下的./conf/neo4j.conf文件,并修改如下内容:
dbms.mode=READ_REPLICA
causal_clustering.initial_discovery_members=qianfeng01:5000,qianfeng02:5000,qianfeng03:5000
2.4.4.7 移除核心服务
可以使用neo4j-admin unbind
命令将核心服务器降级为独立实例。
一旦服务器从集群中解绑定,存储文件就相当于Neo4j独立实例。从这里开始,这些文件可以通过在SINGLE模式下重新启动来运行独立实例。
核心服务器实例的磁盘状态与独立服务器实例的磁盘状态不同。重要的是要理解,一旦实例从集群解除绑定,它就不能与该集群重新集成。这是因为集群和单个实例现在都是独立的数据库,对它们应用了不同且不可调和的写操作。从技术上讲,集群将有写入条目到其Raft日志,而独立实例将直接写入事务日志(因为在独立实例中没有Raft日志)。
如果我们尝试将独立实例重新插入到集群中,那么日志和存储将不匹配。运营商不应抱着将数据复制的乐观希望,试图将独立的数据库合并到集群中。这种情况不会发生,而且很可能导致不可预测的集群行为。
第三章 Neo4j操作
3.1 Cypher介绍
Cypher 是 Neo4j 的一种声明式图形查询语言,允许用户从图形数据库中高效的存储和检索数据。它是一种声明式的、受 SQL 启发的语言。语法提供了一种视觉和逻辑方式来匹配图中节点和关系的模式。Cypher 旨在让每个人都易于学习、理解和使用,而且还融合了其他标准数据访问语言的强大功能。
它的设计适合于开发人员和操作专业人员。Cypher的设计简单,但功能强大;高度复杂的数据库查询可以很容易地表达,使您能够专注于业务,而不是迷失在数据库访问中。
Cypher借用了SQL的结构——使用不同的子句构建查询。子句被链接在一起,它们在彼此之间提供中间结果集。例如,一个MATCH子句中的匹配变量将是下一个子句存在的上下文。查询语言由几个不同的子句组成。常用的Cypher命令如下:
常用的Neo4j CQL命令如下:
CQL命令 | 用法 |
---|---|
CREATE | 创建节点,关系和属性 |
MATCH | 检索有关节点,关系和属性数据 |
RETURN | 返回查询结果 |
WHERE | 提供条件过滤检索数据 |
DELETE | 删除节点和关系 |
REMOVE | 删除节点和关系的属性 |
ORDER BY | 排序检索数据 |
SET | 添加或更新标签 |
很多命令都是组合使用,而不是独立的字句,部分字句能独立应用,一部分也不能独立应用。例如,下面就是查询命令的组合:
- MATCH:要匹配的图形模式。这是从图中获取数据的最常用方法。
- WHERE:不是独立的子句,而是MATCH、OPTIONAL MATCH和WITH的一部分。向模式添加约束,或过滤通过WITH传递的中间结果。
- RETURN:返回结果。
3.2 语法
-
节点语法:
Cypher 使用一对括号来表示一个节点:
()
. 这让人想起带有圆形端盖的圆形或矩形。下面是一些节点示例,提供了不同类型和变量的细节:() (matrix) (:Movie) (matrix:Movie) (matrix:Movie {title: 'zhangsan'}) (matrix:Movie {title: 'zhangsan', released: 2007})
()
代表一个匿名的、无特征的节点。(matrix)
其中matrix为添加的变量,变量作用范围仅限于单个语句(单个语句可被调用)。它在另一个陈述中可能具有不同的含义或没有含义。:Movie
模式声明了节点的标签。这允许我们使用限制模式。title
或released
,节点的属性表示为键值对列表,括在一对大括号内,例如:{name: 'goudan'}
。属性可用于存储信息和/或限制模式。 -
关系语法
Cypher 使用一对破折号 (
--
) 表示无向关系;定向关系的一端有一个箭头 (<--
,-->
);括号表达式 ([...]
) 可用于添加详细信息,这可能包括变量、属性和类型信息:-- --> -[role]-> -[:ACTED_IN]-> -[role:ACTED_IN]-> -[role:ACTED_IN {roles: ['Neo']}]->
关系的括号对中的语法和语义与节点括号之间使用的语法和语义非常相似。
role
可以定义一个变量(例如,role),以便在语句的其他地方使用。关系的类型(例如,:ACTED_IN
)类似于节点的标签。属性(例如,roles
)完全等同于节点属性。 -
模式语法
结合节点和关系的语法,我们可以表达模式。以下可能是该领域中的一个简单模式(或事实):
(keanu:Person:Actor {name: 'zhangsan'})-[role:ACTED_IN {roles: ['Neo']}]->(matrix:Movie {title: 'goudan'})
Actor相当于节点标签,
:ACTED_IN
模式声明了关系的关系类型。变量(例如,role
)可以在语句的其他地方使用来指代关系。如同节点属性,关系属性被表示为一对大括号括起来,例如键/值对的列表:
{roles: ['Neo']}
。在这种情况下,我们为 使用了一个数组属性roles
,允许指定多个角色。属性可用于存储信息和/或限制模式。 -
模式变量
为了增加模块化并减少重复,Cypher 允许将模式分配给变量。这允许检查匹配路径,用于其他表达式等。
acted_in = (:Person)-[:ACTED_IN]->(:Movie)
该
acted_in
变量将包含两个节点以及找到或创建的每条路径的连接关系。有多项功能的路径,例如访问细节:nodes(path)
,relationships(path)
,和length(path)
。 -
规则
Cypher 语句通常有多个子句,每个子句执行一个特定的任务,例如:
- 在图中创建和匹配模式
- 过滤、投影、排序或分页结果
- 撰写部分陈述
通过组合 Cypher 子句,我们可以组合更复杂的语句来表达我们想要知道或创建的内容。
3.3 图数据库操作
3.3.1 CREATE命令
-
简单创建
// CREATE命令语法: CREATE (<node-name>:<label-name>)
创建()空节点。
create ()
创建“Employee”节点。节点名:emp 标签:Employee
CREATE (emp:Employee)
创建“Dept”节点。节点名:dept 标签:Dept
CREATE (dept:Dept)
创建“Dept”节点。节点名:dept1, 标签:Dept,设置depId属性
CREATE (dept1:Dept) SET dept1.depId=1
-
复杂创建
// 语法 CREATE (<node-name>:<label-name>{ <Property1-name>:<Property1-Value>........<Propertyn-name>:<Propertyn-Value>} )
注意:
Property1-name不能使用单引号或者双引号包裹。
创建具有一些属性(deptno,dname,location)的Dept节点。
CREATE (dept:Dept { deptno:10,dname:"BigData",location:"beijing" })
创建具有一些属性(id,name,sal,deptno)的Employee节点。
CREATE (emp:Employee{id:123,name:"goudan",sal:35000,deptno:10})
为“Cinema”节点创建多个标签名称。
CREATE (m:Movie:Cinema:Film:Picture)
创建多节点、标签、属性等。
CREATE (john:Person {name: 'John'}) CREATE (joe:Person {name: 'Joe'}) CREATE (steve:Person {name: 'Steve'}) CREATE (sara:Person {name: 'Sara'}) CREATE (maria:Person {name: 'Maria'}) CREATE (john)-[:FRIEND]->(joe)-[:FRIEND]->(steve) CREATE (john)-[:FRIEND]->(sara)-[:FRIEND]->(maria)
3.3.2 CREATE创建关系
// 语法
CREATE (<node1-name>:<label1-name>)-[(<relationship-name>:<relationship-label-name>)]->(<node2-name>:<label2-name>)
创建p1->p2的关系。
CREATE (p1:Profile1)-[r1:LIKES]->(p2:Profile2)
创建具有节点、节点属性、关系、关系属性、变量。
CREATE (a:Person {name: 'Tom Hanks', born: 1956})-[r:ACTED_IN {roles: ['Forrest']}]->(m:Movie {title: 'Forrest Gump', released: 1994})
CREATE (d:Person {name: 'Robert Zemeckis', born: 1951})-[:DIRECTED]->(m)
先创建节点,再创建关系
CREATE (a:Person{name:'Tom'}), (b:Person{name:'Jerry'})
CREATE (a)-[r:Like]->(b)
3.3.3 MATCH查询语句
CQL MATCH 命令用于从数据库获取有关节点和属性的数据;从数据库获取有关节点,关系和属性的数据等。
// 语法
MATCH
(<node-name>:<label-name>
)
注意:
- 不能单独使用 MATCH 命令从数据库检索数据,否则会报错。
- MATCH通常需要和return等其他语句搭配使用。
例如MATCH (dept:Dept)将返回如下错误:
匹配dept并将其返回
MATCH (dept:Dept) return dept
查找名为“John”的用户和“John”的朋友(尽管不是他的直接朋友),然后返回“John”和找到的任何朋友的朋友。
MATCH (john {name: 'John'})-[:FRIEND]->()-[:FRIEND]->(fof)
RETURN john.name, fof.name
3.3.4 RETURN语句
Neo4j CQL RETURN子句主要用于:
- 检索节点的某些属性
- 检索节点的所有属性
- 检索节点和关联关系的某些属性
- 检索节点和关联关系的所有属性
// 语法
RETURN <node-name>.<property1-name>,........<node-name>.<propertyn-name>
注意:
- 不能单独使用RETURN子句。我们应该结合MATCH使用或CREATE命令。
- 返回单个属性时,不能以图方式展示。
检索结点某个属性
MATCH(dept:Dept) RETURN dept.dname
查询节点多个属性
MATCH(dept:Dept) RETURN dept.dname,dept.deptno
创建节点时并返回对应的节点
create (stu:Student {name:"goudan",age:18,isWedding:false})
return stu
查询所有节点
match (a) return a
匹配所有节点,并且关系为:FRIEND的图
MATCH (a) -[:FRIEND]->()
RETURN a
匹配所有节点和所有关系的节点
MATCH (a)-[r]->()
RETURN a
3.3.5 LOAD CSV
-
准备employee.csv到$NEO4J_HOME/import/employee.csv目录下:
"age","depId","gender","name","salary" 25,1,"male","Leo",20000.126 25,1,"male","Leo",20000.126 30,2,"female","Marry",25000.0 35,1,"male","Jack",15000.0 42,3,"male","Tom",18000.0 30,2,"female","Marry",25000.0 21,3,"female","Kattie",21000.0 19,2,"male","Jen",8000.0 30,2,"female","Jen",28000.0 42,3,"male","Tom",18000.0 35,1,"male","Jack",15000.0 18,3,"female","XiaoFang",58000.0 42,3,"male","Tom",18000.0 21,3,"female","Kattie",21000.0 19,2,"male","Jen",8000.0 30,2,"female","Jen",28000.0 42,3,"male","Tom",18000.0 18,3,"female","XiaoFang",58000.0 25,1,"male","Leo",20000.126 30,2,"female","Marry",25000.0 35,1,"male","Jack",15000.0 42,3,"male","Tom",18000.0 21,3,"female","Kattie",21000.0 19,2,"male","Jen",8000.0 30,2,"female","Jen",28000.0 42,3,"male","Tom",18000.0 18,3,"female","XiaoFang",58000.0 25,1,"male","Leo",20000.126 30,2,"female","Marry",25000.0 35,1,"male","Jack",15000.0 42,3,"male","Tom",18000.0 21,3,"female","Kattie",21000.0 19,2,"male","Jen",8000.0 30,2,"female","Jen",28000.0 42,3,"male","Tom",18000.0 18,3,"female","XiaoFang",58000.0
-
CQL导入
LOAD CSV WITH HEADERS FROM 'file:///employee.csv' AS line FIELDTERMINATOR ',' CREATE (:Employee { age:toInteger(line.age) ,depId: toInteger(line.depId), gender: line.gender,name:line.name,salary:toFloat(line.salary)})
-
查询
MATCH (n:Employee) RETURN n.name,n.age,n.depId,n.gender,n.salary LIMIT 5
3.3.6 WHERE条件语句
像SQL一样,Neo4j CQL在CQL MATCH命令中提供了WHERE子句来过滤MATCH查询的结果。
-
简单过滤
// 语法 WHERE <condition>
-
复杂过滤
// 语法 WHERE <condition> <boolean-operator> <condition>
Neo4j支持以下布尔运算符在Neo4j CQL WHERE子句中使用以支持多个条件。
布尔运算符 含义 备注 AND 同时满足条件 OR 满足其中一个条件 NOT XOR Neo4j 支持以下的比较运算符,在 Neo4j CQL WHERE 子句中使用来支持条件。
布尔运算符 含义 备注 = 等于 <> 不等 < 小于 > 大于 <= 小于等于 >= 大于等于 Neo4j还支持使用正则表达式=~ 'regexp’查询。例如,查询name以J开头的用户:match (n) where n.name = ~‘J.*’ return n 。
查询男性雇员
MATCH (n:Employee) WHERE n.gender='male' RETURN n.name,n.age,n.depId,n.gender,n.salary LIMIT 5
查询男性并且薪水高于15000的雇员
MATCH (n:Employee)
WHERE n.gender='male' AND n.salary>15000
RETURN n.name,n.age,n.depId,n.gender,n.salary
LIMIT 5
查询男性并且薪水高于15000的雇员,不含重复数据,且按薪水排序
MATCH (n:Employee)
WHERE n.gender='male' AND n.salary>15000
RETURN DISTINCT n.name,n.age,n.depId,n.gender,n.salary
ORDER BY n.salary
使用匹配正则表达式=~ ‘regexp’, 查询以Le开头的名称
match (e:Employee)
where e.name=~"Le.*"
return e
多个节点关联查询
// 先创建节点
create (stu1:Student{id:1,name:"ls",age:12})
create (stuInfo1:StudentInfo{id:1,gender:"Famle",addr:"beijing"})
// 关联查询
match (stu:Student),(info:StudentInfo) where stu.id = info.id return stu,info
where创建关系
// 语法
MATCH (<node1-label-name>:<node1-name>),(<node2-label-name>:<node2-name>)
WHERE <condition>
CREATE (<node1-label-name>)-[<relationship-label-name>:<relationship-name>{<relationship-properties>}]->(<node2-label-name>) // 对查询出来的数据创建关系
match (stu:Student),(info:StudentInfo) where stu.id = info.id
create (stu)-[r:属于{createDate:"2023-8-18"}]->(info)
return stu,info
3.3.7 IN操作
与SQL一样,Neo4j CQL提供了一个IN运算符,以便为CQL命令提供值的集合。
// 语法
IN[<Collection-of-values>]
查询年龄为18,19,30的员工
match (e:Employee) where e.age in [18,19,30] return e
3.3.8 SET子句
有时,根据我们的客户端要求,我们需要向现有节点或关系添加新属性。要实现此功能,Neo4j CQL 提供了一个SET子句。SET 子
- 向现有节点或关系添加新属性
- 添加或更新属性值
// 语法
SET <property-name-list>
注意:
- SET通常需要和MATCH搭配使用
- 它没有创建新节点的功能,MATCH查询不出来节点,则不能更新或者添加属性。
更新多个属性
match (s:Student)
where s.id=1
set s.age=16,s.name="lisi"
return s
添加新属性
match (s:Student)
set s.location="beijing"
return s
3.3.9 ORDER BY排序
Neo4j CQL在MATCH命令中提供了“ORDER BY”子句,对MATCH查询返回的结果进行排序,可以按升序或降序对行进行排序。
默认情况下,它按升序对行进行排序。 如果我们要按降序对它们进行排序,我们需要使用DESC子句。
// 语法
ORDER BY <property-name-list> [ASC|DESC]
查询学生,并按照年龄降序排序
match (s:Student)
return s
order by s.age desc
3.3.10 LIMIT && SKIP子句
Neo4j CQL已提供“LIMIT”子句来过滤或限制查询返回的行数。 它修剪CQL查询结果集底部的结果。
如果我们要修整CQL查询结果集顶部的结果,那么我们应该使用CQL SKIP子句。
// 语法
...
return x
[skip m]
[limit n]
单独limit使用
match (s:Student)
return s
limit 1
单独使用skip
match (s:Student) return s skip 1
使用skip结合limit,达到分页效果
match (s:Student) return s skip 1 limit 1
3.3.11 UNION && UNION ALL
与SQL一样,Neo4j CQL有两个子句,将两个不同的结果合并成一组结果
-
UNION
合并两个结果集或多个结果集到一个结果集。
合并时将去重。
要求结果列类型和来自两组结果的名称必须匹配,这意味着列名称应该相同,列的数据类型应该相同。
// 语法 <MATCH Command1>UNION <MATCH Command2>
合并employee和student中的name和age属性,并去重
match (e:Employee) return e.name as name,e.age as age union match (s:Student) return s.name as name,s.age as age
-
UNION ALL
除了合并时不去重之外,其他的和UNION是一摸一样。
// 语法 <MATCH Command1> UNION ALL <MATCH Command2>
合并employee和student中的name和age属性,不去重
match (e:Employee) return e.name as name,e.age as age union match (s:Student) return s.name as name,s.age as age
注意:
如果这两个查询不返回相同的列名和数据类型,那么它抛出一个错误。可以使用as 来处理不同的别名。
3.3.12 MERGE合并
Neo4j使用CQL MERGE命令主要作用:
- 创建节点,关系和属性
- 为从数据库检索数据
MERGE命令是CREATE命令和MATCH命令的组合:MERGE = CREATE + MATCH
// 语法
MERGE (<node-name>:<label-name>
{<Property1-name>:<Pro<rty1-Value>.....<Propertyn-name>:<Propertyn-Value>
})
合并Student用户
merge (s:Student{id:2,name:"cuihua"})
合并Student用户
merge (s:Student{id:2,name:"cuihua"})
注意:
- Neo4j CQL MERGE命令语法与CQL CREATE命令类似。
- 如果Node不存在,则merge就新建,如果存在就不处理。
- 如果Node存在,但属性不存在,则是创建新的Node,并添加相应属性。
3.3.13 NULL值
Neo4j CQL将空值视为节点或关系的属性的缺失值或未定义值。
当我们创建一个节点标签名称但未指定其属性值的节点时,它将创建一个具有NULL属性值的新节点。
create (s:Student) return s
使用null值作为查询条件
match (s:Student) where s.name is not null return s
注意:
还可以用null 作为查询的条件,比如:where s.name is null 或者 where s.name is not null
3.3.14 DELETE语句
Neo4j使用CQL DELETE子句
- 删除节点。
- 删除节点及相关节点和关系。
// 语法
match ...
DELETE <node-name-list>
删除employee的所有节点
match (e:Employee) delete e
删除Profile1指向Profile2的所有关系,
MATCH (n:Profile1)-[r]->(m:Profile2)
where r is not null
return r
删除节点及其关系,直接使用DETACH DELETE命令即可删除节点和关系
MATCH (a:Person{name:'Tom'})-[r:Like]->(b:Person{name:'Jerry'})
DETACH DELETE a
批量删除某类型关系
MATCH (a)-[r:Friends]->(b) //找到所有的Friends类型的关系
where r is not null //也可以根据关系的其他属性来过滤
DELETE r
当需要删除大量关系时,使用单条DELETE命令会很慢,可以使用以下命令来批量删除:
MATCH (a)-[r:Friends]->(b)
WITH r LIMIT 10000 //with是传递结果集,限制每次处理10000条,这样就可以每次处理10000条,提高删除效率。
DELETE r
RETURN COUNT(r)
删除所有节点
match (n) delete n
注意:
- 不能直接使用delet语句,比如delete (s:Student)。
- 可以使用逗号 ,运算符来分隔节点名
- 有关系的节点不能删除,需要先把关系删除了才能删除节点。
3.3.15 REMOVE语句
Neo4j CQL REMOVE子句可以删除节点或关系的现有属性。
Neo4j CQL REMOVE命令用于
- 删除节点或关系的标签
- 删除节点或关系的属性
Neo4j CQL DELETE和REMOVE命令之间的主要区别:
- DELETE操作用于删除节点和关联关系。
- REMOVE操作用于删除标签和属性。
Neo4j CQL DELETE和REMOVE命令之间的相似性:
- 这两个命令不应单独使用。
- 两个命令都应该与MATCH命令一起使用。
删除节点属性
match (d:Dept) remove d.location return d
删除关系属性
// 创建节点和关系
create (s:Student{name:"stu"}) -[r:belown{createDate:"2023-8-18",creator:"zs"}]->(si:StudentInfo{name:"si"})
create (s:Student{name:"stu"}) -[r:belown{createDate:"2023-8-18",creator:"zs"}]->(si:StudentInfo{name:"si"})
// 删除对应属性
match (s:Student) -[r:belown] -> (si:StudentInfo) remove r.creator return r
删除满足条件的标签
match (d:Dept{depId:1}) remove d:Dept return d
删除所有标签
match (d:Dept) remove d:Dept return d
3.4 函数
3.4.1 函数介绍
与SQL类似,Cypher也支持函数,并且函数类型非常丰富,大多数的函数和SQL都很类似,Cypher函数分类具体如下:
- 判断函数
- 标量函数
- 聚合函数
- 列表函数
- 数学函数-数值|对数|三角函数
- 字符串函数
- 时间函数-即时类型|持续时间
- 空间函数
- 自定义函数
注意:
- 函数如果输入参数为null则返回null。
- 以字符串作为输入的函数都对Unicode字符进行操作,而不是对标准
char []
操作。例如,size()函数应用于任何Unicode字符将返回1,即使该字符不适合一个16位字符。
3.4.2 判定函数
对给定的输入返回true或false的布尔值,主要用于查询WHERE的部分过滤子图,所有这些函数都都可以放到where或return等语句中。
Function | Description | 语法 |
---|---|---|
all() | 判断一个断言是否适用于列表中的所有元素 | all(variable IN list WHERE predicate) |
any() | 判断是否一个断言至少适用于列表中的一个元素 | |
exists() | 如果数据库中存在该模式或节点存在该属性时,返回true | exists(pattern-or-property) |
none() | 断言不适合用于列表中的任何元素则返回true | |
single() | 断言刚好只适用于列表中的某一元素,则返回true |
案例:
// 断言所有节点都必须具有age属性,并且age属性值都必须大于30
MATCH p = (a)
WHERE a.name = 'Tom' and ALL(x IN nodes(p) WHERE x.age > 30)
RETURN p
// 判断存在name属性的节点,返回是否name和是否存在朋友
MATCH (n)
WHERE exists(n.name)
RETURN n.name AS name, exists((n)-[:FRIEND]->()) AS is_friend
3.4.3 标量函数
返回一个单值,通常是输入一行返回一行。
Function | Description | 语法 |
---|---|---|
coalesce() | 返回表达式列表中第一个非空的值,如果所有的实参都为空,则返回null | coalesce(expression [, expression]*) |
endNode() | 返回一个关系的结束节点 | endNode(relationship) |
head() | 返回列表中的第一个元素 | head(list) |
id() | 返回元素数据或关系的id标识 | |
last() | 返回列表中的最后一个元素 | last(expression) |
length() | 返回路径,字符串的长度 | |
properties() | 将实参转换成属性值的map,实参可以是节点或关系 | |
randomUUID() | 返回随机生成的UUID字符串值。 | |
size() | 返回列表中元素的个数 | size(list) |
size() applied to pattern expression | 返回匹配模式的个数 | |
size() applied to string | 返回字符串中Unicode字符的个数 | |
startNode() | 回一个关系的开始节点 | |
timestamp() | 返回当前时间戳,毫秒为单位。 | |
toBoolean() | 将字符串值转换为布尔值 | |
toFloat() | 将整数或字符串值转换为浮点数 | |
toInteger() | 将浮点值或字符串值转换为整数值 | |
type() | 返回关系的类型 |
案例:
// 返回name='Tom'的name和age属性中第一个非空的属性的值
MATCH (a)
where a.name = 'Tom'
RETURN coalesce(a.name, a.age)//查找name为'Tom Hanks'的关系的开始节点和结束节点
MATCH (n:Person{name:'Tom Hanks'})-[r]-(m)
RETURN startNode(r), endNode(r)//返回[:`属于`]关系的节点列表中的第一个数据和最后一个数据
match p=(a)-[:`属于`]->(b)
return nodes(p),head(nodes(p)),last(nodes(p))// 返回匹配到节点的所有属性名与属性值
CREATE (p:Person{name:'Stefan', city:'Berlin'})
RETURN properties(p)// 返回匹配到节点的所有属性名与属性值
CREATE (p:Person{name:'Tom', city:'depId'})
RETURN properties(p)// 返回模式表达式匹配到的查询结果集,计算的是结果集元素的个数
MATCH(a)
WHERE a.name = 'John'
RETURN size((a)-->()) AS rs// 返回匹配到路径的长度
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'John'
RETURN length(p)// 返回字符串的长度
MATCH (a)
WHERE length(a.name) > 8
RETURN a.name,length(a.name)// 将字符串转换为boolean类型,解析失败返回null
return toBoolean('abc')//转int和float类型,解析失败返回null
RETURN toInt('66'), toFloat('666')// 获取节点的id值
MATCH (a)
RETURN id(a)//获取节点关系类型
MATCH (n)-[r]->()
WHERE n.name = 'John'
RETURN type(r)
3.4.4 聚合函数
这些函数接受多个值作为参数,并从中计算和返回聚合值。
Function | Description |
---|---|
avg() - Numeric values | 返回一系列数据平均值 |
collect() | 返回包含表达式返回值的列表 |
count() | 返回行数 |
max() | 返回最大值 |
min() | |
sum() - Numeric values | 返回总和 |
案例:
//返回Employee的平均年龄、总行数、最大年龄、最小年龄、年龄总和
match (a:Employee)
return avg(a.age),count(a),max(a.age),min(a.age),sum(a.age)//返回Person的name列表
MATCH (n:Person)
RETURN collect(n.name)
3.4.5 列表函数
这些函数返回其他值的列表。
Function | Description | 语法 |
---|---|---|
extract() | 从节点或关系列表中返回单个属性或者某个函数的值,这将遍历整个列表,针对每一个元素运行表达式,并返回结果列表 | extract(variable IN list |expression) |
filter() | 返回列表中满足条件的所有元素 | filter(variable IN list WHERE predicate) |
keys() | 返回节点或关系或map上所有的key名 | keys(expression) |
labels() | 返回查询元素中所有的标签 | labels(node) |
nodes() | 返回查询路径中所有的节点 | nodes(path) |
range() | 返回一个范围内的数字,步长默认为1,包含起始的边界值 | range(start, end [, step]) |
reduce() | 对列表中的元素执行一个表达式并将结果存入累加器中 | reduce(accumulator = initial, variable IN list |expression) |
relationships() | 返回一条路径中所有的关系 | relationships(path) |
reverse() | 返回一个原始列表的倒序列表 | |
tail() | 返回除了首元素以外的所有元素 | tail(list) |
案例:
//返回关系中节点属性
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'John' and b.name = 'Joe' and c.name = 'Steve'
RETURN extract(n IN nodes(p)|n.name) AS extracted// 返回array属性元素字符数为3的列表
MATCH(a)
WHERE a.name = 'Jhon'
RETURN filter(x IN a.array WHERE size(x) = 3)// 将路径中的节点的keys、labels和关系属性返回
MATCH p = (a)-->(b)
RETURN keys(a),labels(b),relationships(p)// 将路径中所有节点的id都加起来返回一个单值
MATCH p = (a)-->(b)-->(c)
WHERE a.name = 'John' AND b.name = 'Joe' AND c.name = 'Steve'
RETURN reduce(count = 0, n IN nodes(p)|count + id(n)) AS reduction//返回range生成列表的倒序的除首位元素的所有元素
return tail(reverse(range(0,10,2)))
3.4.6 字符串函数
这些函数用于操作字符串或创建另一个值的字符串表示形式。
Function | Description |
---|---|
left() | 返回左半部分指定长度的字符串 |
lTrim() | 返回原字符串移除左侧的空白字符后的字符串 |
replace() | 字符串替换 |
reverse() | 返回原字符串的倒序字符串 |
right() | 返回右半部分指定长度的字符串 |
rTrim() | 返回移除原字符串右侧的空白字符字符串 |
split() | 返回以指定模式分隔后的字符串序列 |
substring() | 字符串子串,接受一个字符串,开始索引位置,长度 |
toLower() | 返回转小写的字符串 |
toString() | 将整数、浮点数、布尔值或时间类型(即日期、时间、LocalTime、DateTime、LocalDateTime或Duration)的值转换为字符串 |
toUpper() | 返回转大写的字符串 |
trim() | 返回移除左右两端的空白字符的字符串 |
3.4.7 数学函数
进行数学运算的函数
Function | Description |
---|---|
abs() | 返回数值的绝对值 |
ceil() | 向上取整 |
floor() | 向下取整 |
rand() | 返回[0,1)之间的一个随机数 |
round() | 返回给定数字四舍五入到最接近的整数的值 |
sign() | 返回一个数值的正负,正为1,复为-1,零则为0 |
其他还有三角函数,比如:sin()、cos()、asin()、acos()、pi()等
其他还有对数函数,比如:sqrt()、e()、exp()、log()、log10()
3.4.8 时间函数
时间类型(Date、Time、LocalTime、DateTime和LocalDateTime)的值可以使用以下函数创建和操作:
Function | Description |
---|---|
date() | 返回当前日期 |
[date({year , month, day]}) | 返回指定格式日期 |
[date({year , week, dayOfWeek]}) | 返回指定表达式中的周、周的第几天 |
[date({year , quarter, dayOfQuarter]}) | 返回指定格式的季度 |
date(string) | 字符串解析成date |
datetime() | 返回当前日期时间 |
除了上述的,还有很多的即时时间函数。比如以datetime()扩展、以localdatetime()扩展、time()扩展和localtime()等扩展。
除了即时时间,还有一类是持续时间,主要以duration()扩展,有固定的格式,比如表示都以P开头,具体如下:
3.4.9 空间函数
这些函数用于指定地理或笛卡尔坐标参考系中的2D或3D点,并计算两点之间的测地线距离。
Function | Description |
---|---|
distance() | 返回同一坐标系中任意两点之间的距离 |
point() - Cartesian 2D | 给定两个点坐标值,返回一个2维对象 |
point() - Cartesian 3D | 给定3个坐标值,返回3维坐标对象 |
point() - WGS 84 2D | 根据WGS 84地理坐标系中的2个坐标值,返回一个2D点对象 |
point() - WGS 84 3D | 根据WGS 84地理坐标系中的3个坐标值,返回一个3D点对象 |
3.4.10 用户自定义函数
用户定义函数用Java编写,部署到数据库中,并以与任何其他Cypher函数相同的方式调用。可以开发和使用的函数主要有两种类型:
Type | Description | Usage | Developing |
---|---|---|---|
Scalar | 一行返回一个值 | Using UDF | Extending Neo4j (UDF) |
Aggregating | 给多行,返回一行 | Using aggregating UDF | Extending Neo4j (Aggregating UDF) |
-
创建自定义标量函数
用户定义函数的创建方式与过程的创建方式类似,但使用@UserFunction进行注释,并且返回单个值,而不是返回值流。
package example;import org.junit.Rule; import org.junit.Test; import org.neo4j.driver.v1.*; import org.neo4j.harness.junit.Neo4jRule;import static org.hamcrest.core.IsEqual.equalTo; import static org.junit.Assert.assertThat;public class JoinTest {// This rule starts a Neo4j instance@Rulepublic Neo4jRule neo4j = new Neo4jRule()// This is the function to test.withFunction( Join.class );@Testpublic void shouldAllowIndexingAndFindingANode() throws Throwable{// This is in a try-block, to make sure you close the driver after the testtry( Driver driver = GraphDatabase.driver( neo4j.boltURI() , Config.build().withEncryptionLevel( Config.EncryptionLevel.NONE ).toConfig() ) ){// GivenSession session = driver.session();// WhenString result = session.run( "RETURN example.join(['Hello', 'World']) AS result").single().get("result").asString();// ThenassertThat( result, equalTo( "Hello,World" ) );}} }
-
调用自定义标量函数
用户定义函数的调用方式与任何其他Cypher函数相同。函数名必须是完全限定的,因此在包org.neo4j中定义的名为join的函数。可以使用以下方式调用示例:
MATCH (p: Person) WHERE p.age = 36 RETURN org.neo4j.examples.join(collect(p.names))
第四章 Neo4j Admin
4.1 备份与恢复
neo4j目前有三种备份方式:
- Java在线备份,通过Java程序可在neo4j启动状态下备份数据,也可远程备份(社区版本目前不支持)
- neo4j-admin工具,可在neo4j数据库关闭情况下本地备份
- neo4j-backup工具,可在neo4j启动状态下在线备份,可远程备份。
-
单机备份
采用以上备份之前,需要在conf/neo4j.conf中配置如下内容:
Parameter name Default value Description dbms.backup.enabled
true
启用在线备份支持 dbms.backup.address
127.0.0.1:6362-6372
在线备份监听地址 从指定服务器进行备份:
在此配置中,备份客户端在单独的服务器上运行。这是单机数据库的推荐配置。
运行备份客户端的服务器必须安装Neo4j,但不需要托管数据库。
强烈建议从不同于生产服务器的服务器上运行备份。
在本机服务器进行备份:
在此配置中,备份程序运行在承载Neo4j生产数据库的同一台服务器上。
当备份程序启动时,它将启动一个新的Java进程。如果有一个正在运行的Neo4j数据库,它将与Neo4j进程并行运行。在生产系统上,Neo4j通常被配置为最大限度地利用系统的可用RAM。如果在生产系统上运行备份,则整体性能可能会受到负面影响,在极端情况下可能会导致内存不足错误。
因此,强烈建议从与生产服务器不同的服务器上运行备份。如果不可能使用单独的备份服务器,则可以通过显式定义为备份进程分配多少内存来控制对生产系统的影响。
-
集群备份
采用以上备份之前,需要在conf/neo4j.conf中配置如下内容:
Parameter name Default value Description dbms.backup.enabled
true
dbms.backup.address
127.0.0.1:6362
dbms.backup.ssl_policy
SSL策略 通常建议选择Read Replicas作为备份提供者,因为在典型的集群部署中,它们比核心服务器要多。此外,大备份可能导致Read Replica出现性能问题,但不会影响核心集群的性能或冗余。
要在因果集群上执行备份,需要组合一些设置和参数。下表说明了使用追赶协议时的可用选项:
备份目标地址 服务端SSL策略设置 备份客户端对应SSL策略 默认端口 dbms.backup.address
dbms.backup.ssl_policy
dbms.backup.ssl_policy
6362
causal_clustering.transaction_listen_address
causal_clustering.ssl_policy
dbms.backup.ssl_policy
6000
下图使用
dbms.backup.ssl_policy
配置:下图使用
causal_clustering.ssl_policy
配置: -
备份操作
采用以上备份之前,需要在conf/neo4j.conf中配置如下内容:
dbms.backup.enabled=true 默认开启dbms.backup.address=<主机名或者ip>:6362 用于在线远程备份
neo4j-admin备份与恢复命令格式
neo4j-admin dump --database=<database> --to=<destination-path> neo4j-admin load --from=<archive-path> --database=<database> [--force]
neo4j-admin的操作,需要关闭数据库,不然会出现以下错误:
备份
neo4j stop neo4j-admin dump --database=graph.db --to=/usr/local/neo4j-backup/20230818.dump
恢复
neo4j-admin load --from=/usr/local/neo4j-backup/20230818.dump --database=graph.db
4.2 索引管理
数据库索引是数据库中某些数据的冗余副本,用于更有效地搜索相关数据。这是以额外的存储空间和较慢的写入为代价的,因此决定对哪些内容建立索引,哪些内容不建立索引是一项重要的任务,而且通常是非常重要的任务。
Cypher®允许在具有给定标签的所有节点的一个或多个属性上创建索引:
- 在任何给定标签的单个属性上创建的索引称为单属性索引。
- 为任何给定标签在多个属性上创建的索引称为复合索引。
- 全文索引。
一旦创建了索引,当图发生更改时,数据库将自动管理索引并使其保持最新状态。一旦索引被创建并上线,Neo4j将自动拾取并开始使用索引。
4.2.1 复合索引限制
与单属性索引不同,复合索引目前只支持相等性检查:n.prop = value和列表成员检查:n.prop IN list。不支持在索引属性上包含以下谓词类型的查询:
- existence check:
exists(n.prop)
- range search:
n.prop > value
- prefix search:
STARTS WITH
- suffix search:
ENDS WITH
- substring search:
CONTAINS
复合索引需要在索引的所有属性上使用谓词。如果仅在索引属性的一个子集上有谓词,则不可能使用复合索引。要获得这种索引行为,有必要在相关的属性子集或单个属性上创建额外的索引。
4.2.2 创建单值属性索引
可以使用CREATE index on: label (property)
为具有特定标签的所有节点在单个属性上创建索引。
CREATE INDEX ON :Person(name)
4.2.3 创建复合索引
对具有特定标签的所有节点的多个属性进行索引。复合索引——可以用CREATE index ON:Label(prop1,…,propN)
创建。只有带有指定标签且包含索引定义中所有属性的节点才会被添加到索引中。
CREATE INDEX ON :Person(name, born)
4.2.4 获取所有索引
调用内置过程db.indexes
将列出数据库中的所有索引。
call db.indexes
4.2.5 删除单值索引
可以使用DROP index on: label (property)
删除所有具有标签和单个属性组合的节点上的索引。
DROP INDEX ON :Person(name)
4.2.6 删除复合索引
可以使用DROP index on: label (prop1,…,propN)
删除所有具有标签和多个属性组合的节点上的复合索引。
DROP INDEX ON :Person(name, born)
4.2.7 索引的使用
要提供索引提示,请在适用的MATCH子句之后使用USING index variable:Label(属性)或USING index SEEK variable:Label(属性)
。
//直接查看计划(无索引基础上)
explain MATCH (person:Person { name: 'Tom Hanks' })
RETURN person// 创建索引
CREATE INDEX ON :Person(name)
//再有索引的基础上查看执行计划(代码不在运行)
explain MATCH (person:Person { name: 'Tom Hanks' })
RETURN person//再有索引的基础上查看执行计划(代码继续运行)
PROFILE
MATCH (p { name: 'Tom Hanks' })
RETURN p//使用using查看
MATCH (person:Person { name: 'Tom Hanks' })
USING index :Person(name)
RETURN person.name as name
4.3 约束管理
像SQL一样,Neo4j数据库也支持对NODE或Relationship的属性的UNIQUE约束。
Neo4j通过使用约束来保证数据完整性。约束可应用于节点或者关系。可以创建节点属性的唯一性约束,也可以创建节点和关系的属性存在性约束。
可以使用属性的存在性约束确保拥有特定标签的所有节点或者拥有特定类型的所有关系的属性是存在的。所有的试图创建新的没有该属性的节点或关系,以及试图删除强制属性的查询都将失败。
可以对某个给定的标签添加多个约束,也可以将唯一性约束和存在性约束同时添加到同一个属性上。
在属性上添加唯一性约束的时候,同时也会自动为该属性添加一个索引。因此,不能单独地添加这样一个索引。
UNIQUE约束的优点如下:
- 避免重复记录。
- 强制执行数据完整性规则。
注意:
只有Neo4j企业版才具有属性存在性约束这个高级功能。
4.3.1 创建节点唯一约束
Neo4j CQL已提供“CREATE CONSTRAINT”命令,以在NODE或关系的属性上创建唯一约束。具体语法如下:
CREATE CONSTRAINT ON (<label_name>)
ASSERT <property_name> IS UNIQUE
注意:
上述语法描述了它的<PROPERTY_NAME> <LABEL_NAME>节点或关系创造了一个独特的约束。
使用IS UNIQUE语法创建约束,它能确保数据库中拥有特定标签和属性值的节点是唯一的。
CREATE CONSTRAINT ON (book:Book)
ASSERT book.isbn IS UNIQUE
创建一个具有数据库中没有的isbn的Book节点。
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
创建数据库中已经存在的isbn的Book节点。这种情况下实惠报错说节点已经存在。
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
注意:
当数据库中已经有两个Book节点拥有相同的isbn号时,在Book节点的isbn属性上创建属性唯一性约束。这种情况下约束将创建失败,因为它与已有的数据冲突。可以选择创建索引或者移除冲突的节点然后再重新创建约束。
4.3.2 删除节点唯一属性
使用DROP CONSTRAINT可以删除数据库中的一个约束。
DROP CONSTRAINT ON (book:Book)
ASSERT book.isbn IS UNIQUE
4.3.2 创建节点属性存在性约束
使用ASSERT exists(variable.propertyName)
创建约束,可确保有指定标签的所有节点都有一个特定的属性。
CREATE CONSTRAINT ON (book:Book)
ASSERT exists(book.isbn)
创建一个存在isbn属性的Book节点。
CREATE (book:Book { isbn: '1449356265', title: 'Graph Databases' })
创建一个违背存在性约束的节点。在:Book(isbn)有存在性约束的情况下,试图创建一个没有isbn属性的Book节点。
CREATE (book:Book { title: 'Graph Databases' })
注意:
在:Book(isbn)有存在性约束的情况下,试图从一个已存在的Book节点移除isbn属性。这种情况下,移除属性将失败。
4.3.3 删除节点属性存在性约束
使用DROP CONSTRAINT可以从数据库中移除一个约束。
DROP CONSTRAINT ON (book:Book)
ASSERT exists(book.isbn)
4.3.4 创建关系属性存在性约束
使用ASSERT exists(variable.propertyName)
创建约束,可确保特定类型的所有关系都有一个特定的属性。
CREATE CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day)
创建一个存在day属性的LIKED关系。
MATCH (TomH:Person {name:'Tom Hanks', born:1956}), (book:Book { isbn: '1449356265', title: 'Graph Databases' })
CREATE (TomH)-[like:LIKED { day: 'yesterday' }]->(book)
return TomH,book
在有:LIKED(day)存在性约束的情况下,试图创建一个没有day属性的LIKED关系。
MATCH (person :Person {name:'Tom'}), (book:Book { isbn: '1449356265', title: 'Graph Databases' })
CREATE (person)-[like:LIKED]->(book)
return person,book
注意:
- 当数据库中存在LIKED关系且没有day属性时,试图在LIKED关系的day属性上创建属性存在性约束。这种情况因为与已存在的数据冲突,因此约束创建失败。可以选择移除冲突的关系,然后再重新创建约束。
- 在有LIKED(day)存在性约束的情况下,试图从一个已有LIKED关系中移除day属性。这种情况下,也是会报错的。
4.3.5 删除关系属性存在性约束
使用DROP CONSTRAINT从数据库中移除一个约束。
DROP CONSTRAINT ON ()-[like:LIKED]-() ASSERT exists(like.day)
4.4 批量导入
Neo4j批量导入数据有两种方法:
- 第一种是使用Cypher语法中的LOAD CSV
- 第二种是使用neo4j自带的工具neo4j-admin import
LOAD CSV请参考前面章节,可以发现使用load csv只能导入结点,如果还想导入关系数据,就只能靠neo4j自带的import工具了。
这里给出neo4j-admin import语法,具体如下:
neo4j-admin import [--mode=csv] [--database=<name>][--additional-config=<config-file-path>][--report-file=<filename>][--nodes[:Label1:Label2]=<"file1,file2,...">][--relationships[:RELATIONSHIP_TYPE]=<"file1,file2,...">][--id-type=<STRING|INTEGER|ACTUAL>][--input-encoding=<character-set>][--ignore-extra-columns[=<true|false>]][--ignore-duplicate-nodes[=<true|false>]][--ignore-missing-nodes[=<true|false>]][--multiline-fields[=<true|false>]][--delimiter=<delimiter-character>][--array-delimiter=<array-delimiter-character>][--quote=<quotation-character>][--max-memory=<max-memory-that-importer-can-use>][--f=<File containing all arguments to this import>][--high-io=<true/false>]
示例
movies.csv (:LABEL被自动检测为节点标签,也可以后面通过–nodes:movie手动指定)
movie:ID,name,:LABEL
tt0133093,The Matrix,movie
tt0234215,The Matrix Reloaded,movie
tt0242653,The Matrix Revolutions,movie
actors.csv
person:ID,name,:LABEL
keanu,Keanu Reeves,person
laurence,Laurence Fishburne,person
carrieanne,Carrie-Anne Moss,person
roles.csv
:START_ID,role,:END_ID
keanu,Neo,tt0133093
keanu,Neo,tt0234215
keanu,Neo,tt0242653
laurence,Morpheus,tt0133093
laurence,Morpheus,tt0234215
laurence,Morpheus,tt0242653
carrieanne,Trinity,tt0133093
执行导入命令
neo4j-admin import --database=graphImport.db \
--nodes ./import/actors.csv \
--nodes ./import/movies.csv \
--relationships:ACTED_IN ./import/roles.csv
导入完成后,修改./conf/neo4j.conf文件中的dbms.active_database=graphImport.db
,并将其注释打开。再使用neo4j restart重启数据库,然后就可以看到刚才导入的数据。
注意
使用neo4j-admin import只能往空数据库中导入数据,且csv文件必须在import下。
使用csv文件导入数据时,每个结点都必须有一个唯一的ID属性,但是最好不要起名为ID,这会和数据库本身维护的ID字段冲突。
第五章 API开发
5.1 Java开发介绍
Neo4j支持两种类型的API:
• Neo4j的原生的Java API 是一种低级别的纯 JAVA API,用于执行数据库操作。
• Neo4j Cypher Java API 用于执行所有CQL命令以执行数据库操作。
5.2 Neo4j原生Java开发
-
maven依赖
<dependency><groupId>org.neo4j</groupId><artifactId>neo4j</artifactId><version>3.5.35</version> </dependency>
-
创建Neo4j数据库
GraphDatabaseService graphDB = new GraphDatabaseFactory().newEmbeddedDatabase(file);
-
创建节点
Node user1 = graphDB.createNode(Label.label("User")); user1.setProperty("name", "John Johnson"); Node user2 = graphDB.createNode(Label.label("User")); user2.setProperty("name", "Kate Smith");
-
创建关系
user1.createRelationshipTo(user2, RelationshipType.withName("IS_FRIEND_OF"));
-
示例如图所示