重庆分公司,新征程启航
为企业提供网站建设、域名注册、服务器等服务
图数据库查询语言Gremlin vs Cypher vs nGQL的操作入门是怎样的,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。
成都创新互联专注于企业成都全网营销推广、网站重做改版、舒兰网站定制设计、自适应品牌网站建设、H5技术、商城网站定制开发、集团公司官网建设、外贸营销网站建设、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为舒兰等各大城市提供网站开发制作服务。
文章的开头我们先来看下什么是图数据库,根据维基百科的定义:图数据库是使用图结构进行语义查询的数据库,它使用节点、边和属性来表示和存储数据。
虽然和关系型数据库存储的结构不同(关系型数据库为表结构,图数据库为图结构),但不计各自的性能问题,关系型数据库可以通过递归查询或者组合其他 SQL 语句(Join)完成图查询语言查询节点关系操作。得益于 1987 年 SQL 成为国际标准化组织(ISO)标准,关系型数据库行业得到了很好的发展。同 60、70 年代的关系型数据库类似,图数据库这个领域的查询语言目前也没有统一标准,虽然 19 年 9 月经过国际 SQL 标准委员会投票表决,决定将图查询语言(Graph Query Language)纳为一种新的数据库查询语言,但 GQL 的制定仍需要一段时间。
介于市面上没有统一的图查询语言标准,在本文中我们选取市面上主流的几款图查询语言来分析一波用法,由于篇幅原因本文旨在简单介绍图查询语言和常规用法,更详细的内容将在进阶篇中讲述。
Gremlin 是 Apache ThinkerPop 框架下的图遍历语言。Gremlin 可以是声明性的也可以是命令性的。虽然 Gremlin 是基于 Groovy 的,但具有许多语言变体,允许开发人员以 Java、JavaScript、Python、Scala、Clojure 和 Groovy 等许多现代编程语言原生编写 Gremlin 查询。
支持图数据库:Janus Graph、InfiniteGraph、Cosmos DB、DataStax Enterprise(5.0+) 、Amazon Neptune
Cypher 是一个描述性的图形查询语言,允许不必编写图形结构的遍历代码对图形存储有表现力和效率的查询,和 SQL 很相似,Cypher 语言的关键字不区分大小写,但是属性值,标签,关系类型和变量是区分大小写的。
支持图数据库: Neo4j、redisGraph、AgensGraph
nGQL 是一种类 SQL 的声明型的文本查询语言,nGQL 同样是关键词大小写不敏感的查询语言,目前支持模式匹配、聚合运算、图计算,可无嵌入组合语句。
支持图数据库:Nebula Graph
在比较这 3 个图查询语言之前,我们先来看看他们各自的术语,如果你翻阅他们的文档会经常见到下面这些“关键字”,在这里我们不讲用法,只看这些图数据库常用概念在这 3 个图数据库文档中的叫法。
术语 | Gremlin | Cypher | nGQL |
---|---|---|---|
点 | Vertex | Node | Vertex |
边 | Edge | Relationship | Edge |
点类型 | Label | Label | Tag |
边类型 | label | RelationshipType | edge type |
点 ID | vid | id(n) | vid |
边 ID | eid | id(r) | 无 |
插入 | add | create | insert |
删除 | drop | delete | delete / drop |
更新属性 | setProperty | set | update |
我们可以看到大体上对点和边的叫法类似,只不过 Cypher 中直接使用了 Relationship 关系一词代表边。其他的术语基本都非常直观。
了解过 Gremlin、Cypher、nGQL 中常见的术语之后,我们来看看使用这 3 个图查询语言过程中会需要了解的常规语法。
# Gremlin 创建图 g = TinkerGraph.open().traversal() # nGQL 创建图空间 CREATE SPACE gods
图结构由点和边组成,一条边连接两个点。在 Gremlin 和 nGQL 中称之为 Vertex,Cypher 则称之为 Node。如何在图数据库中新建一个点呢?可以参考下面的语法
# Gremlin 创建/插入点 g.addV(vertexLabel).property() # Cypher 创建点 CREATE (:nodeLabel {property}) # nGQL 创建/插入点 INSERT VERTEX tagName (propNameList) VALUES vid:(tagKey propValue)
点允许有对应的类型,在 Gremlin 和 Cypher 叫
label
,在 nGQL 中为
tag
。点类型可对应有多种属性(Property),例如
Person 可以有
name、age 等属性。
点类型相关的语法示例如下:
# Gremlin 创建点类型 g.addV(vertexLabel).property() # nGQL 创建点类型 CREATE tagName(PropNameList)
这里说明下,无论在 Gremlin 和 nGQL 中存在类似
IF NOT EXISTS
用法,即:如果不存在则创建,存在则直接返回。
创建好点之后如何查看点类型呢,可以参考以下方式。
# Gremlin 查看(获取)点类型 g.V().label().dedup(); # Cypher 查看点类型方法 1 MATCH (n) RETURN DISTINCT labels(n) # Cypher 查看点类型方法 2 CALL db.labels(); # nGQL 查看点类型 SHOW TAGS
上面简单介绍了点、点类型,下面进入数据库基本 DML——CRUD,在上文介绍点时顺便介绍了点的创建和插入,这里说下如何插入特定类型的点,和点的获取、删除和更新。
和插入点的操作类似,只不过需要指定某种点类型。语法参考:
# Gremlin 插入特定类型点 g.addV(String vertexLabel).property() # Cypher 插入特定类型点 CREATE (node:label) # nGQL 插入特定类型点 INSERT VERTEX(prop_name_list) VALUES :(prop_value_list)
# Gremlin 查看点 g.V() # Cypher 查看点 MATCH (n) WHERE condition RETURN properties(n) # nGQL 查看点 FETCH PROP ON
术语篇中提过 nGQL 中删除操作对应单词有
Delete
和
Drop
,在 nGQL 中 Delete 一般用于点边,Drop 用于 Schema 删除,这点和 SQL 的设计思路是一样的。
# Gremlin 删除点 g.V().drop() # Cypher 删除点 MATCH (node:label) DETACH DELETE node # nGQL 删除点 DELETE VERTEX
用数据库的小伙伴都知道数据的常态是数据变更,来瞅瞅这 3 个图查询是使用什么语法来更新点数据的吧
# Gremlin 更新点 g.V().property() # Cypher 更新点 SET n.prop = V # nGQL 更新点 UPDATE VERTEX SET
可以看到 Cypher 和 nGQL 都使用 SET 关键词来设置点对应的类型值,只不过 nGQL 中多了 UPDATE 关键词来标识操作,Gremlin 的操作和上文提到的查看点类似,只不过增加了变更 property 值操作。
在 Gremlin 和 nGQL 称呼边为 Edge,而 Cypher 称之为 Relationship。下面进入到边相关的语法内容
和点一样,边也可以有对应的类型
# Gremlin 创建边类型 g.edgeLabel() # nGQL 创建边类型 CREATE EDGE edgeTypeName(propNameList)
说完边类型应该进入到边的常规操作部分了
可以看到和点的使用语法类似,只不过在 Cypher 和 nGQL 中分别使用
-[]->
和
->
来表示关系,而 Gremlin 则用
to()
关键词来标识指向关系,在使用这 3 种图查询语言的图数据库中的边均为有向边,下图左边为有向边,右边为无向边。
# Gremlin 插入指定边类型的边 g.addE(String edgeLabel).from(v1).to(v2).property() # Cypher 插入指定边类型的边 CREATE (: )- [( : )] ->( : ) # nGQL 插入指定边类型的边 INSERT EDGE ( ) VALUES -> : \ ( )
# Gremlin 删除边 g.E().drop() # Cypher 删除边 MATCH ( : )-[r:relationship-label-name]->() DELETE r # nGQL 删除边 DELETE EDGE ->
# Gremlin 查看指定边 g.E() # Cypher 查看指定边 MATCH (n)-[r:label]->() WHERE condition RETURN properties(r) # nGQL 查看指定边 FETCH PROP ON ->
除了常规的点、边 CRUD 外,我们可以简单看看这 3 种图查询语言的组合查询。
# Gremlin 指定点查指定边 g.V().outE( ) # Cypher 指定点查指定边 Match (n)->[r:label]->[] WHERE id(n) = vid RETURN r # nGQL 指定点查指定边 GO FROM OVER
在反向查询中,Gremlin 使用了 in 来表示反向关系,而 Cypher 则更直观的将指向箭头反向变成
<-
来表示反向关系,nGQL 则用关键词
REVERSELY
来标识反向关系。
# Gremlin 沿指定点反向查询指定边 g.V().inE( ) # Cypher 沿指定点反向查询指定边 MATCH (n)<-[r:label]-() # nGQL 沿指定点反向查询指定边 GO FROM OVER REVERSELY
如果在图中,边的方向不重要(正向、反向都可以),那 Gremlin 使用
both()
,Cypher 使用
-[]-
,nGQL使用关键词
BIDIRECT
。
# Traverse edges with specified vertices Gremlin g.V().bothE( ) # Traverse edges with specified vertices Cypher MATCH (n)-[r:label]-() # Traverse edges with specified vertices nGQL GO FROM OVER BIDIRECT
Gremlin 和 nGQL 分别用 times 和 step 来表示 N 跳关系,而 Cypher 用
relationship*1..N
来表示 N 跳关系。
# Gremlin 沿指定点查询指定边 N 跳 g.V().repeat(out( )).times(N) # Cypher 沿指定点查询指定边 N 跳 MATCH (n)-[r:label*N]->() WHERE condition RETURN r # nGQL 沿指定点查询指定边 N 跳 GO N STEPS FROM OVER
# Gremlin 返回指定两点路径 g.V().repeat(out()).until( ).path() # Cypher 返回指定两点路径 MATCH p =(a)-[.*]->(b) WHERE condition RETURN p # nGQL 返回指定两点路径 FIND ALL PATH FROM TO OVER *
说了一通语法之后,是时候展示真正的技术了——来个具体一点的例子。
实操示例使用了 Janus Graph 的示例图 The Graphs of Gods。该图结构如下图所示,描述了罗马万神话中诸神关系。
# 插入点 ## nGQL nebula> INSERT VERTEX character(name, age, type) VALUES hash("saturn"):("saturn", 10000, "titan"), hash("jupiter"):("jupiter", 5000, "god"); ## Gremlin gremlin> saturn = g.addV("character").property(T.id, 1).property('name', 'saturn').property('age', 10000).property('type', 'titan').next(); ==>v[1] gremlin> jupiter = g.addV("character").property(T.id, 2).property('name', 'jupiter').property('age', 5000).property('type', 'god').next(); ==>v[2] gremlin> prometheus = g.addV("character").property(T.id, 31).property('name', 'prometheus').property('age', 1000).property('type', 'god').next(); ==>v[31] gremlin> jesus = g.addV("character").property(T.id, 32).property('name', 'jesus').property('age', 5000).property('type', 'god').next(); ==>v[32] ## Cypher cypher> CREATE (src:character {name:"saturn", age: 10000, type:"titan"}) cypher> CREATE (dst:character {name:"jupiter", age: 5000, type:"god"}) # 插入边 ## nGQL nebula> INSERT EDGE father() VALUES hash("jupiter")->hash("saturn"):(); ## Gremlin gremlin> g.addE("father").from(jupiter).to(saturn).property(T.id, 13); ==>e[13][2-father->1] ## Cypher cypher> CREATE (src)-[rel:father]->(dst)
# nGQL nebula> DELETE VERTEX hash("prometheus"); # Gremlin gremlin> g.V(prometheus).drop(); # Cypher cypher> MATCH (n:character {name:"prometheus"}) DETACH DELETE n
# nGQL nebula> UPDATE VERTEX hash("jesus") SET character.type = 'titan'; # Gremlin gremlin> g.V(jesus).property('age', 6000); ==>v[32] # Cypher cypher> MATCH (n:character {name:"jesus"}) SET n.type = 'titan';
# nGQL nebula> FETCH PROP ON character hash("saturn"); =================================================== | character.name | character.age | character.type | =================================================== | saturn | 10000 | titan | --------------------------------------------------- # Gremlin gremlin> g.V(saturn).valueMap(); ==>[name:[saturn],type:[titan],age:[10000]] # Cypher cypher> MATCH (n:character {name:"saturn"}) RETURN properties(n) ╒════════════════════════════════════════════╕ │"properties(n)" │ ╞════════════════════════════════════════════╡ │{"name":"saturn","type":"titan","age":10000}│ └────────────────────────────────────────────┘
# nGQL nebula> LOOKUP ON character WHERE character.name == 'hercules' | \ -> GO FROM $-.VertexID OVER father YIELD $$.character.name; ===================== | $$.character.name | ===================== | jupiter | --------------------- # Gremlin gremlin> g.V().hasLabel('character').has('name','hercules').out('father').values('name'); ==>jupiter # Cypher cypher> MATCH (src:character{name:"hercules"})-[:father]->(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"jupiter" │ └──────────┘
# nGQL nebula> LOOKUP ON character WHERE character.name == 'hercules' | \ -> GO 2 STEPS FROM $-.VertexID OVER father YIELD $$.character.name; ===================== | $$.character.name | ===================== | saturn | --------------------- # Gremlin gremlin> g.V().hasLabel('character').has('name','hercules').out('father').out('father').values('name'); ==>saturn # Cypher cypher> MATCH (src:character{name:"hercules"})-[:father*2]->(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"saturn" │ └──────────┘
# nGQL nebula> LOOKUP ON character WHERE character.age > 100 YIELD character.name, character.age; ========================================================= | VertexID | character.name | character.age | ========================================================= | 6761447489613431910 | pluto | 4000 | --------------------------------------------------------- | -5860788569139907963 | neptune | 4500 | --------------------------------------------------------- | 4863977009196259577 | jupiter | 5000 | --------------------------------------------------------- | -4316810810681305233 | saturn | 10000 | --------------------------------------------------------- # Gremlin gremlin> g.V().hasLabel('character').has('age',gt(100)).values('name'); ==>saturn ==>jupiter ==>neptune ==>pluto # Cypher cypher> MATCH (src:character) WHERE src.age > 100 RETURN src.name ╒═══════════╕ │"src.name" │ ╞═══════════╡ │ "saturn" │ ├───────────┤ │ "jupiter" │ ├───────────┤ │ "neptune" │ │───────────│ │ "pluto" │ └───────────┘
# nGQL nebula> GO FROM hash("pluto") OVER lives YIELD lives._dst AS place | GO FROM $-.place OVER lives REVERSELY WHERE \ $$.character.name != "pluto" YIELD $$.character.name AS cohabitants; =============== | cohabitants | =============== | cerberus | --------------- # Gremlin gremlin> g.V(pluto).out('lives').in('lives').where(is(neq(pluto))).values('name'); ==>cerberus # Cypher cypher> MATCH (src:character{name:"pluto"})-[:lives]->()<-[:lives]-(dst:character) RETURN dst.name ╒══════════╕ │"dst.name"│ ╞══════════╡ │"cerberus"│ └──────────┘
# which brother lives in which place? ## nGQL nebula> GO FROM hash("pluto") OVER brother YIELD brother._dst AS god | \ GO FROM $-.god OVER lives YIELD $^.character.name AS Brother, $$.location.name AS Habitations; ========================= | Brother | Habitations | ========================= | jupiter | sky | ------------------------- | neptune | sea | ------------------------- ## Gremlin gremlin> g.V(pluto).out('brother').as('god').out('lives').as('place').select('god','place').by('name'); ==>[god:jupiter, place:sky] ==>[god:neptune, place:sea] ## Cypher cypher> MATCH (src:Character{name:"pluto"})-[:brother]->(bro:Character)-[:lives]->(dst) RETURN bro.name, dst.name ╒═════════════════════════╕ │"bro.name" │"dst.name"│ ╞═════════════════════════╡ │ "jupiter" │ "sky" │ ├─────────────────────────┤ │ "neptune" │ "sea" │ └─────────────────────────┘
关于图数据库查询语言Gremlin vs Cypher vs nGQL的操作入门是怎样的问题的解答就分享到这里了,希望以上内容可以对大家有一定的帮助,如果你还有很多疑惑没有解开,可以关注创新互联行业资讯频道了解更多相关知识。