天天看點

為什麼你應該在你下個 Ruby APP 中使用 Neo4j

每天,我都需要儲存大量的資料,而我可以使用很多工具,比如 postgresql,mysql,sqlite,redis 和 mongodb,當我積累了豐富的經驗,并且熟練掌握這些工具的時候,我就不認為他們還有什麼樂趣了。我從心裡熱愛 ruby,是因為它很有趣并且能讓我能用自己的方式做一些很厲害的事情。但是我并沒有意識到這樣的資料處理工具将會影響我很久,盡管它令我找到了新的樂趣,是以請讓我向你介紹 neo4j 吧。

neo4j 是圖形化的資料庫!這意味着它被優化用于管理和查詢實體(節點)之間的關系(節點),而不是像一個使用表的關系資料庫那樣的東西。

為什麼這很棒?想象一個沒有國外鑰匙的世界。資料庫中的每個實體的關系之間都十分密切,以至于可以直接引用其他實體。如果你想要引用的關系沒有關系表或者搜尋掃面,就隻需要幾個連結就行了。這完美的展現了典型的對象模型。這非常令人興奮,因為 neo4j 可以提供很多我們所期待的資料庫應該具有的功能,并且為我們提供了工具來查詢複雜資料圖形。

為了連結 neo4j,我們将使用 neo4j gem。連接配接到你在 rails 應用程式中的 neo4j 的方法,可以在 gem 文檔中找到。同時此顯示代碼的應用程式也可在 github 庫中運作 rails 應用程式(使用 sitepoint 的分支)你可以在你的資料庫中運作使用前 rake load_sample_data 指令來填充資料庫。

下面是一個關于資産模型的基本案例來源于資産管理的rails app

簡單解釋一下:

1.neo4j的gem給我們的neo4j:: activenode子產品,其中包括我們所做的這個模型

2.此資産意味着這種模式将負責所有在neo4j标記資産的節點(除标簽一個節點可以有很多的标簽發揮類似的作用)

3.我們有一個title屬性來描述各個節點

4.我們有傳出has_many的分類關聯。這種關聯幫助我們通過下面的資料庫has_category關系找到分類的對象。

有了這個模型,我們可以進行基本的查詢來找到一個資産,并得到它的分類

熟悉 activerecord 或者 mongoid 的任何人會看到找個上百次。為了讓它變得更有趣,讓我們來定義一個 category 模型:

在這裡,我們的關聯有一個 origin 的選項引用了 資産(asset)模型的 categories 關聯。如果我們想要的話,我們可以再一次指定 type: :has_category

如果我們想要獲得所有和我們的 資産共享一個 category 的所有 資産(asset)會怎樣?

那這樣又會發生什麼事?activenode 生成一個資料庫查詢,指定了一個從我們的 資産(asset)到所有其他共享一個 category 的 資産(asset)的路徑。然後資料庫會發回那些 資産(asset)給我們。下面給出它用到的查詢:

這種查詢語言叫做 cypher,它等同于 neo4j 的 sql。特别注意括号周圍節點和接頭的 ascii 風格所代表的關系。這種 cypher 查詢有點備援,因為 activenode 在算法上面會生成這些查詢。如果一個人去寫這種查詢,它将會看起來像這樣:

同時,我也發現 cypher 比 sql 要容易并且更強大, 但是我們在這裡我們不會擔心 cypher 太多。如果你之後想學習更過關于 cypher 的知識,你可以查找 totorials 和 refcard。

正如你所看到的,我們可以使用 neo4j 來跨實體。太了不起了!我們也可以使用帶有成對 joins 的 sql 來做到。雖然 cypher 看起來很酷,但是我們還沒有什麼重大的突破。假設我們想要使用這些查詢來建立一些基于共享 category 的 asset recommendation 會怎樣?我們會想要去對 asset 排序然後按照最多共同 category 來排序。讓我們在我們的模型中建立一個方法:

這裡有幾個有趣的地方值得注意一下:

為什麼你應該在你下個 Ruby APP 中使用 Neo4j

你注意到這裡沒有一個 group by 字句了嗎?neo4j 非常智能地意識到 collect 和 count 是聚合函數,并且在我們的結果中通過非聚合列排序(在這個案例中隻有 asset變量)。

注意那條 sql!

作為最後一步,我們能夠建立的不僅僅是相同 category 的 recommendation。想象一下我們有下面 neo4j 子圖:

為什麼你應該在你下個 Ruby APP 中使用 Neo4j

除了共享的 category 之外,讓我們來解釋一下 creators 和 viewers asset 有多少共同之處:

在這裡我們深入研究并開始建構我們自己的查詢。結構一樣,但不僅僅是通過在共享 category 的兩個 asset 之間查找一條path, 我們也會指定兩個更多的可選path。我們可以指定三個可選path,但是 neo4j 需要使用資料庫裡邊每一個其他的 資産(asset) 來對比我們的 資産(asset)。為我們的 category 節點使用 match 而不是 optional_match,我們要求至少有一個共享 category。這樣極大的限制了我們的搜尋空間。

在圖中有1個共享 category,0個共享 creator 以及兩個共享的 viewer。這意味着”ruby“和”ruby on rails“之間的分數将會是:

(12) + (04) + (2*0.1) =2.2

還要注意,我們在這三條path上的 count 聚合函數上做計算(和排序)。對我來說很酷,它使得我有點興奮地去思考。。。

讓我們來處理另外一個普遍的問題。假設你的 ceo 來到你桌子前對你說,“我們已經建構了一個非常棒的 app,但是客戶想要能夠控制誰可以看他們的東西。你可以建立一些隐私控制嗎?”看起來很簡單。讓我們依賴一個标記來通路私有資産(asset):

使用這些設計,你可以顯示使用者可以看到的所有資産(asset),因為資産(asset)是公開的或者因為觀察者擁有它。沒問題,但同樣不是個大問題。在另外一個資料庫裡邊,你可以在兩個列/屬性上面做搜尋。讓我們來感受一下!

産品經理來到你身邊說,“嘿,謝謝你,但是現在人們想要給其他使用者直接通路他們私有的東西”。沒問題!你可以建立以個 ui 來讓使用者添加和移除他們資産(asset)的 viewalbe_by 關系,并且可以這樣查詢:

另外這将是一個連接配接表。你仍在使用者可以擷取資産(asset)的另外一個路徑。花費一些時間來欣賞 neo4j 無模式的本質。

滿足于你的工作日,靠在椅子上和喝着下午茶。當然,前提是社交媒體客戶服務代表說“使用者喜歡新的功能,但是他們希望能夠建立分組以及配置設定通路分組。你可以做到嗎?并且,能允許任意層次的分組嗎?你深深得看着他們的眼睛幾分鐘,然後說:”當然!“。之後開始變得複雜,來看個例子:

為什麼你應該在你下個 Ruby APP 中使用 Neo4j

如果這些資産都是私人代碼,到目前為止給 matz 和 tenderlove 去通路 ruby,以及 dhh 去通路 ruby on rails。添加分組支援,你開始直接配置設定分組:

這個是很簡單的,因為你隻需要添加另外一條路徑。當然,到目前為止那是我們的舊帽子。 tenderlove 和 yehuda 就能夠看到”ruby on rails“資源(asset),因為他們是“railsists”分組的。還要注意:現在一些使用者有多個資産(asset)路徑(像 matz 去 ruby 通過 rubyists 分組和通過 created 關聯)你需要傳回 distinct asset。

然而通過一個層次結構組織來指定一個任意的path需要花費你較多的時間。你可以檢視neo4j文檔直到你找到一些叫做”variable relationships“的東西并試試:

這裡你已經做到了!這種查詢會找到可通路一個 group 的 資産(asset)并且貫穿了0到5的 has_subgroup關系,最後以一個檢視使用者是否在最後一個 group 裡邊的檢查結束。你是這個故事的男主角,你的公司會為你能夠快速做好這項工作而給你獎金。

你可以用 neo4j 做很多我做不到并且很棒的事情(包括使用它的 amazing web interface 去利用 cyper 查找你的資料)。它不僅僅是一種傑出的方式用一個簡單而直覺的方式來存儲你的資料,而且提供了很多高度聯系的資料查詢效率的好處(相信我你的資料是高度聯系的,盡管你沒有意識到)。我鼓勵你去看看 neo4j 并在你的下一個項目中試試。