03 使用圖進(jìn)行數(shù)據(jù)建模

1 模型和目標(biāo)

建模是為了讓不規(guī)則的領(lǐng)域的一些具體方面變成結(jié)構(gòu)化的、可操縱的空間。對于事物實際存在的方式,并沒有一種天然的表達(dá)方式,我們只能有目的地選擇、抽象和簡化,一些方法能更好的滿足某個特定目標(biāo)。

圖建模與其他建模技術(shù)的不同之處在于其邏輯模型和物理模型之間有更加密切的關(guān)系。關(guān)系型數(shù)據(jù)管理技術(shù)背離了用自然的語言來描述領(lǐng)域:

  • 先將其表述成邏輯模型,然后生硬的將其轉(zhuǎn)換成物理模型
  • 轉(zhuǎn)換使得概念化的世界和模型的數(shù)據(jù)庫實例之間產(chǎn)生了語義失調(diào)
  • 圖數(shù)據(jù)庫建模這個分歧會明顯的縮小

2 帶標(biāo)簽的屬性圖模型

帶標(biāo)簽的屬性圖模型主要有以下幾個特征:

  • 由節(jié)點、聯(lián)系、屬性和標(biāo)簽組成
  • 節(jié)點上包含屬性
  • 節(jié)點上可以被打上一個或者多個標(biāo)簽
  • 聯(lián)系連接節(jié)點
    • 一個方向
    • 一個名字
    • 一個開始節(jié)點
    • 一個結(jié)束節(jié)點
  • 聯(lián)系也可以有屬性
    • 可以給圖算法提供元數(shù)據(jù)
    • 給聯(lián)系增加額外的語義(包括特權(quán)和權(quán)重)
    • 可以用于運行時的約束查詢

3 查詢圖:Cypher簡介

3.1 Cypher的理念

1570502605160.png

這個模式描述了3個有交集的朋友,用ASCII字符畫表達(dá)出來就是:

(emil)<-[:KNOWS]-(jim)-[:KNOWS]->(ian)-[:KNOWS]->(emil)

這個模式描述了一條路徑,它將一個叫jim的節(jié)點和兩外兩個叫ian和emil的節(jié)點連接起來,同時也將ian節(jié)點和emil節(jié)點連接起來。

要注意:ian、jim和emil是標(biāo)識符。標(biāo)識符可以讓我們在描述一個模式時,多次指向同一個節(jié)點(可以幫我們繞過查詢語句其實只有一個方向的事實【只能從左到右處理文本】),而示意圖可以從兩個防線展開。除了偶爾需要使用這種方式重復(fù)使用標(biāo)識符,整個語句的意圖仍然是清晰的。

示意圖一般傾向于使用特定節(jié)點或聯(lián)系的實例,而不是類或原型。即使是非常大的圖,也要用真實的節(jié)點和聯(lián)系來表示,只不過通常會選取較小的子圖來做示例。即我們更喜歡使用實例化需求來表示圖

一個Cypher查詢使用斷言將模式的一個或多個部分錨定到圖的具體位置上,然后通過縮小沒有被錨定的范圍來尋找附近的匹配

  • 實際的圖中的錨點和模式中的哪一部分綁定,是Cypher根據(jù)查詢中的標(biāo)簽和屬性斷言決定的
  • Cypher使用已有的索引、限制和斷言中的元信息來自動計算出結(jié)果
  • 還有少數(shù)情況,用它去幫助確定一些附加的線索

Cypher也是由子句組成的,最簡單的查詢包括:

  • 一個MATCH子句
  • 一個RETURN子句
  • 如下所示:
MATCH (a:Person {name:[Jim]})-[:KNOWS]->(b)-{:KNOWS}->{c}
RETURN b, c

3.2 MATCH

理論上來講,如下模式會在圖數(shù)據(jù)中出現(xiàn)多次:

(a)-[:KNOWS]->(b)-[:KNOWS]->(c)

如果用戶數(shù)據(jù)集比較大的話,可能會有很多相互的關(guān)系能夠匹配這個模式。

要想錨定這個查詢,我們需將它的一些部分先在圖上固定。

同樣,還可以用WHERE子句里的斷言來表示錨定:

MATCH (a:Person)-[:KNOWS]->(b)-[:KNOWS]->(c), (a)-[:KNOWS]->(c)
WHERE a.name='Jim'
RETURN b, c

3.3 RETURN

RETURN子句用來指明已經(jīng)匹配查詢的數(shù)據(jù)中,哪些節(jié)點、聯(lián)系和屬性是需要返回給客戶端的。

3.4 其他Cypher子句

  • WHERE : 提供過濾模式匹配結(jié)果的條件
  • CREATE和CREATE UNIQUE:用來創(chuàng)建節(jié)點和聯(lián)系
  • MERGE:保證給出的模式在圖中一定存在
    • 要么復(fù)用已經(jīng)存在的與斷言匹配的節(jié)點和聯(lián)系
    • 要么創(chuàng)建新的節(jié)點和聯(lián)系
  • DELETE:刪除節(jié)點、聯(lián)系以及屬性
  • SET:設(shè)置屬性值
  • FOREACH:對一個列表中的每個元素執(zhí)行更新操作
  • UNION:合并兩個或更多查詢的結(jié)果
  • WITH:鏈?zhǔn)讲樵儯耙粋€查詢的結(jié)果作為后一個查詢的條件。和Unix的管道命令很相似
  • START:在圖中制定一個或多個起始點,可以是節(jié)點、也可以是聯(lián)系。(已經(jīng)不推薦使用START了,更推薦在MATCH子句中指定錨點)

4 關(guān)系建模和圖建模對比

一個簡化的數(shù)據(jù)中心應(yīng)用部署如下所示:

1570581978511.png

作為這類系統(tǒng)的運維方,我們主要關(guān)心兩件事情:

  • 為了達(dá)到(或超越)服務(wù)水平協(xié)議,目前提供的功能用來確定單點故障的前瞻性分析,以及與可用性服務(wù)相關(guān)的,用于快速定位客戶投訴原因的回顧性分析。
  • 為消耗的資源計費,其中包括硬件成本、虛擬化、網(wǎng)絡(luò)供應(yīng)以及軟件開發(fā)和運營成本(因為這些都是我們可見的簡單的系統(tǒng)邏輯擴(kuò)展)。

當(dāng)構(gòu)建一個數(shù)據(jù)中心管理方案,我們希望用來存儲和查詢數(shù)據(jù)底層數(shù)據(jù)模型,能夠保證有效地解決以上兩個關(guān)心的問題。隨著應(yīng)用組合的變化、數(shù)據(jù)中心物理布局的發(fā)展、或是虛擬機(jī)實例的遷移,我們希望底層的模型也能隨之更新。有了這些需求和限制,在看看關(guān)系建模和圖建模有什么區(qū)別。

4.1 系統(tǒng)管理領(lǐng)域中的關(guān)系建模

關(guān)系世界建模尋求對這一領(lǐng)域?qū)嶓w的理解,它們是如何相關(guān)聯(lián)的,以及它們狀態(tài)變化的規(guī)律。大多是非正式的(可能是草稿、討論),其示意圖如下:

1570693477513.png

E-R圖也是一種圖,盡管它可以像圖數(shù)據(jù)庫一樣給聯(lián)系命名,但在兩個實體之間,E-R圖只允許建立一條無向的命名的聯(lián)系。而真實世界的實體之間的聯(lián)系豐富多彩,種類繁多,關(guān)系模型無法滿足。

找到合適的邏輯模型之后,將它映射成表和關(guān)系,這種規(guī)范化過程可以消除數(shù)據(jù)的冗余。很多情況下,就類似于將E-R圖用表格形式撰寫一遍,然后用SQL命令把這些表格加載到數(shù)據(jù)庫中。這時一大批出乎預(yù)料的復(fù)雜度悄悄靠近了設(shè)計的模型,如:

  • 一對多聯(lián)系的外鍵約束(標(biāo)記為[FK])
  • 支持多對多聯(lián)系的連接表(AppDatabase)
  • 當(dāng)然模型中沒有重復(fù)的數(shù)據(jù)
1570694322351.png

關(guān)系范型帶來的挑戰(zhàn)之一是這些規(guī)范化的模型通常都應(yīng)付不了真實世界中需求變化的速度。

  • 理論上規(guī)范化的模式應(yīng)該能夠回答各種突發(fā)奇想的與領(lǐng)域有關(guān)的問題,規(guī)范化的模式還必須為具體的訪問模式做專門的特殊化處理;

  • 為了讓關(guān)系型數(shù)據(jù)庫在處理常規(guī)應(yīng)用請求時表現(xiàn)良好,需要接受修改用戶數(shù)據(jù)模型是為了適應(yīng)數(shù)據(jù)庫引擎而不是用戶這個現(xiàn)實,即反規(guī)范化(denormalization)

    • 為了獲得查詢性能,反規(guī)范化在某些情況下,會人為制造重復(fù)數(shù)據(jù)
    • 為了獲得最佳效果,會把一個標(biāo)準(zhǔn)模型轉(zhuǎn)換成一個反規(guī)范化模型,使其迎合底層RDBMS以及物理存儲層的特性,這個過程會有大量的數(shù)據(jù)冗余產(chǎn)生
    • “設(shè)計--> 規(guī)范化 --> 反規(guī)范化”看似可以接受,因為這是一次性的工作。但建造一個高性能的關(guān)系模型所花費的精力相對于開發(fā)整個項目的工作來說只是較小的一部分,很多時候系統(tǒng)變更不只發(fā)生在開發(fā)過程中,有時候產(chǎn)品上線后,需求還會變更
    • 很多人假設(shè)系統(tǒng)上線后會使用很長時間,且這段時間內(nèi),生產(chǎn)環(huán)境都是穩(wěn)定的。這個假設(shè)前半段是對的,但問題在于他們很少有穩(wěn)定的時候。隨著業(yè)務(wù)需求的改變和政策法規(guī)的逐步發(fā)展,系統(tǒng)和底層的數(shù)據(jù)結(jié)構(gòu)必然也要隨之改變
    • 項目的設(shè)計和開發(fā)階段,數(shù)據(jù)模型總會經(jīng)歷翻天覆地的變化,幾乎所有的變化都是為了使模型適應(yīng)應(yīng)用程序的需求以便上線后可以使用,一旦上線,應(yīng)用程序或是數(shù)據(jù)模型再想增加一些違背最初設(shè)計

    將結(jié)構(gòu)變化引入到數(shù)據(jù)庫的技術(shù)機(jī)制叫做遷移(migration)。遷移提供了一種結(jié)構(gòu)化的、步進(jìn)式的方式對數(shù)據(jù)庫進(jìn)行重構(gòu),這樣一來數(shù)據(jù)庫可以有效的響應(yīng)應(yīng)用程序的變化。

    • 代碼重構(gòu)只需幾秒鐘或幾分鐘就可以完成,但數(shù)據(jù)庫重構(gòu)不但耗時,并且風(fēng)險高、成本高
    • 反規(guī)范化模型的問題是它阻礙了系統(tǒng)業(yè)務(wù)需求的快速發(fā)展,為了適應(yīng)關(guān)系模型,對關(guān)系模型強(qiáng)加了很多變化,使得概念模型和數(shù)據(jù)真實的物理布局之間產(chǎn)生了鴻溝
      • 從業(yè)務(wù)相關(guān)方的角度來看,因為這些強(qiáng)加的變化,使得業(yè)務(wù)相關(guān)方無法再參與到系統(tǒng)進(jìn)一步的發(fā)展中
      • 從開發(fā)的角度,困難來源于業(yè)務(wù)需求的變化轉(zhuǎn)換為底層的穩(wěn)固的關(guān)系結(jié)構(gòu),使得系統(tǒng)的演化遠(yuǎn)遠(yuǎn)落后于業(yè)務(wù)的發(fā)展
    • 沒有專家的幫助和謹(jǐn)慎的規(guī)劃,遷移一個反規(guī)范化的數(shù)據(jù)庫存在很多風(fēng)險
      • 就是遷移完成后,如果沒能和存儲保持良好的關(guān)系,性能就會出問題
      • 一些曾經(jīng)故意制造的冗余數(shù)據(jù)成了沒人要的“孤兒”,這又帶來數(shù)據(jù)完整性的風(fēng)險

4.2 系統(tǒng)管理領(lǐng)域中的圖建模

圖建模的目的:

  • 可以和領(lǐng)域保持高度一致,且不用犧牲性能
  • 支撐業(yè)務(wù)發(fā)展的同時,可以在快速的變化和增長之中保持?jǐn)?shù)據(jù)的完整性的模型

圖建模的工作方法:

  • 第一步:與關(guān)系建模一樣,用低保真的方法來描述領(lǐng)域并統(tǒng)一意見,比如用白板上的草圖;
  • 第二步:關(guān)系建模是將圖轉(zhuǎn)換成表格,圖建模目的是生成一個和應(yīng)用目標(biāo)相關(guān)的領(lǐng)域部分的精確呈現(xiàn)。及領(lǐng)域中的每個實體,
    • 確保將其相關(guān)角色轉(zhuǎn)換成標(biāo)簽
    • 特性轉(zhuǎn)換成屬性
    • 與鄰近實體之間的關(guān)系轉(zhuǎn)換為聯(lián)系

數(shù)據(jù)中心的例子,最后得出的圖模型如下:

1570786586881.png

從邏輯上來說,不需要表,也不需要規(guī)范化和反規(guī)范化。一旦得到了領(lǐng)域模型的精確表示,把他放到數(shù)據(jù)庫里簡直小菜一碟。

4.3 測試模型

測試模型是為了驗證領(lǐng)域模型是否能適應(yīng)真實的查詢。

  • 一些設(shè)計決策像烙印一樣烙進(jìn)我們的程序里,阻礙我們進(jìn)一步的發(fā)展
  • 需要事先檢查領(lǐng)域模型和建立的圖模型,這樣圖結(jié)構(gòu)的變化就谷安考業(yè)務(wù)變化驅(qū)動,而不用為了糟糕的設(shè)計做數(shù)據(jù)遷移

驗證的方式:

  • 根據(jù)圖的節(jié)點沿著路徑看讀的句子是否邏輯正確,如“應(yīng)用實例1使用了數(shù)據(jù)庫實例123,分布在服務(wù)器a,b上”
  • 可查詢的設(shè)計:驗證圖是可以支持哪些我們想要的查詢,必須要描述出這些查詢。舉例:某個用戶使用某個云服務(wù)的時候出現(xiàn)問題,就需要搜索出用戶和這個程序之間的路徑,以及該程序運行所依賴的資源。

5 跨域模型

商業(yè)洞察力往往依賴于我們對復(fù)雜的價值鏈背后的網(wǎng)絡(luò)效應(yīng)的理解。為了達(dá)到一定程度的理解,需要聯(lián)合多個領(lǐng)域,又不能讓每個領(lǐng)域的細(xì)節(jié)失真或者犧牲掉。

  • 屬性圖可以給一個價值鏈建模,使其成為一個圖的集合,
  • 每張圖都有具體的聯(lián)系關(guān)聯(lián)其子域,又能將它們區(qū)別開來

5.1 創(chuàng)建莎士比亞圖

1571122593315.png
  • 短線表示:文學(xué)領(lǐng)域
  • 實線表示:喜劇領(lǐng)域
  • 長虛線表示:地理信息領(lǐng)域

創(chuàng)建腳本如下所示:

CREATE (shakespare:Author {firstname:'William', lastname:'Shakespeare'}),
       (juliusCaesar:Play {title:'Julius Caesar'}),
       (shakespare)-[:WROTE_PLAY {year:'1599'}]->(juliusCaesar),
       (theTempest:Play {title: 'The Tempest'}),
       (shakespare)-[:WROTE_PLAY {year:'1610'}]->(theTempest),
       (rsc:Company {name:'RSc'}),
       (production1:Production {name:'Julius Caesar'}),
       (rsc)-[:PRODUCED]->(production1),
       (production1)-[:PRODUCTION_OF]->(juliusCaesar),
       (performance1:Performance {date:'20120729'}),
       (performance1)-[:PERFORMANCE_OF]->(production1),
       (production2:Production {name:'The Tempest'}),
       (rsc)-[:PRODUCED]->(production2),
       (production2)-[:PRODUCTION_OF]->(theTempest),
       (performance2:Performance {date:'20061121'}),
       (performance2)-[:PERFORMANCE_OF]->(production2),
       (performance3:Performance {date:'20120730'}),
       (performance3)-[:PERFORMANCE_OF]->(production1),
       (billy:User {name:'Billy'}),
       (review:Review {rating:5, review:'This was awsome!'}),
       (billy)-[:WROTE_REVIEW]->(review),
       (review)-[:RATED]->(performance1),
       (theatreRoyal:Venue {name:'Theatre Royal'}),
       (performance1)-[:VENUE]->(theatreRoyal),
       (performance2)-[:VENUE]->(theatreRoyal),
       (performance3)-[:VENUE]->(theatreRoyal),
       (greyStreet:Street {name:'Grey Street'}),
       (theatreRoyal)-[:Street]->(greyStreet),
       (newcastle:City {name:'Newcastle'}),
       (greyStreet)-[:CITY]->(newcastle),
       (tyneAndWear:County {name:'Tyne and Wear'}),
       (newcastle)-[:COUNTY]->(tyneAndWear),
       (england:Country {name:'England'}),
       (tyeAndWear)-[:COUNTRY]->(england),
       (stratford:City {name:'Stratford upon Avon'}),
       (stratford)-[:COUNTRY]->(england),
       (rsc)-[:BASED_IN]->(stratford),
       (shakespeare)-[:BORN_IN]->(stratford)

上面的語句做了兩件事:

  • 創(chuàng)建帶有標(biāo)簽的節(jié)點(以及它們的屬性)
  • 將節(jié)點用聯(lián)系連接起來(在需要的地方使用聯(lián)系屬性)

? 標(biāo)識符(shakespeare)幫助我們將聯(lián)系和這個基礎(chǔ)節(jié)點相連。標(biāo)識符在當(dāng)前的查詢范圍內(nèi)是可用的,但是出了這個范圍就不行了。如果想節(jié)點或是聯(lián)系一個可以長久使用的名字,需要為特定的標(biāo)簽或者屬性組合創(chuàng)建索引

5.2 開始查詢

通常從一個或多個熟悉的起始點開始查詢,也就是所謂的“綁定”節(jié)點。

  • Cypher使用MATCH和WHERE子句里任意的標(biāo)簽和屬性謂詞
  • 結(jié)合索引和約束提供的元數(shù)據(jù)

一起來尋找錨定這個圖模式 的開始點

舉例:

  • 假設(shè)要找所有紐卡斯?fàn)柕幕始覄≡貉莩鲞^莎士比亞戲劇:

    • 作者(Author):莎士比亞

    • 地點(Venue):皇家劇院

    • 城市(City):紐卡斯?fàn)?/p>

    • 如下:

      MATCH (theater:Venue {name:'Theatre Royal'}),
          (newcastle:City {name:'Newcastle'}),
          (bard:Author {lastname:'Shakespeare'})
      

?

5.3 聲明查找的信息模式

例子:找到所有在紐卡斯?fàn)柕幕始覄≡貉莩龅纳勘葋啈騽。?/p>

MATCH (theater:Venue {name:'Theatre Royal'}),
      (newcastle:City {name:'Newcastle'}), 
      (bard:Author {lastname:'Shakespeare'}),
      (newcastle)<-[:Street|CITY*1..2]-(theater)
      <-[:VENUE]-()-[:PERFORMANCE_OF]->()
      -[:PRODUCTION_OF]->(play)<-[:WROTE_PLAY]-(bard)
RETURN DISTINCT play.title AS play

上述MATCH模式用了幾個特殊的語法元素:

  • 通過指定的節(jié)點和屬性值,標(biāo)識符newcastle、theater和bard都被錨定到圖中真實的節(jié)點
  • 數(shù)據(jù)庫中有多個皇家劇院,比如:普利茅斯、巴斯、溫徹斯特和諾里奇這些城市都有一個皇家劇院,那么theater會綁定到所有的這些節(jié)點上。為了進(jìn)一步找到在紐卡斯?fàn)柕幕始覄≡海黾恿?lt;-[:Street|CITY*1..2]-,這句的意思是劇院的節(jié)點和紐卡斯?fàn)柕墓?jié)點之間不應(yīng)該超過兩條以上的Street或CITY的聯(lián)系。通過提供一個可變的路徑長度,允許細(xì)粒度的地址分出層次(例如由街、區(qū)或自治區(qū)和市構(gòu)成的地址)
  • (theater)<-[:VENUE]-()使用了匿名節(jié)點,即空括號。因為了解數(shù)據(jù),所以知道匿名節(jié)點將會匹配到一些演出,但是對每場演出的具體細(xì)節(jié)并不關(guān)心。在查詢的其他地方也不會用到,所以就沒有把他綁定到任何標(biāo)識符上。
  • 在將演出連接到作品時,又一次用到了匿名節(jié)點()-[:PERFORMANCE_OF]->()。如果對這些演出或是原著有興趣,也可以使用標(biāo)識符來替代匿名節(jié)點
  • MATCH的剩余部分就是一個簡單的“節(jié)點到聯(lián)系到節(jié)點”的模式:(play)<-[:WROTE_PLAY]-[bard]。保證查詢只返回莎士比亞所寫的戲劇。因為(play)連接到匿名的作品節(jié)點,然后通過連接到的演出節(jié)點,可以有把握的推斷這部喜劇應(yīng)該在紐卡斯?fàn)柕幕始覄≡貉莩鲞^。把這個戲劇節(jié)點命名后,就可以在后面的查詢里用到這個標(biāo)識符

5.4 約束匹配

WHERE子句可以限制圖查詢,基于以下規(guī)則:

  • 匹配的子圖中必須有(或者沒有)限制的路徑
  • 節(jié)點必須有指定的標(biāo)簽或者指定名字的聯(lián)系
  • 在匹配的節(jié)點或聯(lián)系上的某個屬性必須有(或者沒有)特定的屬性,無論它們的值是什么
  • 在匹配的節(jié)點或聯(lián)系上的某個屬性必須有特定的屬性值
  • 必須能滿足其他任意復(fù)雜的表達(dá)式斷言(如:在某個特定的日期或者之前必須有演出上演)

例子:如果需要將結(jié)果的范圍縮小到莎士比亞晚期的戲劇,通常是指1608年前后,這個可以通過過濾WROTE_PLAY聯(lián)系上的year屬性來達(dá)到目的。調(diào)整語句如下所示:

MATCH (theater:Venue {name:'Theatre Royal'}),
      (newcastle:City {name:'Newcastle'}), 
      (bard:Author {lastname:'Shakespeare'}),
      (newcastle)<-[:Street|CITY*1..2]-(theater)
      <-[:VENUE]-()-[:PERFORMANCE_OF]->()
      -[:PRODUCTION_OF]->(play)<-[w:WROTE_PLAY]-(bard)
WHERE toInt(w.year) > 1608
RETURN DISTINCT play.title AS play

上面的語句還用到了如下語法:

  • toInt(w.year) > 1608 將字符串轉(zhuǎn)換車數(shù)值,再進(jìn)行比較
  • 當(dāng)然也可以 w.year > '1608' 進(jìn)行比較,但是數(shù)字的字符串最好轉(zhuǎn)換為數(shù)值進(jìn)行比較,所以如果字符串本身就是數(shù)值,最好以數(shù)值的形式存儲在數(shù)據(jù)庫中

舉例:前面將演出的year字段設(shè)置成了字符串,現(xiàn)在需要將其改成年份

MATCH (bard:Author {lastname:'Shakespeare'}),
      p =  (play)<-[w:WROTE_PLAY]-(bard)
SET w.year = toInt(w.year)
RETURN p          

5.5 處理結(jié)果

  • RETURN DISTINCT play.title AS play:其中DISTINCT 保證返回的結(jié)果是唯一的

  • RETURN count(play):統(tǒng)計演出的次數(shù)

  • 如果根據(jù)演出次數(shù)對結(jié)果排序,可以對PERFORMANCE_OF綁定一個標(biāo)識p,然后對這個p進(jìn)行計數(shù)(count)和排序,如下:

    MATCH (theater:Venue {name:'Theatre Royal'}),
          (newcastle:City {name:'Newcastle'}), 
          (bard:Author {lastname:'Shakespeare'}),
          (newcastle)<-[:Street|CITY*1..2]-(theater)
          <-[:VENUE]-()-[p:PERFORMANCE_OF]->()
          -[:PRODUCTION_OF]->(play)<-[:WROTE_PLAY]-(bard)
    RETURN  play.title AS play, count(p) AS performance_count
    ORDER BY performance_count DESC
                      
    

5.6 查詢鏈

有時候一個MATCH得到一切是不可能的。WITH子句允許將幾個匹配連接到一起,將前一個查詢的結(jié)果當(dāng)做條件輸送到下一個查詢中。

舉個例子:查詢一些莎士比亞的戲劇,然后按照寫作的年份將它們排序,年代最近的排在最前面。使用WITH子句,將結(jié)果輸送到RETURN子句里,它使用collect函數(shù)輸出一個用逗號分隔的戲劇名稱列表

MATCH (bard:Author {lastname:'Shakespeare'}) -[w:WROTE_PLAY]->(play)
WITH play
ORDER BY w.year ASC
RETURN collect(play.title) AS plays

上面的語句中需要先對play進(jìn)行排序,然后再對play進(jìn)行collect處理。

WITH可以用來將只讀子句從以寫入為中心的SET操作中分離出來。

WITH通過可以把復(fù)雜的查詢分解成多個簡單模式,將復(fù)雜的查詢分而治之。

6 建模時常見的陷阱

雖然圖建模是掌握復(fù)雜的問題域的一種極具表現(xiàn)力的方式,但只有表現(xiàn)力并不能保證每一個圖都適合其用途。下面說一些常見的有問題的模型

6.1 電子郵件起源問題域

信息交流模式分析是一個經(jīng)典的圖問題,涉及用途去發(fā)現(xiàn)領(lǐng)域?qū)<摇㈥P(guān)鍵影響力以及信息傳播的通信通道。但在這個場景下,我們尋找的是一個壞蛋,而不是正面的榜樣或是專家:就是可疑的電子郵箱通信模式,很可能是違反公司規(guī)定,甚至是違法。

6.2 敏感的第一個迭代

早期的模型,如下所示:

CREATE (alice:User {username:'Alice'}),
       (bob:User {username:'Bob'}), 
       (charlie:User {username:'Charlie'}), 
       (davina:User {username:'Davina'}), 
       (edward:User {username:'Edward'}), 
       (alice)-[:ALIAS_OF]->(bob)

該語句中很容易看出“Alice是Bob的一個別名”。如下圖所示:

1571310979866.png

!

接下來用他們曾經(jīng)相互發(fā)送過的電子郵件記錄來把用戶連接起來

MATCH (bob:User {username:'Bob'}), 
       (charlie:User {username:'Charlie'}), 
       (davina:User {username:'Davina'}), 
       (edward:User {username:'Edward'})
CREATE (bob)-[:EMAILED]->(charlie),
       (bob)-[:CC]->(charlie),
       (bob)-[:BCC]->(charlie),

這種描述乍一看合理且忠實于領(lǐng)域的表示方式。從上面的圖可以看到“Bob給Charlie發(fā)了電子郵件”,但Bob到底在電子郵件中寫了什么,雖然我們可以看到的是Bob抄送或是密件抄送了一些人,但看不到最重要的東西:電子郵件本身。如下圖所示:

1571318949732.png

運行下面的查詢時,這個圖結(jié)構(gòu)帶來的信息缺失顯得尤為明顯:

MATCH (bob:User {username:'Bob'})-[e:EMAILED]->
       (charlie:User {username:'Charlie'})
RETURN e

這個查詢返回了Bob和Charlie之間的EMAILED聯(lián)系。只讓我們知道有電子郵件交流,而不能告訴我們電子郵件是什么,如下所示:

1571319365142.png

也許在EMAILED聯(lián)系上加一些代表電子郵件特性的屬性就可以挽救局面,但那其實只是在拖延時間,我們還是無法知道EMAILED、CC和BCC這些關(guān)系之間是如何相互作用的,也就是說不清楚哪些電子郵件是抄送的,哪些是密件抄送的,以及它們都是發(fā)給誰的。

丟掉了電子郵件節(jié)點就丟掉了信息

6.3 第二次魅力

要修復(fù)這個有缺失的模型,需要加入電子郵件節(jié)點來代表在業(yè)務(wù)中來往的電子郵件,并擴(kuò)展聯(lián)集包含所有電子郵件支持的地址信息。替換掉這個有結(jié)構(gòu)缺失的語句:

CREATE (bob)-[:EMAILED]->(charlie)

用如下語句創(chuàng)建包含更多細(xì)節(jié)的結(jié)構(gòu):

CREATE (email_1:Email{id:'1', content:'Hi Charlie,.... Kind regards, Bob'}),
       (bob)-[:SENT]->(email_1),
       (email_1)-[:TO]->(charlie),
       (email_1)-[:TO]->(davina),
       (email_1)-[:CC]->(alice),
       (email_1)-[:BCC]->(Edward)
基于電子郵件的星狀圖

6.4 發(fā)展中的領(lǐng)域

  • 在圖庫中,更傾向于添加新的節(jié)點和聯(lián)系來增加信息或者成分,而不是修改已有的模型。這樣做不會影響已有的查詢,并且是完全安全的

  • 使用已有的聯(lián)系類型對圖做修改,或者修改現(xiàn)有節(jié)點的屬性(不僅僅是屬性值)有可能是安全的,但是我們需要運行一些有代表性的查詢來增強(qiáng)我們的信心,來告訴我們即時圖的結(jié)構(gòu)改變了,仍然可用

7 辨別節(jié)點和聯(lián)系

建模的過程可以非常合適地總結(jié)為用圖結(jié)構(gòu)來表達(dá)想問我們的領(lǐng)域的問題。也就是說,我們所說的面向可查詢的設(shè)計:

  1. 描述驅(qū)動模型的客戶端或者最終用戶的目標(biāo)
  2. 把這些目標(biāo)轉(zhuǎn)述成要問的領(lǐng)域問題
  3. 明確這些問題中出現(xiàn)實體和聯(lián)系
  4. 把這些聯(lián)系和實體翻譯成Cypher的路徑表達(dá)方式
  5. 用圖的模式來表達(dá)我們想問的領(lǐng)域問題,使用路徑表達(dá)式,就如同我們建模這個領(lǐng)域時一樣

通過拷問用來描述領(lǐng)域的語言,可以快速明確圖中的核心元素:

  • 常用的名字可以作為標(biāo)簽,如:user、email,變成標(biāo)簽User和Email
  • 帶有賓語的動詞可以作為聯(lián)系名稱,如:sent、wrote,變成SENT和WROTE
  • 一個合適的名詞(比如人們或者公司名)指代一樣?xùn)|西的實體,就把他建模為節(jié)點,用一個或是多個屬性來記錄他的特點

8 避免反模式

  • 不要把實體建模成聯(lián)系,應(yīng)用使用聯(lián)系來傳達(dá)實體之間如何相連的,以及這些聯(lián)系的性質(zhì);
  • 動詞名詞話(一個名詞可以借此轉(zhuǎn)型為動詞的語言習(xí)慣)經(jīng)常影藏了名詞的出現(xiàn)以及相應(yīng)的領(lǐng)域?qū)嶓w;
  • 理解圖的天然可擴(kuò)展性,增加一些領(lǐng)域?qū)嶓w以及增加在它們之間使用新的節(jié)點和新的聯(lián)系進(jìn)行連接的方式,這些看起來像是往數(shù)據(jù)里猛灌大量的數(shù)據(jù),但其實是很自然的事情
  • 在寫入數(shù)據(jù)時,為了保證查詢效率而混入數(shù)據(jù)元素是很糟糕的做法。
  • 按照向領(lǐng)域提出的問題來建模,就能得到一個精確表示領(lǐng)域的模型。確立這樣的數(shù)據(jù)模型,圖數(shù)據(jù)庫在讀的時候就能表現(xiàn)的很好。
  • 即使存儲了非常大量的數(shù)據(jù),圖數(shù)據(jù)庫的查詢速度依然良好。

當(dāng)我們學(xué)著去組織我們的圖并且不用去反規(guī)范化它們的時候,我們要學(xué)會去相信圖數(shù)據(jù)庫,這是很重要的。

9 小結(jié)

  1. 圖數(shù)據(jù)庫賦予了軟件開發(fā)人員用圖表達(dá)問題領(lǐng)域的能力,并可以在運行時查詢圖
    • 圖可以清晰的描述問題域;
    • 圖數(shù)據(jù)庫可以讓我們在存儲這種描述方式時得以保留領(lǐng)域和數(shù)據(jù)之間的親緣關(guān)系
    • 圖建模免去了使用復(fù)雜的數(shù)據(jù)管理代碼來規(guī)范化和反規(guī)范化數(shù)據(jù)這一步驟
  2. 我們創(chuàng)建的圖應(yīng)該讓那些查詢語句讀起來很順暢,同時要避免混雜實體和動作,在多個迭代中滿足系統(tǒng)的需求,同時也可以跟上代碼的演變;
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,238評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,430評論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,134評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,893評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 71,653評論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,136評論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,212評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,372評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,888評論 1 334
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,738評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,939評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,482評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,179評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,588評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,829評論 1 283
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,610評論 3 391
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 47,916評論 2 372

推薦閱讀更多精彩內(nèi)容