一個訪客在Nigel Small釋出的文章,他感興趣的領域包括Python、Javascript、PostgreSQL、Neo4j和Linux。他是一些開源專案的創始者,其中最有名的是py2neo,他是一個活躍的部落格、演說者及Neo4j社群成員。可以通過@technige找到他。
Neo4j是物件導向基於Java的 ,被設計為一個建立在Java之上、可以直接嵌入應用的資料儲存。此後,其他語言和平臺的支援被引入,Neo4j社群獲得持續增長,獲得了越來越多的技術支持者。目前已支援.NET、Ruby、Python、Node.js及PHP等。因此,不管是什麼專案,沒有理由不引入Neo4j。
本文重點介紹Python,這門語言的哲學與Java大大不同,同時展示py2neo庫如何被用來建立一個簡單的應用程式。
一個快速的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" : { }, "node" : "http://localhost:7474/db/data/node", "node_index" : "http://localhost:7474/db/data/index/node", "relationship_index" : "http://localhost:7474/db/data/index/relationship", "extensions_info" : "http://localhost:7474/db/data/ext", "relationship_types" : "http://localhost:7474/db/data/relationship/types", "batch" : "http://localhost:7474/db/data/batch", "cypher" : "http://localhost:7474/db/data/cypher", "transaction" : "http://localhost:7474/db/data/transaction", "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 ("http://localhost:7474/db/data/") |
第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來抽象出大多數沉重的負擔。這裡的例子並沒有解決唯一性,儘管功能上提供了唯一索引和Cypher CREATE UNIQUE語句。Django開發者可能也想要考慮一個層,如Neomodel,它在Py2neo頂層上表示了一個Djangoesque ORM-style 層。