圖數(shù)據(jù)庫查詢語言Gremlin vs Cypher vs nGQL的操作入門是怎樣的,針對(duì)這個(gè)問題,這篇文章詳細(xì)介紹了相對(duì)應(yīng)的分析和解答,希望可以幫助更多想解決這個(gè)問題的小伙伴找到更簡單易行的方法。
成都創(chuàng)新互聯(lián)專注于企業(yè)成都全網(wǎng)營銷推廣、網(wǎng)站重做改版、舒蘭網(wǎng)站定制設(shè)計(jì)、自適應(yīng)品牌網(wǎng)站建設(shè)、H5技術(shù)、商城網(wǎng)站定制開發(fā)、集團(tuán)公司官網(wǎng)建設(shè)、外貿(mào)營銷網(wǎng)站建設(shè)、高端網(wǎng)站制作、響應(yīng)式網(wǎng)頁設(shè)計(jì)等建站業(yè)務(wù),價(jià)格優(yōu)惠性價(jià)比高,為舒蘭等各大城市提供網(wǎng)站開發(fā)制作服務(wù)。
文章的開頭我們先來看下什么是圖數(shù)據(jù)庫,根據(jù)維基百科的定義:圖數(shù)據(jù)庫是使用圖結(jié)構(gòu)進(jìn)行語義查詢的數(shù)據(jù)庫,它使用節(jié)點(diǎn)、邊和屬性來表示和存儲(chǔ)數(shù)據(jù)。
雖然和關(guān)系型數(shù)據(jù)庫存儲(chǔ)的結(jié)構(gòu)不同(關(guān)系型數(shù)據(jù)庫為表結(jié)構(gòu),圖數(shù)據(jù)庫為圖結(jié)構(gòu)),但不計(jì)各自的性能問題,關(guān)系型數(shù)據(jù)庫可以通過遞歸查詢或者組合其他 SQL 語句(Join)完成圖查詢語言查詢節(jié)點(diǎn)關(guān)系操作。得益于 1987 年 SQL 成為國際標(biāo)準(zhǔn)化組織(ISO)標(biāo)準(zhǔn),關(guān)系型數(shù)據(jù)庫行業(yè)得到了很好的發(fā)展。同 60、70 年代的關(guān)系型數(shù)據(jù)庫類似,圖數(shù)據(jù)庫這個(gè)領(lǐng)域的查詢語言目前也沒有統(tǒng)一標(biāo)準(zhǔn),雖然 19 年 9 月經(jīng)過國際 SQL 標(biāo)準(zhǔn)委員會(huì)投票表決,決定將圖查詢語言(Graph Query Language)納為一種新的數(shù)據(jù)庫查詢語言,但 GQL 的制定仍需要一段時(shí)間。
介于市面上沒有統(tǒng)一的圖查詢語言標(biāo)準(zhǔn),在本文中我們選取市面上主流的幾款圖查詢語言來分析一波用法,由于篇幅原因本文旨在簡單介紹圖查詢語言和常規(guī)用法,更詳細(xì)的內(nèi)容將在進(jìn)階篇中講述。
Gremlin 是 Apache ThinkerPop 框架下的圖遍歷語言。Gremlin 可以是聲明性的也可以是命令性的。雖然 Gremlin 是基于 Groovy 的,但具有許多語言變體,允許開發(fā)人員以 Java、JavaScript、Python、Scala、Clojure 和 Groovy 等許多現(xiàn)代編程語言原生編寫 Gremlin 查詢。
支持圖數(shù)據(jù)庫:Janus Graph、InfiniteGraph、Cosmos DB、DataStax Enterprise(5.0+) 、Amazon Neptune
Cypher 是一個(gè)描述性的圖形查詢語言,允許不必編寫圖形結(jié)構(gòu)的遍歷代碼對(duì)圖形存儲(chǔ)有表現(xiàn)力和效率的查詢,和 SQL 很相似,Cypher 語言的關(guān)鍵字不區(qū)分大小寫,但是屬性值,標(biāo)簽,關(guān)系類型和變量是區(qū)分大小寫的。
支持圖數(shù)據(jù)庫: Neo4j、redisGraph、AgensGraph
nGQL 是一種類 SQL 的聲明型的文本查詢語言,nGQL 同樣是關(guān)鍵詞大小寫不敏感的查詢語言,目前支持模式匹配、聚合運(yùn)算、圖計(jì)算,可無嵌入組合語句。
支持圖數(shù)據(jù)庫:Nebula Graph
在比較這 3 個(gè)圖查詢語言之前,我們先來看看他們各自的術(shù)語,如果你翻閱他們的文檔會(huì)經(jīng)常見到下面這些“關(guān)鍵字”,在這里我們不講用法,只看這些圖數(shù)據(jù)庫常用概念在這 3 個(gè)圖數(shù)據(jù)庫文檔中的叫法。
術(shù)語 | Gremlin | Cypher | nGQL |
---|---|---|---|
點(diǎn) | Vertex | Node | Vertex |
邊 | Edge | Relationship | Edge |
點(diǎn)類型 | Label | Label | Tag |
邊類型 | label | RelationshipType | edge type |
點(diǎn) ID | vid | id(n) | vid |
邊 ID | eid | id(r) | 無 |
插入 | add | create | insert |
刪除 | drop | delete | delete / drop |
更新屬性 | setProperty | set | update |
我們可以看到大體上對(duì)點(diǎn)和邊的叫法類似,只不過 Cypher 中直接使用了 Relationship 關(guān)系一詞代表邊。其他的術(shù)語基本都非常直觀。
了解過 Gremlin、Cypher、nGQL 中常見的術(shù)語之后,我們來看看使用這 3 個(gè)圖查詢語言過程中會(huì)需要了解的常規(guī)語法。
# Gremlin 創(chuàng)建圖 g = TinkerGraph.open().traversal() # nGQL 創(chuàng)建圖空間 CREATE SPACE gods
圖結(jié)構(gòu)由點(diǎn)和邊組成,一條邊連接兩個(gè)點(diǎn)。在 Gremlin 和 nGQL 中稱之為 Vertex,Cypher 則稱之為 Node。如何在圖數(shù)據(jù)庫中新建一個(gè)點(diǎn)呢?可以參考下面的語法
# Gremlin 創(chuàng)建/插入點(diǎn) g.addV(vertexLabel).property() # Cypher 創(chuàng)建點(diǎn) CREATE (:nodeLabel {property}) # nGQL 創(chuàng)建/插入點(diǎn) INSERT VERTEX tagName (propNameList) VALUES vid:(tagKey propValue)
點(diǎn)允許有對(duì)應(yīng)的類型,在 Gremlin 和 Cypher 叫
label
,在 nGQL 中為
tag
。點(diǎn)類型可對(duì)應(yīng)有多種屬性(Property),例如
Person 可以有
name、age 等屬性。
點(diǎn)類型相關(guān)的語法示例如下:
# Gremlin 創(chuàng)建點(diǎn)類型 g.addV(vertexLabel).property() # nGQL 創(chuàng)建點(diǎn)類型 CREATE tagName(PropNameList)
這里說明下,無論在 Gremlin 和 nGQL 中存在類似
IF NOT EXISTS
用法,即:如果不存在則創(chuàng)建,存在則直接返回。
創(chuàng)建好點(diǎn)之后如何查看點(diǎn)類型呢,可以參考以下方式。
# Gremlin 查看(獲?。c(diǎn)類型 g.V().label().dedup(); # Cypher 查看點(diǎn)類型方法 1 MATCH (n) RETURN DISTINCT labels(n) # Cypher 查看點(diǎn)類型方法 2 CALL db.labels(); # nGQL 查看點(diǎn)類型 SHOW TAGS
上面簡單介紹了點(diǎn)、點(diǎn)類型,下面進(jìn)入數(shù)據(jù)庫基本 DML——CRUD,在上文介紹點(diǎn)時(shí)順便介紹了點(diǎn)的創(chuàng)建和插入,這里說下如何插入特定類型的點(diǎn),和點(diǎn)的獲取、刪除和更新。
和插入點(diǎn)的操作類似,只不過需要指定某種點(diǎn)類型。語法參考:
# Gremlin 插入特定類型點(diǎn) g.addV(String vertexLabel).property() # Cypher 插入特定類型點(diǎn) CREATE (node:label) # nGQL 插入特定類型點(diǎn) INSERT VERTEX(prop_name_list) VALUES :(prop_value_list)
# Gremlin 查看點(diǎn) g.V() # Cypher 查看點(diǎn) MATCH (n) WHERE condition RETURN properties(n) # nGQL 查看點(diǎn) FETCH PROP ON
術(shù)語篇中提過 nGQL 中刪除操作對(duì)應(yīng)單詞有
Delete
和
Drop
,在 nGQL 中 Delete 一般用于點(diǎn)邊,Drop 用于 Schema 刪除,這點(diǎn)和 SQL 的設(shè)計(jì)思路是一樣的。
# Gremlin 刪除點(diǎn) g.V().drop() # Cypher 刪除點(diǎn) MATCH (node:label) DETACH DELETE node # nGQL 刪除點(diǎn) DELETE VERTEX
用數(shù)據(jù)庫的小伙伴都知道數(shù)據(jù)的常態(tài)是數(shù)據(jù)變更,來瞅瞅這 3 個(gè)圖查詢是使用什么語法來更新點(diǎn)數(shù)據(jù)的吧
# Gremlin 更新點(diǎn) g.V().property() # Cypher 更新點(diǎn) SET n.prop = V # nGQL 更新點(diǎn) UPDATE VERTEX SET
可以看到 Cypher 和 nGQL 都使用 SET 關(guān)鍵詞來設(shè)置點(diǎn)對(duì)應(yīng)的類型值,只不過 nGQL 中多了 UPDATE 關(guān)鍵詞來標(biāo)識(shí)操作,Gremlin 的操作和上文提到的查看點(diǎn)類似,只不過增加了變更 property 值操作。
在 Gremlin 和 nGQL 稱呼邊為 Edge,而 Cypher 稱之為 Relationship。下面進(jìn)入到邊相關(guān)的語法內(nèi)容
和點(diǎn)一樣,邊也可以有對(duì)應(yīng)的類型
# Gremlin 創(chuàng)建邊類型 g.edgeLabel() # nGQL 創(chuàng)建邊類型 CREATE EDGE edgeTypeName(propNameList)
說完邊類型應(yīng)該進(jìn)入到邊的常規(guī)操作部分了
可以看到和點(diǎn)的使用語法類似,只不過在 Cypher 和 nGQL 中分別使用
-[]->
和
->
來表示關(guān)系,而 Gremlin 則用
to()
關(guān)鍵詞來標(biāo)識(shí)指向關(guān)系,在使用這 3 種圖查詢語言的圖數(shù)據(jù)庫中的邊均為有向邊,下圖左邊為有向邊,右邊為無向邊。
# 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 ->
除了常規(guī)的點(diǎn)、邊 CRUD 外,我們可以簡單看看這 3 種圖查詢語言的組合查詢。
# Gremlin 指定點(diǎn)查指定邊 g.V().outE( ) # Cypher 指定點(diǎn)查指定邊 Match (n)->[r:label]->[] WHERE id(n) = vid RETURN r # nGQL 指定點(diǎn)查指定邊 GO FROM OVER
在反向查詢中,Gremlin 使用了 in 來表示反向關(guān)系,而 Cypher 則更直觀的將指向箭頭反向變成
<-
來表示反向關(guān)系,nGQL 則用關(guān)鍵詞
REVERSELY
來標(biāo)識(shí)反向關(guān)系。
# Gremlin 沿指定點(diǎn)反向查詢指定邊 g.V().inE( ) # Cypher 沿指定點(diǎn)反向查詢指定邊 MATCH (n)<-[r:label]-() # nGQL 沿指定點(diǎn)反向查詢指定邊 GO FROM OVER REVERSELY
如果在圖中,邊的方向不重要(正向、反向都可以),那 Gremlin 使用
both()
,Cypher 使用
-[]-
,nGQL使用關(guān)鍵詞
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 跳關(guān)系,而 Cypher 用
relationship*1..N
來表示 N 跳關(guān)系。
# Gremlin 沿指定點(diǎn)查詢指定邊 N 跳 g.V().repeat(out( )).times(N) # Cypher 沿指定點(diǎn)查詢指定邊 N 跳 MATCH (n)-[r:label*N]->() WHERE condition RETURN r # nGQL 沿指定點(diǎn)查詢指定邊 N 跳 GO N STEPS FROM OVER
# Gremlin 返回指定兩點(diǎn)路徑 g.V().repeat(out()).until( ).path() # Cypher 返回指定兩點(diǎn)路徑 MATCH p =(a)-[.*]->(b) WHERE condition RETURN p # nGQL 返回指定兩點(diǎn)路徑 FIND ALL PATH FROM TO OVER *
說了一通語法之后,是時(shí)候展示真正的技術(shù)了——來個(gè)具體一點(diǎn)的例子。
實(shí)操示例使用了 Janus Graph 的示例圖 The Graphs of Gods。該圖結(jié)構(gòu)如下圖所示,描述了羅馬萬神話中諸神關(guān)系。
# 插入點(diǎn) ## 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" │ └─────────────────────────┘
關(guān)于圖數(shù)據(jù)庫查詢語言Gremlin vs Cypher vs nGQL的操作入門是怎樣的問題的解答就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,如果你還有很多疑惑沒有解開,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道了解更多相關(guān)知識(shí)。