圖數(shù)據(jù)庫(kù)越來(lái)越受歡迎和被采用。隨著來(lái)自許多不同來(lái)源的數(shù)據(jù)量越來(lái)越大,能夠理解數(shù)據(jù)并了解它們之間的聯(lián)系至關(guān)重要。或許有些小伙伴可能聽說(shuō)過(guò)圖數(shù)據(jù)庫(kù) (GDB),有些人可能還沒有聽說(shuō)過(guò)。在本文中,我們將準(zhǔn)確介紹它們是什么,以及它們與更傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù)管理系統(tǒng) (RDBMS) 的比較,后者一直是過(guò)去 40 多年的主要軟件應(yīng)用程序。
受 Neo4j 用作圖查詢指導(dǎo)性介紹的一個(gè)小電影數(shù)據(jù)集的啟發(fā),我們將查看并排示例以及數(shù)據(jù)模型或查詢?cè)趫D數(shù)據(jù)庫(kù)和關(guān)系型數(shù)據(jù)庫(kù)。
在本文中,我們將:
- 介紹圖數(shù)據(jù)庫(kù),簡(jiǎn)要介紹現(xiàn)有的兩種模型
- 從概念上了解關(guān)系范式和圖范式之間的差異
- 查看電影數(shù)據(jù)集,從 GDB 和 RDBMS 的角度比較和對(duì)比數(shù)據(jù)模型
- 基于 Cypher(用于 GDB)或 SQL 比較和對(duì)比一些查詢
- 討論電影示例中出現(xiàn)的更有趣的查詢,并準(zhǔn)確地找出正在發(fā)生的事情
如果您想在閱讀文章之前(或在閱讀期間?。┦褂檬纠菥氹娪皵?shù)據(jù)集,我們非常歡迎您這樣做。
什么是圖數(shù)據(jù)庫(kù)?
首先,在我們深入研究什么是圖數(shù)據(jù)庫(kù)之前,讓我們定義這個(gè)術(shù)語(yǔ)。圖數(shù)據(jù)庫(kù)是一種“不僅僅是 SQL”(NoSQL,全程為Not Only SQL)的數(shù)據(jù)存儲(chǔ)。它們旨在以圖結(jié)構(gòu)存儲(chǔ)和檢索數(shù)據(jù)。
使用的存儲(chǔ)機(jī)制可能因數(shù)據(jù)庫(kù)而異。一些 GDB 可能使用更傳統(tǒng)的數(shù)據(jù)庫(kù)結(jié)構(gòu),例如基于表,然后在頂部有一個(gè)圖形 API 層。其他的將是“原生”GDB——從存儲(chǔ)、管理和查詢到數(shù)據(jù)庫(kù)的整個(gè)構(gòu)造維護(hù)數(shù)據(jù)的圖形結(jié)構(gòu)。許多當(dāng)前可用的圖形數(shù)據(jù)庫(kù)通過(guò)將實(shí)體之間的關(guān)系視為“一等公民”來(lái)做到這一點(diǎn)。
不同類型的圖數(shù)據(jù)庫(kù)
廣義上有兩種類型的 GDB,資源描述框架 (RDF)/三重存儲(chǔ)/語(yǔ)義圖數(shù)據(jù)庫(kù)和屬性圖數(shù)據(jù)庫(kù)。
RDF GDB 使用三元組的概念,它是由三個(gè)元素組成的語(yǔ)句:主語(yǔ)-謂語(yǔ)-賓語(yǔ)。
主語(yǔ)將是圖中的資源或節(jié)點(diǎn),對(duì)象將是另一個(gè)節(jié)點(diǎn)或文字值,謂詞表示主題和對(duì)象之間的關(guān)系。節(jié)點(diǎn)或關(guān)系上沒有內(nèi)部結(jié)構(gòu),一切都由唯一標(biāo)識(shí)符以 URI 的形式標(biāo)識(shí)。
這種結(jié)構(gòu)背后的動(dòng)機(jī)是交換和發(fā)布數(shù)據(jù)。
GDB 屬性專注于存儲(chǔ)接近邏輯模型的數(shù)據(jù)的概念。這反過(guò)來(lái)將基于數(shù)據(jù)本身所尋求的問題,并專注于使該表示盡可能高效地存儲(chǔ)和查詢。
與基于 RDF 的圖不同,節(jié)點(diǎn)和關(guān)系上有內(nèi)部結(jié)構(gòu),可提供豐富的數(shù)據(jù)表示以及相關(guān)的元數(shù)據(jù)。
屬性圖數(shù)據(jù)庫(kù)剖析
在本文的其余部分,我們將重點(diǎn)關(guān)注原生屬性圖數(shù)據(jù)庫(kù),特別是 Neo4j。讓我們檢查一下主要組件。
屬性圖數(shù)據(jù)庫(kù)的主要組成部分如下:
- 節(jié)點(diǎn):在圖論中也稱為頂點(diǎn)——構(gòu)建圖的主要數(shù)據(jù)元素
- 關(guān)系:在圖論中也稱為邊——兩個(gè)節(jié)點(diǎn)之間的鏈接。它將有方向和類型。沒有關(guān)系的節(jié)點(diǎn)是允許的,沒有兩個(gè)節(jié)點(diǎn)的關(guān)系是不允許的
- 標(biāo)簽:定義一個(gè)節(jié)點(diǎn)類別,一個(gè)節(jié)點(diǎn)可以有多個(gè)
- 屬性:豐富一個(gè)節(jié)點(diǎn)或關(guān)系,不可以為空值!
圖數(shù)據(jù)庫(kù)與關(guān)系數(shù)據(jù)庫(kù)
關(guān)系數(shù)據(jù)庫(kù)回顧
許多開發(fā)人員都熟悉傳統(tǒng)的關(guān)系數(shù)據(jù)庫(kù),其中數(shù)據(jù)存儲(chǔ)在定義良好的模式中的表中。
表中的每一行都是一個(gè)離散的數(shù)據(jù)實(shí)體。行中的這些元素之一通常用于定義其唯一性:主鍵。它可能是一個(gè)唯一的 ID,也可能是一個(gè)人的身份證號(hào)碼之類的東西。
然后我們通過(guò)一個(gè)稱為規(guī)范化的過(guò)程來(lái)減少數(shù)據(jù)重復(fù)。在規(guī)范化中,我們將引用(例如某人的地址)移動(dòng)到另一個(gè)表中。因此,我們從代表實(shí)體的行到代表該人地址的行獲得了一個(gè)引用。
例如,如果某人更改了他們的地址,您不希望該人的地址到處都是多個(gè)版本,并且必須嘗試記住該人地址所在位置的所有不同實(shí)例。規(guī)范化確保您擁有一個(gè)版本的數(shù)據(jù),因此您可以在一個(gè)地方進(jìn)行更新。
然后當(dāng)我們查詢時(shí),我們要重構(gòu)這個(gè)歸一化的數(shù)據(jù)。我們執(zhí)行所謂的 JOIN 操作。
在我們的主實(shí)體行中,我們有一個(gè)主鍵,用于標(biāo)識(shí)實(shí)體的 ID,比如說(shuō)人。我們還有一個(gè)叫做外鍵的東西,它代表我們地址表中的一行。我們通過(guò)主鍵和外鍵連接這兩個(gè)表,并使用它在地址表中查找地址。這稱為 JOIN,這些 JOIN 在查詢時(shí)和讀取時(shí)完成。
當(dāng)我們?cè)陉P(guān)系數(shù)據(jù)庫(kù)中執(zhí)行 JOIN 時(shí),它是一個(gè)集合比較操作,我們?cè)谄渲胁榭次覀兊膬山M數(shù)據(jù)重疊的位置(在這種情況下,集合是人員表和地址表)。在高層次上,這就是傳統(tǒng)關(guān)系數(shù)據(jù)庫(kù)的工作方式。
原生圖數(shù)據(jù)庫(kù)的工作原理:連接和無(wú)索引鄰接
讓我們快速瀏覽一下原生圖形數(shù)據(jù)庫(kù)及其工作原理。
我們談到了關(guān)系數(shù)據(jù)庫(kù)中的離散實(shí)體是表中的一行。在原生圖形數(shù)據(jù)庫(kù)中,該行相當(dāng)于一個(gè)節(jié)點(diǎn)。它仍然是一個(gè)離散的實(shí)體,所以我們?nèi)匀挥羞@個(gè)標(biāo)準(zhǔn)化的元素。
一個(gè)節(jié)點(diǎn)將是一個(gè)實(shí)體。如果我們有個(gè)人節(jié)點(diǎn),我們將有一個(gè)人一個(gè)節(jié)點(diǎn)。我們會(huì)有一定程度的獨(dú)特性,比如說(shuō)社會(huì)安全號(hào)碼。
然而,關(guān)鍵的區(qū)別在于,當(dāng)我們將這個(gè)人節(jié)點(diǎn)連接到另一個(gè)離散實(shí)體(例如地址)時(shí),我們會(huì)在這兩個(gè)點(diǎn)之間創(chuàng)建物理連接(也稱為關(guān)系)。
地址會(huì)有一個(gè)指針,說(shuō)明連接到節(jié)點(diǎn)的關(guān)系的出站部分是什么?然后我們有另一個(gè)指向關(guān)系的入站部分的指針指向另一個(gè)節(jié)點(diǎn)。
因此,實(shí)際上,我們正在收集一組指針,這是這兩個(gè)實(shí)體之間物理連接的體現(xiàn)。這就是最大的不同。
在關(guān)系數(shù)據(jù)庫(kù)中,您將在讀取時(shí)使用連接重新構(gòu)建數(shù)據(jù),這意味著在查詢時(shí),它會(huì)嘗試找出事物如何映射在一起。
在圖數(shù)據(jù)庫(kù)中,由于我們已經(jīng)知道這兩個(gè)元素是相連的,所以我們不需要在查詢時(shí)查找映射。我們所做的只是跟蹤與其他節(jié)點(diǎn)的存儲(chǔ)關(guān)系。
這就是我們所說(shuō)的無(wú)索引鄰接。與其他數(shù)據(jù)庫(kù)系統(tǒng)相比,這種無(wú)索引鄰接的概念是理解原生圖形數(shù)據(jù)庫(kù)性能優(yōu)化的關(guān)鍵。
無(wú)索引鄰接意味著在局部圖遍歷期間,按照連接圖中節(jié)點(diǎn)的這些指針(關(guān)系),操作的性能不依賴于圖的整體大小。這取決于連接到您正在遍歷的節(jié)點(diǎn)的關(guān)系數(shù)量。
當(dāng)我們說(shuō) JOIN 是一個(gè)集合操作(??交集)時(shí),我們使用關(guān)系數(shù)據(jù)庫(kù)中的索引來(lái)查看這兩個(gè)集合的重疊位置。這意味著 JOIN 操作的性能隨著表變大而開始變慢。
在大 O 符號(hào)術(shù)語(yǔ)中,這類似于使用索引的對(duì)數(shù)增長(zhǎng)——類似于 O(log n) 并且隨著查詢中的 JOIN 數(shù)量呈指數(shù)增長(zhǎng)。
另一方面,圖中的遍歷關(guān)系更多的是基于我們實(shí)際遍歷的節(jié)點(diǎn)中的關(guān)系數(shù)量的線性增長(zhǎng),而不是圖的整體大小。
這是圖數(shù)據(jù)庫(kù)為我們提供無(wú)索引鄰接的基本查詢時(shí)間優(yōu)化。從性能的角度來(lái)看,當(dāng)我們考慮原生圖形數(shù)據(jù)庫(kù)時(shí),這確實(shí)是最重要的事情。
電影圖簡(jiǎn)介
我們已經(jīng)談到了圖形和關(guān)系數(shù)據(jù)庫(kù)之間的理論差異?,F(xiàn)在讓我們開始看一些并排比較。
電影圖由一個(gè)數(shù)據(jù)集組成,該數(shù)據(jù)集由演員、導(dǎo)演、制片人、作家、評(píng)論家和電影組成,以及有關(guān)它們?nèi)绾蜗嗷ミB接的信息。
電影數(shù)據(jù)集包括:
- 133個(gè)人節(jié)點(diǎn)/實(shí)體
- 38個(gè)電影節(jié)點(diǎn)/實(shí)體
- 上述實(shí)體之間的 253 個(gè)關(guān)系/連接,描述的連接例如:
- 導(dǎo)演電影的人
- 在電影中扮演的角色和扮演的角色
- 寫電影的人
- 制作電影的人
- 評(píng)論過(guò)電影并給出評(píng)分和摘要的人
- 跟隨另一個(gè)人的人
雖然它是一個(gè)相對(duì)較小的數(shù)據(jù)集,但它全面地描述了圖的力量。
比較數(shù)據(jù)模型
首先我們來(lái)看看我們各自數(shù)據(jù)庫(kù)的數(shù)據(jù)模型。與所有數(shù)據(jù)模型一樣,它們的外觀最終取決于您提出的問題類型。所以讓我們假設(shè)我們要問以下類型的問題:
- 一個(gè)人演過(guò)哪些電影?
- 一個(gè)人與哪些電影有關(guān)?
- 一個(gè)人曾經(jīng)合作過(guò)的所有合作演員是誰(shuí)?
基于這些,以下是相關(guān)的潛在數(shù)據(jù)模型:
電影圖的實(shí)體關(guān)系數(shù)據(jù)模型電影圖的屬性圖數(shù)據(jù)模型你會(huì)立刻發(fā)現(xiàn)一些東西——那些 ID 不見了!因?yàn)橐坏┪覀冎滥抢镉羞B接,我們就將數(shù)據(jù)連接在一起,我們不再需要它們,或者那些映射表來(lái)讓我們知道不同的數(shù)據(jù)行如何連接在一起。
比較查詢
現(xiàn)在讓我們繼續(xù)比較一些查詢。從:PLAY movies示例中選取一些最初的查詢,讓我們看一下 Cypher 查詢的一些并排比較,以及等效的 SQL 查詢是什么樣的。
什么是 Cypher,我聽到你問?Cypher 是一種圖查詢語(yǔ)言,用于查詢 Neo4j 圖數(shù)據(jù)庫(kù)。還有一個(gè)OpenCypher版本,許多其他供應(yīng)商都在使用它。
隨著我們進(jìn)行查詢,它應(yīng)該開始變得更加清晰,圖數(shù)據(jù)庫(kù)以及一種幫助探索關(guān)系的查詢語(yǔ)言是如何真正開始發(fā)揮作用的。讓我們開始尋找湯姆漢克斯吧!
如何找到湯姆漢克斯
MATCH (p:Person {name: "Tom Hanks"})
RETURN p
CypherSELECT * FROM person
WHERE person.name = "Tom Hanks"
SQL如何找到湯姆漢克斯的電影
MATCH (:Person {name: “Tom Hanks”})-->(m:Movie)
RETURN m.title
SELECT movie.title FROM movie
INNER JOIN movie_person ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
WHERE person.name = "Tom Hanks"
如何找到湯姆漢克斯導(dǎo)演的電影
MATCH (:Person {name: "Tom Hanks"})-[:DIRECTED]->(m:Movie)
RETURN m.title
SELECT movie.title FROM movie
INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE person.name = "Tom Hanks" AND involvement.title = "Director"
如何找到湯姆漢克斯的合作演員
MATCH (:Person {name: "Tom Hanks"})-->(:Movie)<-[:ACTED_IN]-(coActor:Person)
RETURN coActor.name
WITH tom_movies AS (
SELECT movie.movie_id FROM movie
INNER JOIN person_movie ON movie.movie_id = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
WHERE person.name = "Tom Hanks")
SELECT person.name FROM person
INNER JOIN person_movie ON tom_movies = person_movie.movie_id
INNER JOIN person ON person_movie.person_id = person.person_id
INNER JOIN involvement ON person_movie.involve_id = involvement.involve_id
WHERE involvement.title = "Actor"
使用 Cypher 進(jìn)行更多查詢
希望您能了解 Cypher 和 SQL 查詢之間的差異。也許您也很高興了解更多關(guān)于它們的信息!我們將在博客文章中進(jìn)一步提供一些參考。
現(xiàn)在,讓我們看看您可以在:PLAY movies圖表示例中找到的其他一些 Cypher 查詢,并解釋發(fā)生了什么。
沒有典型的培根數(shù)問題,任何電影圖表都是不完整的,我們的電影圖表也不例外!
到目前為止,我們看到的例子每次都遍歷一個(gè)關(guān)系。我們可以輕松地利用這些“寫入時(shí)連接”來(lái)遍歷許多關(guān)系來(lái)回答有趣的問題。
所以,回到凱文培根的數(shù)字。以下查詢將從 Kevin Bacon 人物節(jié)點(diǎn)開始,然后從該起點(diǎn)出發(fā)最多 4 跳,以帶回所有連接的電影和人物。
MATCH (bacon:Person {name:"Kevin Bacon"})-[*1..4]-(hollywood)
RETURN DISTINCT hollywood
我們可以通過(guò)使用*1..4查詢模式的關(guān)系部分的語(yǔ)法來(lái)做到這一點(diǎn):
- * 表示一切
- 1..4 表示范圍 - 1 表示距離 1 跳,4 表示最多 4 跳
我們可以在這個(gè)電影數(shù)據(jù)集上做的另一件事是兩個(gè)節(jié)點(diǎn)之間的最短路徑。
在這個(gè)例子中,讓我們找出Kevin Bacon 和 Meg Ryan 之間的最短路徑。您會(huì)發(fā)現(xiàn)我們*再次將語(yǔ)法用于關(guān)系模式——指示一切。
對(duì)您來(lái)說(shuō)可能是新的東西是p=. 您已經(jīng)看到我們?nèi)绾问褂霉?jié)點(diǎn)的引用(例如bacon或meg在我們當(dāng)前的查詢中),并且我們可以對(duì)關(guān)系執(zhí)行相同的操作。
我們還可以對(duì)整個(gè)路徑(即所有涉及的節(jié)點(diǎn)和關(guān)系)進(jìn)行引用。我們?yōu)榇耸褂玫恼Z(yǔ)法是refName =,在本例中是p=。
我們還使用 Cypher 函數(shù)shortestPath()——這是一個(gè)簡(jiǎn)單的最短路徑函數(shù),它將返回兩個(gè)指定節(jié)點(diǎn)之間的第一個(gè)最短路徑。請(qǐng)注意,可能還有另一條同樣短的路徑,但這個(gè)簡(jiǎn)單的函數(shù)只會(huì)帶回遇到的第一個(gè)路徑。
對(duì)于那些對(duì)其他路徑相關(guān)功能感興趣的人,請(qǐng)查看 APOC 和 GDS 中可用的功能。
MATCH p=shortestPath(
(bacon:Person {name:"Kevin Bacon"})-[*]-(meg:Person {name:"Meg Ryan"}))
RETURN p
給大家一個(gè)警告:您可能會(huì)看到這一點(diǎn),[*]并試圖在沒有shortestPath()函數(shù)或1..4范圍約束的情況下運(yùn)行您的圖形。但這很可能會(huì)導(dǎo)致一些意想不到的事情。
在我們與 Kevin Bacon 和 Meg Ryan 的示例中,即使在這個(gè)非常小的數(shù)據(jù)集中只有 253 個(gè)關(guān)系,節(jié)點(diǎn)和關(guān)系之間的所有可能的路徑組合也很容易遇到 Bacon 和 Ryan 之間的數(shù)百萬(wàn)條不同的路徑。
當(dāng)使用*在你的關(guān)系作為查詢的一部分,非常謹(jǐn)慎使用!這個(gè)問題沒有提出最短路徑,因?yàn)楫?dāng)遇到比當(dāng)前識(shí)別的最短路徑更長(zhǎng)的潛在路徑時(shí),它會(huì)立即被丟棄。
一個(gè)簡(jiǎn)單的推薦查詢
這里有兩個(gè)查詢真正展示了圖形數(shù)據(jù)庫(kù)的強(qiáng)大功能,我們可以輕松地使用數(shù)據(jù)中的連接來(lái)提出一些建議。
在我們的第一個(gè)查詢中,我們正在為湯姆漢克斯尋找新的合作演員,以與他尚未合作的人合作。查詢通過(guò)以下方式執(zhí)行此操作:
- 首先,找到他已經(jīng)合作過(guò)的所有合作演員
- 然后,找出所有的co-actors的co-actors(簡(jiǎn)稱co-co-actors)
- 接下來(lái),我們要排除那些已經(jīng)和湯姆合作過(guò)的合作演員,并確保合作演員不是湯姆本人
- 最后,我們返回建議的合作演員姓名,我們將對(duì)其進(jìn)行排序,但與他們合作的合作演員的數(shù)量 - 與該合作演員合作的合作演員越多,建議越好。
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cocoActors)
WHERE NOT (tom)-[:ACTED_IN]->()<-[:ACTED_IN]-(cocoActors)
AND tom <> cocoActors
RETURN cocoActors.name AS Recommended, count(*) AS Strength
ORDER BY Strength DESC
太好了,所以我們找到了一些潛在的合作演員。在下一個(gè)查詢中,我們想推薦湯姆克魯斯作為湯姆漢克斯合作的潛在新合作演員。但是,誰(shuí)來(lái)介紹這些湯姆斯呢?回到我們?nèi)サ碾娪皥D表。
在這個(gè)查詢中,我們:
- 找到湯姆漢克斯的合作演員,然后找出哪些合作演員也和湯姆克魯斯合作過(guò)
- 然后我們將返回合作演員以及他們與湯姆漢克斯和湯姆克魯斯共同出演的電影
MATCH (tom:Person {name:"Tom Hanks"})-[:ACTED_IN]->(m)<-[:ACTED_IN]-(coActors),
(coActors)-[:ACTED_IN]->(m2)<-[:ACTED_IN]-(cruise:Person {name:"Tom Cruise"})
RETURN tom, m, coActors, m2, cruise