一個快速的REST例子
首先來看些基本知識。如果沒有服務API,Neo4j就不能支持其他語言。該接口提供一組基于JSON消息格式的RESTful Web服務和一個全面的發現機制。使用中使用這個接口的最快和最容易的方法是通過使用cURL:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
$ curl http: / / localhost: 7474 / db / data / { "extensions" : { }, "neo4j_version" : "2.0.0-M03" } |
從這個端點返回JSON對象包含一組資源名稱和URI下可以找到的Cypher端點。在消息載荷中接受傳送來的Cyper請求并執行這些查詢,在HTTP響應中返回結果。
正是這種REST API接口,使得現在已有的各種Neo4j驅動得以建立。py2neo提供了這些REST資源的簡單封裝,這使Python應用程序開發者可以放心使用Neo4j而不用考慮底層的客戶機-服務器協議。
一個簡單的應用
為實際驗證py2neo,我們將著眼于建立一個簡單的用于存儲姓名和電子郵件地址的通訊錄管理系統。我們自然會使用節點來模擬每一個獨立實體,但它是要記住,Neo4j沒有類型的概念。類型是從周圍的關系和屬性推斷來的。
下面的關系圖中人顯示為紅色、電子郵件地址節點顯示為藍色。這些當然是純粹的邏輯演示節點,但數據本身并沒有區別。
我們的應用程序將完成兩個功能:添加新的聯系人信息和檢索聯系人的完整列表。為此,我們將創建一個Person類包裝Py2neoNodeobject,這使我們有一個底層處理的實現且留出用戶級的功能。上圖中的ROOT節點是指上圖中一個固定的參考點,我們沿著這個點開始。
讓我們直接看看代碼。下面是一個完整的小型應用。這個程序允許添加新的名字與一個或者更多email地址相連接的以及提供了一個容易的方式來顯示這些連接信息的一個命令行工具。沒有參數的運行是顯示使用模式,而且這個唯一的依賴只是需要一個本地未修改的Neo4j實例(instance)而已。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
|
#!/usr/bin/env python # -*- coding: utf-8 -*- from __future__ import print_function import sys from py2neo import neo4j, node, rel graph_db = neo4j.GraphDatabaseService() class Person( object ): _root = graph_db.get_or_create_indexed_node( "reference" , "contacts" , "root" ) @classmethod def create( cls , name, * emails): person_node, _ = graph_db.create(node(name = name), rel( cls ._root, "PERSON" , 0 )) for email in emails: graph_db.create(node(email = email), rel( cls ._root, "EMAIL" , 0 ), rel(person_node, "EMAIL" , 0 )) return Person(person_node) @classmethod def get_all( cls ): return [Person(person.end_node) for person in cls ._root.match( "PERSON" )] def __init__( self , node): self ._node = node def __str__( self ): return self .name + "\n" + "\n" .join( " <{0}>" . format (email) for email in self .emails) @property def name( self ): return self ._node[ "name" ] @property def emails( self ): return [rel.end_node[ "email" ] for rel in self ._node.match( "EMAIL" )] if __name__ = = "__main__" : if len (sys.argv) < 2 : app = sys.argv[ 0 ] print ("Usage: { 0 } add <name> <email> [<email>...]". format (app)) print ( " {0} list" . format (app)) sys.exit() method = sys.argv[ 1 ] if method = = "add" : print (Person.create( * sys.argv[ 2 :])) elif method = = "list" : for person in Person.get_all(): print (person) else : print ( "Unknown command" ) |
在第09行上是第一行Py2neo代碼,用來創建了一個GraphDatabaseService對象。通過這個,我們就可以訪問使用Neo4j server的大多數功能。可選一個URI傳遞到這個構造器里,盡管如果什么都沒有提供,代而取之的是使用默認的本地參數。也就是說下面兩行是完全相等的:
1
2
3
|
graph_db = neo4j.GraphDatabaseService() graph_db = neo4j.GraphDatabaseService |
第13行介紹了調用了get_or_create_indexed_node,它提供一種在圖形里創建固定引用點的漂亮方式。傳統的Neo4j索引允許節點和關系通過鍵值對訪問,而在這個代碼里我們使用了帶連接的關鍵字和root值的引用索引實例。在第一次執行時,會創建一個新的節點,而且在隨后的執行中,這個節點(即root)會復用(reused)。
在第17行,我們看見了推薦的節點和關系抽象的標記,以及接受和使用節點和關系抽象的 create方法。任意多的抽象都可以被傳遞到這個方法中,并且在單個批處理轉換中創建實體并以指定它們的順序作為一個列表返回。抽象節點用 節點函數表示并帶有一些屬性,然而抽象關系使用rel函數接受一個起始節點,類型和終止節點。上下文中,其他節點,關系起始和終止節點可能整合引用到在其他批處理中其他節點。在我們的例子中,我們把根節點連接到新創建的person節點,否則就作為項目0(item 0)了。
這次我們在第24行和38行上以match方法形式和關系見面[@Lesus 注: oschina代碼行數有問題。對應于本文的第28和44行]。它試圖使用一個特殊的條件集合(set)標識關系,然后使用列表(list)返回它們。這這些示例中,這個關系和PERSON關系相匹配,從root節點和EMAIL關系開始到所給定的person節點。和Cypher很相似,用來查詢包含MATCH關鍵字的場景。
最后值得注意的一點是在上面的代碼中訪問節點屬性的方式只是其中一種簡單的方式。Py2neo重寫了標準python的__getitem__和 __setitem__方法,通過方括號標識來方便訪問任何屬性。這點在第34和38行上可以看到。[@Lesus 注:對應于本文的第39和44行]
總結
在那里(代碼行34和38)我們這樣做了,這顯示了它是如何快速簡易地在JAVA環境之外拼湊出一個Neo4j應用程序,也顯示了Py2neo是如何通過REST API來抽象出大多數沉重的負擔。這里的例子并沒有解決唯一性,盡管功能上提供了唯一索引和CypherCREATE UNIQUE語句。Django開發者可能也想要考慮一個層,如Neomodel,它在Py2neo頂層上表示了一個Djangoesque ORM-style 層。