图数据库Neo4j全栈Web技术解密
注:作者的代表作品【Neo4j + D3.js 項(xiàng)目視頻教程】http://edu.51cto.com/course/11315.html?reci
Neo4j是什么?
圖1. Neo4j Web控制臺(tái)
Neo4j是一個(gè)圖形數(shù)據(jù)庫,這也就意味著它的數(shù)據(jù)并非保存在表或集合中,而是保存為節(jié)點(diǎn)以及節(jié)點(diǎn)之間的關(guān)系。在Neo4j中,節(jié)點(diǎn)以及關(guān)系都能夠包含保存值的屬性,此外:
可以為節(jié)點(diǎn)設(shè)置零或多個(gè)標(biāo)簽(例如Author或Book)
每個(gè)關(guān)系都對(duì)應(yīng)一種類型(例如WROTE或FRIEND_OF)
關(guān)系總是從一個(gè)節(jié)點(diǎn)指向另一個(gè)節(jié)點(diǎn)(但可以在不考慮指向性的情況下進(jìn)行查詢)
為什么要選擇Neo4j?
在考慮為web應(yīng)用選擇某個(gè)數(shù)據(jù)庫時(shí),我們需要考慮對(duì)它有哪些方面的期望,其中最重要的一些條件包括:
它是否易于使用?
它是否允許你方便地回應(yīng)對(duì)需求的變更?
它是否支持高性能查詢?
是否能夠方便地對(duì)其進(jìn)行數(shù)據(jù)建模?
它是否支持事務(wù)?
它是否支持大規(guī)模應(yīng)用?
它是否足夠有趣(很遺憾的是對(duì)于數(shù)據(jù)庫的這方面要求經(jīng)常被忽略)?
從這幾個(gè)方面來說,Neo4j是一個(gè)合適的選擇。Neo4j……
自帶一套易于學(xué)習(xí)的查詢語言(名為Cypher)
不使用schema,因此可以滿足你的任何形式的需求
與關(guān)系型數(shù)據(jù)庫相比,對(duì)于高度關(guān)聯(lián)的數(shù)據(jù)(圖形數(shù)據(jù))的查詢快速要快上許多
它的實(shí)體與關(guān)系結(jié)構(gòu)非常自然地切合人類的直觀感受
支持兼容ACID的事務(wù)操作
提供了一個(gè)高可用性模型,以支持大規(guī)模數(shù)據(jù)量的查詢,支持備份、數(shù)據(jù)局部性以及冗余
提供了一個(gè)可視化的查詢控制臺(tái),你不會(huì)對(duì)它感到厭倦的
什么時(shí)候不應(yīng)使用Neo4j?
作為一個(gè)圖形NoSQL數(shù)據(jù)庫,Neo4j提供了大量的功能,但沒有什么解決方案是完美的。在以下這些用例中,Neo4j就不是非常適合的選擇:
記錄大量基于事件的數(shù)據(jù)(例如日志條目或傳感器數(shù)據(jù))
對(duì)大規(guī)模分布式數(shù)據(jù)進(jìn)行處理,類似于Hadoop
二進(jìn)制數(shù)據(jù)存儲(chǔ)
適合于保存在關(guān)系型數(shù)據(jù)庫中的結(jié)構(gòu)化數(shù)據(jù)
在上面的示例中,你看到了由Author、City、Book和Category以及它們之間的關(guān)系所組成的一個(gè)圖形。如果你希望通過Cypher語句在Neo4j web控制臺(tái)中列出這些數(shù)據(jù)結(jié)果,可以執(zhí)行以下語句:
MATCH(city:City)<-[:LIVES_IN]-(:Author)-[:WROTE]->(book:Book)-[:HAS_CATEGORY]->(category:Category)WHERE?city.name?=?“Chicago”RETURN?*請(qǐng)注意這種ASCII風(fēng)格的語法,它在括號(hào)內(nèi)表示節(jié)點(diǎn)名稱,并用箭頭表示一個(gè)節(jié)點(diǎn)指向另一個(gè)節(jié)點(diǎn)的關(guān)系。Cypher通過這種方式允許你匹配某個(gè)指定的子圖形模式。
當(dāng)然,Neo4j的功能不僅僅在于展示漂亮的圖片。如果你希望按照作者所處的地點(diǎn)(城市)計(jì)算書籍的分類數(shù)目,你可以通過使用相同的MATCH模式,返回一組不同的列,例如:
MATCH(city:City)<-[:LIVES_IN]-(:Author)-[:WROTE]->(book:Book)-[:HAS_CATEGORY]->(category:Category)RETURN?city.name,?category.name,?COUNT(book)執(zhí)行這條語句將返回以下結(jié)果:
?
city.name | category.name | COUNT(category) |
Chicago | Fantasy | 1 |
Chicago | Non-Fiction | 2 |
雖然Neo4j也能夠處理“大數(shù)據(jù)”,但它畢竟不是Hadoop、HBase或Cassandra,通常來說不會(huì)在Neo4j數(shù)據(jù)庫中直接處理海量數(shù)據(jù)(以PB為單位)的分析。但如果你樂于提供關(guān)于某個(gè)實(shí)體及其相鄰數(shù)據(jù)關(guān)系(比如你可以提供一個(gè)web頁面或某個(gè)API返回其結(jié)果),那么它是一種良好的選擇。無論是簡(jiǎn)單的CRUD訪問,或是復(fù)雜的、深度嵌套的資源視圖都能夠勝任。
你應(yīng)該選擇哪種技術(shù)棧以配合Neo4j?
所有主流的編程語言都通過HTTP API的方式支持Neo4j,或者采用基本的HTTP類庫,或是通過某些原生的類庫提供更高層的抽象。此外,由于Neo4j是以Java語言編寫的,因此所有包含JVM接口的語言都能夠充分利用Neo4j中的高性能API。
Neo4j本身也提供了一個(gè)“技術(shù)棧”,它允許你選擇不同的訪問方式,包括簡(jiǎn)單訪問乃至原生性能等等。它提供的特性包括:
通過一個(gè)HTTP API執(zhí)行Cypher查詢,并獲取JSON格式的結(jié)果
一種“非托管擴(kuò)展”機(jī)制,允許你為Neo4j數(shù)據(jù)庫編寫自己的終結(jié)點(diǎn)
通過一個(gè)高層Java API指定節(jié)點(diǎn)與關(guān)系的遍歷
通過一個(gè)低層的批量加載API處理海量初始數(shù)據(jù)的獲取
通過一個(gè)核心Java API直接訪問節(jié)點(diǎn)與關(guān)系,以獲得最大的性能
一個(gè)應(yīng)用程序示例
最近我正好有機(jī)會(huì)將一個(gè)項(xiàng)目擴(kuò)展為基于Neo4j的應(yīng)用程序。該應(yīng)用程序(可以訪問graphgist.neo4j.com查看)是關(guān)于GraphGist的一個(gè)門戶網(wǎng)站。GraphGist是一種通過交互式地渲染(在你的瀏覽器中)生成的文檔,它基于一個(gè)簡(jiǎn)單的文本文件(AsciiDoctor),其中用文字描述以及圖片描述了整個(gè)數(shù)據(jù)模型、架構(gòu)以及用例查詢,可以在線執(zhí)行它們,并使它們保持可視化。它非常類似一個(gè)iPython?notebook或是一張交互式的白紙。GraphGist也允許讀者在瀏覽器中編寫自己定義的查詢,以查看整個(gè)數(shù)據(jù)集。
Neo4j的原作者Neo Technology希望為GraphGist提供一個(gè)由社區(qū)創(chuàng)建的展示項(xiàng)目。當(dāng)然,后端技術(shù)選用了Neo4j,而整個(gè)技術(shù)棧的其余部分,我的選擇是:
Node.js配合Express.js,其中引入了neo4j包
Angular.js
Swagger UI
所有代碼都已開源,可以在GitHub上任意瀏覽。
從概念上講,GraphGist門戶網(wǎng)站是一個(gè)簡(jiǎn)單的應(yīng)用,它提供了一個(gè)GraphGist列表,允許用戶查看每個(gè)GraphGist的詳細(xì)內(nèi)容。數(shù)據(jù)領(lǐng)域是由Gist、Keyword/Domain/Use Case(作為Gist分類)以及Person(作為Gist的作者)所組成的:
現(xiàn)在你已經(jīng)熟悉這個(gè)模型了,在繼續(xù)深入學(xué)習(xí)之前,我想為你快速地介紹一下Cypher這門查詢語言。舉例來說,如果我們需要返回所有的Gist和它們的關(guān)鍵字,可以通過以下語句實(shí)現(xiàn):
MATCH?(gist:Gist)-[:HAS_KEYWORD]->(keyword:Keyword) RETURN?gist.title,?keyword.name這段語句將返回一張表,其中的每一行是由每個(gè)Gist和Keyword的組合構(gòu)成的,正如同SQL join的行為一樣。現(xiàn)在我們更深入一步,假設(shè)我們想要找到某個(gè)人所編寫的Gist對(duì)應(yīng)的所有Domain,我們可以執(zhí)行下面這條查詢語句:
MATCH?(person:Person)-[:WRITER_OF]->(gist:Gist)-[:HAS_DOMAIN]->(domain:Domain) WHERE?person.name?=?“John?Doe” RETURN?domain.name,?COUNT(gist)該語句將返回另一個(gè)結(jié)果表,其中的每一行包含Domain的名稱,以及這個(gè)Person對(duì)于這一Domain所編寫的全部Gist的數(shù)量。這里無需使用GROUP BY語句,因?yàn)楫?dāng)我們使用例如COUNT()這樣的聚合函數(shù)時(shí),Neo4j會(huì)自動(dòng)在RETURN語句中對(duì)其它列進(jìn)行分組操作。
現(xiàn)在你對(duì)Cypher已經(jīng)有一點(diǎn)感覺了吧?那么讓我們來看一個(gè)來自實(shí)際應(yīng)用中的查詢。在創(chuàng)建這個(gè)門戶時(shí),如果能夠通過某種方式,只需對(duì)數(shù)據(jù)庫進(jìn)行一次請(qǐng)求就能夠返回我們所需的所有數(shù)據(jù),并且以一種我們需要的格式進(jìn)行結(jié)構(gòu)組織,那將十分有用。
讓我們開始創(chuàng)建這個(gè)用于門戶的API(可以在GitHub上找到)的查詢吧。首先,我們需要按照Gist的title屬性進(jìn)行匹配,并匹配所有相關(guān)的Gist節(jié)點(diǎn):
//?Match?Gists?based?on?titleMATCH?(gist:Gist)?WHERE?gist.title?=~?{search_query}//?Optionally?match?Gists?with?the?same?keyword//?and?pass?on?these?related?Gists?with?the//?most?common?keywords?firstOPTIONAL?MATCH?(gist)-[:HAS_KEYWORD]->(keyword)<-[:HAS_KEYWORD]-(related_gist)這里有幾個(gè)要注意的地方。首先,WHERE語句是通過一個(gè)正則表達(dá)式(即=~操作符)和一個(gè)參數(shù)對(duì)title屬性進(jìn)行匹配的。參數(shù)(Parameter)是Neo4j的一項(xiàng)特性,它能夠?qū)⒉樵兣c其所代表的數(shù)據(jù)進(jìn)行分離。使用參數(shù)能夠讓Neo4j對(duì)查詢和查詢計(jì)劃進(jìn)行緩存,這也意味著你無需擔(dān)心遭遇查詢注入***。其次,我們?cè)谶@里使用了一個(gè)OPTIONAL MATCH語句,它表示我們希望始終返回原始的Gist,即使它并沒有相關(guān)的Gist。
現(xiàn)在讓我們對(duì)之前的查詢進(jìn)行擴(kuò)展,將RETURN語句替換為WITH語句:
MATCH?(gist:Gist)?WHERE?gist.title?=~?{search_query}OPTIONAL?MATCH?(gist)-[:HAS_KEYWORD]->(keyword)<-[:HAS_KEYWORD]-(related_gist)WITH?gist,?related_gist,?COUNT(DISTINCT?keyword.name)?AS?keyword_countORDER?BY?keyword_count?DESCRETURNgist,COLLECT(DISTINCT?{related:?{?id:?related_gist.id,?title:? related_gist.title,?poster_p_w_picpath:?related_gist.poster_p_w_picpath,?url:? related_gist.url?},?weight:?keyword_count?})?AS?related在RETURN語句中的COLLECT()作用是將由Gist和相關(guān)Gist所組成的節(jié)點(diǎn)轉(zhuǎn)換為一個(gè)結(jié)果集,讓其中每一行Gist只出現(xiàn)一次,并對(duì)應(yīng)一個(gè)相關(guān)Gist的節(jié)點(diǎn)數(shù)組。在COLLECT()語句中,我們?cè)谙嚓P(guān)Gist中僅指定了所需的部分?jǐn)?shù)據(jù),以減小整個(gè)響應(yīng)的大小。
最后,我們將產(chǎn)生這樣一條查詢語句,這也是最后一次使用WITH語句了:
MATCH?(gist:Gist)?WHERE?gist.title?=~?{search_query}OPTIONAL?MATCH?(gist)-[:HAS_KEYWORD]->(keyword)<-[:HAS_KEYWORD]-(related_gist)WITH?gist,?related_gist,?COUNT(DISTINCT?keyword.name)?AS?keyword_countORDER?BY?keyword_count?DESCWITHgist,COLLECT(DISTINCT?{related:?{?id:?related_gist.id,?title:?related_gist.title,?poster_p_w_picpath:?related_gist.poster_p_w_picpath,?url:?related_gist.url?},?weight:?keyword_count?})?AS?related//?Optionally?match?domains,?use?cases,?writers,?and?keywords?for?each?GistOPTIONAL?MATCH?(gist)-[:HAS_DOMAIN]->(domain:Domain)OPTIONAL?MATCH?(gist)-[:HAS_USECASE]->(usecase:UseCase)OPTIONAL?MATCH?(gist)<-[:WRITER_OF]-(writer:Person)OPTIONAL?MATCH?(gist)-[:HAS_KEYWORD]->(keyword:Keyword)//?Return?one?Gist?per?row?with?arrays?of?domains,?use?cases,?writers,?and?keywordsRETURNgist,related,COLLECT(DISTINCT?domain.name)?AS?domains,COLLECT(DISTINCT?usecase.name)?AS?usecases,COLLECT(DISTINCT?keyword.name)?AS?keywordsCOLLECT(DISTINCT?writer.name)?AS?writers,ORDER?BY?gist.title在這個(gè)查詢中,我們將選擇性地匹配所有相關(guān)的Domain、Use Case、Keyword和Person節(jié)點(diǎn),并且將它們?nèi)渴占饋?#xff0c;與我們對(duì)相關(guān)Gist的處理方式相同。現(xiàn)在我們的結(jié)果不再是平坦的、反正規(guī)化的,而是包含一列Gist,其中每個(gè)Gist都對(duì)應(yīng)著相關(guān)Gist的數(shù)組,形成了一種“has many”的關(guān)系,并且沒有任何重復(fù)數(shù)據(jù)。太酷了!
不僅如此,如果你覺得用表的形式返回?cái)?shù)據(jù)太老土,那么Cypher也可以返回對(duì)象:
RETURN{ gist:?gist,domains:?collect(DISTINCT?domain.name)?AS?domains,usecases:?collect(DISTINCT?usecase.name)?AS?usecases,writers:?collect(DISTINCT?writer.name)?AS?writers,keywords:?collect(DISTINCT?keyword.name)?AS?keywords,related_gists:?related}ORDER?BY?gist.title通常來說,在稍具規(guī)模的web應(yīng)用程序中,需要進(jìn)行大量的數(shù)據(jù)庫調(diào)用以返回HTTP響應(yīng)所需的數(shù)據(jù)。雖然你可以并行地執(zhí)行查詢,但通常來說你需要首先返回某個(gè)查詢的結(jié)果集,才能發(fā)送另一個(gè)數(shù)據(jù)庫請(qǐng)求以獲取相關(guān)的數(shù)據(jù)。在SQL中,你可以通過生成復(fù)雜的、開銷很大的表join語句,通過一個(gè)查詢從多張表中返回結(jié)果。但只要你在同一個(gè)查詢中進(jìn)行了多次SQL join,這個(gè)查詢的復(fù)雜性將會(huì)飛快地增長(zhǎng)。更不用說數(shù)據(jù)庫仍然需要進(jìn)行表或索引掃描才能夠獲得相應(yīng)的數(shù)據(jù)了。而在Neo4j中,通過關(guān)系獲取實(shí)體的方式是直接使用對(duì)應(yīng)于相關(guān)節(jié)點(diǎn)的指針,因此服務(wù)器可以隨意進(jìn)行遍歷。
轉(zhuǎn)載于:https://blog.51cto.com/cloudy/1976777
總結(jié)
以上是生活随笔為你收集整理的图数据库Neo4j全栈Web技术解密的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 你知道Unity IoC Contain
- 下一篇: [SinGuLaRiTy] 复习模板-搜