圖形數據庫 Neo4j 開發實戰


https://www.ibm.com/developerworks/cn/java/j-lo-neo4j/

 

Neo4j 是一個高性能的 NoSQL 圖形數據庫。Neo4j 使用圖(graph)相關的概念來描述數據模型,把數據保存為圖中的節點以及節點之間的關系。很多應用中數據之間的關系,可以很直接地使用圖中節點和關系的概念來建模。對於這樣的應用,使用 Neo4j 來存儲數據會非常的自然,要優於使用關系數據庫。本文對 Neo4j 進行了深入的介紹,並結合具體的實例來進行詳細的說明,可以讓您對 Neo4j 有深入的了解,從而可以在應用開發中恰當地選擇 Neo4j 來作為存儲方式。

成 富, 高級軟件工程師

2013 年 6 月 20 日

  • expand內容

Neo4j 簡介

數據存儲一般是應用開發中不可或缺的組成部分。應用運行中產生的和所需要的數據被以特定的格式持久化下來。應用開發中很常見的一項任務是在應用本身的領域對象模型與數據存儲格式之間進行相互轉換。如果數據存儲格式與領域對象模型之間比較相似,那么進行轉換所需的映射關系更加自然,實現起來也更加容易。對於一個特定的應用來說,其領域對象模型由應用本身的特征來決定,一般采用最自然和最直觀的方式來進行建模。所以恰當的選擇數據存儲格式就顯得很重要。目前最常見的數據存儲格式是關系數據庫。關系數據庫通過實體 - 關系模型(E-R 模型)來進行建模,即以表和表之間的關系來建模。在實際開發中可以使用的關系數據庫的實現非常多,包括開源的和商用的。關系數據庫適合用來存儲數據條目的類型同構的表格型數據。如果領域對象模型中不同對象之間的關系比較復雜,則需要使用繁瑣的對象關系映射技術(Object-Relationship Mapping,ORM)來進行轉換。

對於很多應用來說,其領域對象模型並不適合於轉換成關系數據庫形式來存儲。這也是非關系型數據庫(NoSQL)得以流行的原因。NoSQL 數據庫的種類很多,包括鍵值對數據庫、面向文檔數據庫和圖形數據庫等。本文中介紹的 Neo4j 是最重要的圖形數據庫。Neo4j 使用數據結構中圖(graph)的概念來進行建模。Neo4j 中兩個最基本的概念是節點和邊。節點表示實體,邊則表示實體之間的關系。節點和邊都可以有自己的屬性。不同實體通過各種不同的關系關聯起來,形成復雜的對象圖。Neo4j 同時提供了在對象圖上進行查找和遍歷的功能。

對於很多應用來說,其中的領域對象模型本身就是一個圖結構。對於這樣的應用,使用 Neo4j 這樣的圖形數據庫進行存儲是最適合的,因為在進行模型轉換時的代價最小。以基於社交網絡的應用為例,用戶作為應用中的實體,通過不同的關系關聯在一起,如親人關系、朋友關系和同事關系等。不同的關系有不同的屬性。比如同事關系所包含的屬性包括所在公司的名稱、開始的時間和結束的時間等。對於這樣的應用,使用 Neo4j 來進行數據存儲的話,不僅實現起來簡單,后期的維護成本也比較低。

Neo4j 使用“圖”這種最通用的數據結構來對數據進行建模使得 Neo4j 的數據模型在表達能力上非常強。鏈表、樹和散列表等數據結構都可以抽象成用圖來表示。Neo4j 同時具有一般數據庫的基本特性,包括事務支持、高可用性和高性能等。Neo4j 已經在很多生產環境中得到了應用。流行的雲應用開發平台 Heroku 也提供了 Neo4j 作為可選的擴展。

在簡要介紹完 Neo4j 之后,下面介紹 Neo4j 的基本用法。

 

Neo4j 基本使用

在使用 Neo4j 之前,需要首先了解 Neo4j 中的基本概念。

節點和關系

Neo4j 中最基本的概念是節點(node)和關系(relationship)。節點表示實體,由 org.neo4j.graphdb.Node 接口來表示。在兩個節點之間,可以有不同的關系。關系由 org.neo4j.graphdb.Relationship 接口來表示。每個關系由起始節點、終止節點和類型等三個要素組成。起始節點和終止節點的存在,說明了關系是有方向,類似於有向圖中的邊。不過在某些情況,關系的方向可能並沒有意義,會在處理時被忽略。所有的關系都是有類型的,用來區分節點之間意義不同的關系。在創建關系時,需要指定其類型。關系的類型由 org.neo4j.graphdb.RelationshipType 接口來表示。節點和關系都可以有自己的屬性。每個屬性是一個簡單的名值對。屬性的名稱是 String 類型的,而屬性的值則只能是基本類型、String 類型以及基本類型和 String 類型的數組。一個節點或關系可以包含任意多個屬性。對屬性進行操作的方法聲明在接口 org.neo4j.graphdb.PropertyContainer 中。Node 和 Relationship 接口都繼承自 PropertyContainer 接口。PropertyContainer 接口中常用的方法包括獲取和設置屬性值的 getProperty 和 setProperty。下面通過具體的示例來說明節點和關系的使用。

該示例是一個簡單的歌曲信息管理程序,用來記錄歌手、歌曲和專輯等相關信息。在這個程序中,實體包括歌手、歌曲和專輯,關系則包括歌手與專輯之間的發布關系,以及專輯與歌曲之間的包含關系。清單 1 給出了使用 Neo4j 對程序中的實體和關系進行操作的示例。

清單 1. 節點和關系的使用示例
  private static enum RelationshipTypes implements RelationshipType { 
    PUBLISH, CONTAIN 
 } 

 public void useNodeAndRelationship() { 
    GraphDatabaseService db = new EmbeddedGraphDatabase("music"); 
    Transaction tx = db.beginTx(); 
    try { 
        Node node1 = db.createNode(); 
        node1.setProperty("name", "歌手 1"); 
        Node node2 = db.createNode(); 
        node2.setProperty("name", "專輯 1"); 
        node1.createRelationshipTo(node2, RelationshipTypes.PUBLISH); 
        Node node3 = db.createNode(); 
        node3.setProperty("name", "歌曲 1"); 
        node2.createRelationshipTo(node3, RelationshipTypes.CONTAIN); 
        tx.success(); 
    } finally { 
        tx.finish(); 
    } 
 }

在 清單 1 中,首先定義了兩種關系類型。定義關系類型的一般做法是創建一個實現了 RelationshipType 接口的枚舉類型。RelationshipTypes 中的 PUBLISH 和 CONTAIN 分別表示發布和包含關系。在 Java 程序中可以通過嵌入的方式來啟動 Neo4j 數據庫,只需要創建 org.neo4j.kernel.EmbeddedGraphDatabase 類的對象,並指定數據庫文件的存儲目錄即可。在使用 Neo4j 數據庫時,進行修改的操作一般需要包含在一個事務中來進行處理。通過 GraphDatabaseService 接口的 createNode 方法可以創建新的節點。Node 接口的 createRelationshipTo 方法可以在當前節點和另外一個節點之間創建關系。

另外一個與節點和關系相關的概念是路徑。路徑有一個起始節點,接着的是若干個成對的關系和節點對象。路徑是在對象圖上進行查詢或遍歷的結果。Neo4j 中使用 org.neo4j.graphdb.Path 接口來表示路徑。Path 接口提供了對其中包含的節點和關系進行處理的一些操作,包括 startNode 和 endNode 方法來獲取起始和結束節點,以及 nodes 和 relationships 方法來獲取遍歷所有節點和關系的 Iterable 接口的實現。關於圖上的查詢和遍歷,在下面小節中會進行具體的介紹。

使用索引

當 Neo4j 數據庫中包含的節點比較多時,要快速查找滿足條件的節點會比較困難。Neo4j 提供了對節點進行索引的能力,可以根據索引值快速地找到相應的節點。清單 2 給出了索引的基本用法。

清單 2. 索引的使用示例
 public void useIndex() { 
    GraphDatabaseService db = new EmbeddedGraphDatabase("music"); 
    Index<Node> index = db.index().forNodes("nodes"); 
    Transaction tx = db.beginTx(); 
    try { 
        Node node1 = db.createNode(); 
        String name = "歌手 1"; 
        node1.setProperty("name", name); 
        index.add(node1, "name", name); 
        node1.setProperty("gender", "男"); 
        tx.success(); 
    } finally { 
        tx.finish(); 
    } 
    Object result = index.get("name", "歌手 1").getSingle() 
 .getProperty("gender"); 
    System.out.println(result); // 輸出為“男”
 }

在 清單 2 中,通過 GraphDatabaseService 接口的 index 方法可以得到管理索引的 org.neo4j.graphdb.index.IndexManager 接口的實現對象。Neo4j 支持對節點和關系進行索引。通過 IndexManager 接口的 forNodes 和 forRelationships 方法可以分別得到節點和關系上的索引。索引通過 org.neo4j.graphdb.index.Index 接口來表示,其中的 add 方法用來把節點或關系添加到索引中,get 方法用來根據給定值在索引中進行查找。

圖的遍歷

在圖上進行的最實用的操作是圖的遍歷。通過遍歷操作,可以獲取到與圖中節點之間的關系相關的信息。Neo4j 支持非常復雜的圖的遍歷操作。在進行遍歷之前,需要對遍歷的方式進行描述。遍歷的方式的描述信息由下列幾個要素組成。

  • 遍歷的路徑:通常用關系的類型和方向來表示。
  • 遍歷的順序:常見的遍歷順序有深度優先和廣度優先兩種。
  • 遍歷的唯一性:可以指定在整個遍歷中是否允許經過重復的節點、關系或路徑。
  • 遍歷過程的決策器:用來在遍歷過程中判斷是否繼續進行遍歷,以及選擇遍歷過程的返回結果。
  • 起始節點:遍歷過程的起點。

Neo4j 中遍歷方式的描述信息由 org.neo4j.graphdb.traversal.TraversalDescription 接口來表示。通過 TraversalDescription 接口的方法可以描述上面介紹的遍歷過程的不同要素。類 org.neo4j.kernel.Traversal 提供了一系列的工廠方法用來創建不同的 TraversalDescription 接口的實現。清單 3 中給出了進行遍歷的示例。

清單 3. 遍歷操作的示例
 TraversalDescription td = Traversal.description() 
    .relationships(RelationshipTypes.PUBLISH) 
    .relationships(RelationshipTypes.CONTAIN) 
    .depthFirst() 
    .evaluator(Evaluators.pruneWhereLastRelationshipTypeIs(RelationshipTypes.CONTAIN)); 
 Node node = index.get("name", "歌手 1").getSingle(); 
 Traverser traverser = td.traverse(node); 
 for (Path path : traverser) { 
    System.out.println(path.endNode().getProperty("name")); 
 }

在 清單 3 中,首先通過 Traversal 類的 description 方法創建了一個默認的遍歷描述對象。通過 TraversalDescription 接口的 relationships 方法可以設置遍歷時允許經過的關系的類型,而 depthFirst 方法用來設置使用深度優先的遍歷方式。比較復雜的是表示遍歷過程的決策器的 evaluator 方法。該方法的參數是 org.neo4j.graphdb.traversal.Evaluator 接口的實現對象。Evalulator 接口只有一個方法 evaluate。evaluate 方法的參數是 Path 接口的實現對象,表示當前的遍歷路徑,而 evaluate 方法的返回值是枚舉類型 org.neo4j.graphdb.traversal.Evaluation,表示不同的處理策略。處理策略由兩個方面組成:第一個方面為是否包含當前節點,第二個方面為是否繼續進行遍歷。Evalulator 接口的實現者需要根據遍歷時的當前路徑,做出相應的決策,返回適當的 Evaluation 類型的值。類 org.neo4j.graphdb.traversal.Evaluators 提供了一些實用的方法來創建常用的 Evalulator 接口的實現對象。清單 3 中使用了 Evaluators 類的 pruneWhereLastRelationshipTypeIs 方法。該方法返回的 Evalulator 接口的實現對象會根據遍歷路徑的最后一個關系的類型來進行判斷,如果關系類型滿足給定的條件,則不再繼續進行遍歷。

清單 3 中的遍歷操作的作用是查找一個歌手所發行的所有歌曲。遍歷過程從表示歌手的節點開始,沿着 RelationshipTypes.PUBLISH 和 RelationshipTypes.CONTAIN 這兩種類型的關系,按照深度優先的方式進行遍歷。如果當前遍歷路徑的最后一個關系是 RelationshipTypes.CONTAIN 類型,則說明路徑的最后一個節點包含的是歌曲信息,可以終止當前的遍歷過程。通過 TraversalDescription 接口的 traverse 方法可以從給定的節點開始遍歷。遍歷的結果由 org.neo4j.graphdb.traversal.Traverser 接口來表示,可以從該接口中得到包含在結果中的所有路徑。結果中的路徑的終止節點就是表示歌曲的實體。

 

Neo4j 實戰開發

在介紹了 Neo4j 的基本使用方式之后,下面通過具體的案例來說明 Neo4j 的使用。作為一個數據庫,Neo4j 可以很容易地被使用在 Web 應用開發中,就如同通常使用的 MySQL、SQL Server 和 DB2 等關系數據庫一樣。不同之處在於如何對應用中的數據進行建模,以適應后台存儲方式的需求。同樣的領域模型,既可以映射為關系數據庫中的 E-R 模型,也可以映射為圖形數據庫中的圖模型。對於某些應用來說,映射為圖模型更為自然,因為領域模型中對象之間的各種關系會形成復雜的圖結構。

本節中使用的示例是一個簡單的微博應用。在微博應用中,主要有兩種實體,即用戶和消息。用戶之間可以互相關注,形成一個圖結構。用戶發布不同的微博消息。表示微博消息的實體也是圖中的一部分。從這個角度來說,使用 Neo4j 這樣的圖形數據庫,可以更好地描述該應用的領域模型。

如同使用關系數據庫一樣,在使用 Neo4j 時,既可以使用 Neo4j 自身的 API,也可以使用第三方框架。Spring 框架中的 Spring Data 項目提供了對 Neo4j 的良好支持,可以在應用開發中來使用。Spring Data 項目把 Neo4j 數據庫中的 CRUD 操作、使用索引和進行圖的遍歷等操作進行了封裝,提供了更加抽象易用的 API,並通過使用注解來減少開發人員所要編寫的代碼量。示例的代碼都是通過 Spring Data 來使用 Neo4j 數據庫的。下面通過具體的步驟來介紹如何使用 Spring Data 和 Neo4j 數據庫。

開發環境

使用 Neo4j 進行開發時的開發環境的配置比較簡單。只需要根據 參考資源中給出的地址,下載 Neo4j 本身的 jar 包以及所依賴的 jar 包,並加到 Java 程序的 CLASSPATH 中就可以了。不過推薦使用 Maven 或 Gradle 這樣的工具來進行 Neo4j 相關依賴的管理。

定義數據存儲模型

前面已經提到了應用中有兩種實體,即用戶和消息。這兩種實體需要定義為對象圖中的節點。清單 1 中給出的創建實體的方式並不直觀,而且並沒有專門的類來表示實體,后期維護成本比較高。Spring Data 支持在一般的 Java 類上添加注解的方式來聲明 Neo4j 中的節點。只需要在 Java 類上添加 org.springframework.data.neo4j.annotation.NodeEntity 注解即可,如 清單 4 所示。

清單 4. 使用 NodeEntity 注解聲明節點類
  @NodeEntity 
 public class User { 
 @GraphId Long id; 

 @Indexed 
 String loginName; 
 String displayName; 
 String email; 
 }

如 清單 4 所示,User 類用來表示用戶,作為圖中的節點。User 類中的域自動成為節點的屬性。注解 org.springframework.data.neo4j.annotation.GraphId 表明該屬性作為實體的標識符,只能使用 Long 類型。注解 org.springframework.data.neo4j.annotation.Indexed 表明為屬性添加索引。

節點之間的關系同樣用注解的方式來聲明,如 清單 5 所示。

清單 5. 使用 RelationshipEntity 注解聲明關系類
 @RelationshipEntity(type = "FOLLOW") 
 public class Follow { 
 @StartNode 
 User follower; 
 @EndNode 
 User followed; 

 Date followingDate = new Date(); 
 }

在 清單 5 中,RelationshipEntity 注解的屬性 type 表示關系的類型,StartNode 和 EndNode 注解則分別表示關系的起始節點和終止節點。

在表示實體的類中也可以添加對關聯的節點的引用,如 清單 6 中給出的 User 類中的其他域。

清單 6. User 類中對關聯節點的引用
 @RelatedTo(type = "FOLLOW", direction = Direction.INCOMING) 
 @Fetch Set<User> followers = new HashSet<User>(); 

 @RelatedTo(type = "FOLLOW", direction = Direction.OUTGOING) 
 @Fetch Set<User> followed = new HashSet<User>(); 

 @RelatedToVia(type = "PUBLISH") 
 Set<Publish> messages = new HashSet<Publish>();

如 清單 6 所示,注解 RelatedTo 表示與當前節點通過某種關系關聯的節點。因為關系是有向的,可以通過 RelatedTo 的 direction 屬性來聲明關系的方向。對當前用戶節點來說,如果 FOLLOW 關系的終止節點是當前節點,則說明關系的起始節點對應的用戶是當前節點對應的用戶的粉絲,用“direction = Direction.INCOMING”來表示。因此 followers 域表示的是當前用戶的粉絲的集合,而 followed 域表示的是當前用戶所關注的用戶的集合。注解 RelatedToVia 和 RelatedTo 的作用類似,只不過 RelatedToVia 不關心關系的方向,只關心類型。因此 messages 域包含的是當前用戶所發布的消息的集合。

數據操作

在定義了數據存儲模型之后,需要創建相應的類來對數據進行操作。數據操作的對象是數據模型中的節點和關系類的實例,所涉及的操作包括常見的 CRUD,即創建、讀取、更新和刪除,還包括通過索引進行的查找和圖上的遍歷操作等。由於這些操作的實現方式都比較類似,Spring Data 對這些操作進行了封裝,提供了簡單的使用接口。Spring Data 所提供的數據操作核心接口是 org.springframework.data.neo4j.repository.GraphRepository。GraphRepository 接口繼承自三個提供不同功能的接口:org.springframework.data.neo4j.repository.CRUDRepository 接口提供 save、delete、findOne 和 findAll 等方法,用來進行基本的 CRUD 操作;org.springframework.data.neo4j.repository.IndexRepository 則提供了 findByPropertyValue、findAllByPropertyValue 和 findAllByQuery 等方法,用來根據索引來查找;org.springframework.data.neo4j.repository.TraversalRepository 則提供了 findAllByTraversal 方法,用來根據 TraversalDescription 接口的描述來進行遍歷操作。

Spring Data 為 GraphRepository 接口提供了默認的實現。在大多數情況下,只需要聲明一個接口繼承自 GraphRepository 接口即可,Spring Data 會在運行時創建相應的實現類的對象。對表示用戶的節點類 User 進行操作的接口 UserRepository 如 清單 7 所示。

清單 7. 操作 User 類的 UserRepository 接口
 public interface UserRepository extends GraphRepository<User> { 
 }

如 清單 7 所示,UserRepository 接口繼承自 GraphRepository 接口,並通過泛型聲明要操作的是 User 類。對節點類的操作比較簡單,而對於關系類的操作就相對復雜一些。清單 8 中給出了對發布關系進行操作的接口 PublishRepository 的實現。

清單 8. 操作 Publish 類的 PublishRepository 接口
 public interface PublishRepository extends GraphRepository<Publish> { 
 @Query("start user1=node({0}) " + 
            " match user1-[:FOLLOW]->user2-[r2:PUBLISH]->followedMessage" + 
            " return r2") 
 List<Publish> getFollowingUserMessages(User user); 

 @Query("start user=node({0}) match user-[r:PUBLISH]->message return r") 
 List<Publish> getOwnMessages(User user); 
 }

在 清單 8 中,getFollowingUserMessages 方法用來獲取某個用戶關注的所有其他用戶所發布的消息。該方法的實現是通過圖上的遍歷操作來完成的。Spring Data 提供了一種簡單的查詢語言來描述遍歷操作。通過在方法上添加 org.springframework.data.neo4j.annotation.Query 注解來聲明所使用的遍歷方式即可。以 getFollowingUserMessages 方法的遍歷聲明為例,“node({0})”表示當前節點,“start user1=node({0})”表示從當前節點開始進行遍歷,並用 user1 表示當前節點。“match”用來表示遍歷時選中的節點應該滿足的條件。條件“user1-[:FOLLOW]->user2-[r2:PUBLISH]->followedMessage”中,先通過類型為 FOLLOW 的關系找到 user1 所關注的用戶,以 user2 來表示;再通過類型為 PUBLISH 的關系,查找 user2 所發布的消息。“return”用來返回遍歷的結果,r2 表示的是類型為 PUBLISH 的關系,與 getFollowingUserMessages 方法的返回值類型 List<Publish> 相對應。

在應用中的使用

在定義了數據操作的接口之后,就可以在應用的服務層代碼中使用這些接口。清單 9 中給出了用戶發布新微博時的操作方法。

清單 9. 用戶發布新微博的方法
 @Autowired 
 UserRepository userRepository; 

 @Transactional 
 public void publish(User user, String content) { 
 Message message = new Message(content); 
 messageRepository.save(message); 
 user.publish(message); 
 userRepository.save(user); 
 }

如 清單 9 所示,publish 方法用來給用戶 user 發布內容為 content 的微博。域 userRepository 是 UserRepository 接口的引用,由 Spring IoC 容器在運行時自動注入依賴,該接口的具體實現由 Spring Data 提供。在 publish 方法中,首先創建一個 Message 實體類的對象,表示消息節點;再通過 save 方法把該節點保存到數據庫中。User 類的 publish 方法的實現如 清單 10 所示,其邏輯是創建一個 Publish 類的實例表示發布關系,並建立用戶和消息實體之間的關系。最后再更新 user 對象即可。

清單 10. User 類的 publish 方法
 @RelatedToVia(type = "PUBLISH") 
 Set<Publish> messages = new HashSet<Publish>(); 

 public Publish publish(Message message) { 
 Publish publish = new Publish(this, message); 
 this.messages.add(publish); 
 return publish; 
 }

在創建了相關的服務層類之后,就可以從服務層中暴露出相關的使用 JSON 的 REST 服務,然后在 REST 服務的基礎上創建應用的前端展示界面。界面的實現部分與 Neo4j 並無關系,在這里不再贅述。整個程序基於 Spring 框架來開發。Spring Data 為 Neo4j 提供了獨立的配置文件名稱空間,可以方便在 Spring 配置文件中對 Neo4j 進行配置。清單 11 給出了與 Neo4j 相關的 Spring 配置文件。

清單 11. Neo4j 的 Spring 配置文件
 <neo4j:config storeDirectory="data/neo-mblog.db" /> 
 <neo4j:repositories base-package="com.chengfu.neomblog.repository" />

在 清單 11 中,config 元素用來設置 Neo4j 數據庫的數據保存目錄,repositories 元素用來聲明操作 Neo4j 中的節點和關系類的 GraphRepository 接口的子接口的包名。Spring Data 會負責在運行時掃描該 Java 包,並為其中包含的接口創建出對應的實現對象。

示例應用的完整代碼存放在 GitHub 上,見 參考資源

使用 Neo4j 原生 API

如果不使用 Spring Data 提供的 Neo4j 支持,而使用 Neo4j 的原生 API,也是一樣可以進行開發。只不過由於 Neo4j 的原生 API 的抽象層次較低,使用起來不是很方便。下面以示例應用中用戶發布微博的場景來展示原生 API 的基本用法,見 清單 12

清單 12. 使用 Neo4j 原生 API
 public void publish(String username, String message) { 
    GraphDatabaseService db = new EmbeddedGraphDatabase("mblog"); 
    Index<Node> index = db.index().forNodes("nodes"); 
    Node ueserNode = index.get("user-loginName", username).getSingle(); 
    if (ueserNode != null){ 
        Transaction tx = db.beginTx(); 
        try { 
            Node messageNode = db.createNode(); 
            messageNode.setProperty("message", message); 
            ueserNode.createRelationshipTo(messageNode, RelationshipTypes.PUBLISH); 
            tx.success(); 
        } finally { 
            tx.finish(); 
        } 
    } 
 }

從 清單 12 中可以看出,原生 API 的基本用法是先通過 Neo4j 數據庫的索引找到需要操作的表示用戶的節點,然后再創建出表示微博消息的節點,最后在兩個節點之間建立關系。這些步驟都使用 Neo4j 的基本 API 來完成。

與 清單 10 中使用 Spring Data 的功能相同的方法進行比較,可以發現使用原生 API 的代碼要復雜不少,而使用 Spring Data 的則簡潔很多。因此,在實際開發中推薦使用 Spring Data。

 

小結

關系數據庫在很長一段時間都是大多數應用采用的數據存儲方式的首要選擇。隨着技術的發展,越來越多的 NoSQL 數據庫開始流行起來。對於應用開發人員來說,不應該總是盲目使用關系數據庫,而是要根據應用本身的特點,選用最合適的存儲方式。Neo4j 數據庫以“圖”作為數據之間關系的描述方式,非常適合於使用在數據本身就以圖結構來組織的應用中。本文對 Neo4j 數據庫的使用做了詳細的介紹,可以幫助開發人員了解和使用 Neo4j 數據庫。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM