Neo4j 學習筆記(一) 初遇篇

北冥有隻魚發表於2022-03-20
本週讓我們繼續NoSQL家族的訪談之旅,本次我們的採訪物件Neo4J先生。

為什麼要有Neo4J?

各種資料庫事實上都是在描述現實世界上中資料之間的聯絡,現實世界中一個比較典型的場景就是關聯式資料庫力有不逮的地方,那就是社交網路,這種重聯絡的資料。如下圖所示:

圖示意

A和B、C、D、E都是好朋友,這在關係型資料庫是典型的多對多關係, 對於多對多的關係,一般關係型的策略是建立一張中間表來描述這種多對多的關係。但是在關係型資料庫中描述聯絡更常見的場景是描述不同資料的建模關係,比如學生和課程表的關係,選課表就儲存學生ID,課程表ID等其他必要資訊,學生和課程描述的物件是不同的,上面的圖描述的資料是相同的,可能都是社交圈的人。我們該如何儲存這種聯絡,一張自聯絡的表? 儲存跟他聯絡的資訊? 像下面這樣:

聯絡圖

那如果我現在的需求是查詢A的朋友的朋友呢,那一個簡單而無腦的操作就是我想看我朋友的朋友,這只是一層, 對應的SQL其實很好寫:

# 請原諒我還是從Student還是
 SELECT * FROM Student WHERE id in ( 
 SELECT studentReleationId  FROM student_releation WHERE studentId in     
 (SELECT studentReleationId FROM student_releation WHERE studentId  = '1')
# 轉成join的話,先找出我朋友的朋友的Id,然後再做join,或者子查詢
SELECT s2.studentReleationId  FROM student_releation s1 INNER JOIN  student_releation s2 on s1.studentReleationId = s2.studentId
WHERE s1.studentId = '1'

但其實並不推薦join,假設一個人的社交好友有100個,有的其實會更高,那我儲存個人資訊如果只有三萬個人的話,那麼儲存聯絡的表就是三百萬條資料,這個其實還不算交叉認識的。對於關係型資料庫來說大表的join是一場災難,但對於社交軟體來說,三萬個使用者來說是相當少的資料量,對於成熟的社交產品來說,三百萬,三千萬都有可能。再比如電影關係圖,檢視某個導演的電影參演的演員,對於關係型資料庫來說這個也不是不能做,只是數量上去之後,我們查詢速度是不斷在下降的,因為對於表與表之間的關係基本靠join,join的越多速度越慢。由此我們就引出了圖資料庫的應用場景,多對多建模、存取資料。圖資料庫對關聯查詢一般都進行鍼對性優化,比如儲存模型上、資料結構、查詢演算法等,防止區域性資料的查詢引發全部資料的讀取。

關係型資料庫有不同的實現,MySQL,Oracle, SQL Server 一樣,圖資料庫也有不同的實現: Neo4j,JanusGraph,HugeGraph。關係型資料庫的查詢語言是SQL,圖資料庫的查詢語言是: Gremlin、Cypher.本次我們介紹的就是Neo4j。

用起來

我首先還是來到Neo4j官網希望有免費試用的,因為我不想自己裝。然後我看到了免費試用:

免費試用

然後我就不裝了,直接開始用。我們首先還是先介紹基本的語法:

  • 建立

    • 建立結點

      CREATE (Person { name: "Emil", from: "Sweden", klout: 99 }) 
      Person是這個結點的標籤,{}裡面是這個標籤的屬性。
    • 建立聯絡

      MATCH (ee:Person) WHERE ee.name = "Emil"  #  先查詢一次是為了後面建立聯絡
      CREATE (js:Person { name: "Johan", from: "Sweden", learn: "surfing" }), # 建立一個結點,並將js指向該結點
      (ir:Person { name: "Ian", from: "England", title: "author" }),# 建立一個結點,並將ir指向 這個結點
      (rvb:Person { name: "Rik", from: "Belgium", pet: "Orval" }),
      (ally:Person { name: "Allison", from: "California", hobby: "surfing" }),
      (ee)-[:KNOWS {since: 2001}]->(js),(ee)-[:KNOWS {rating: 5}]->(ir),
      # (變數)-[關係描述]->(變數)  這兩個結點建立聯絡 [中是對這個邊的關係描述] KNOWS可以理解為了解,KNOWS{SINCE:2001}表示ee和js認識是在2001年
      (js)-[:KNOWS]->(ir),(js)-[:KNOWS]->(rvb),
      (ir)-[:KNOWS]->(js),(ir)-[:KNOWS]->(ally),
      (rvb)-[:KNOWS]->(ally)
    • 移除結點的屬性

      MATCH (a:Person {name:'Emil'}) REMOVE a.name
    • 刪結點

      MATCH (a:Person {name:'Ian'}) DELETE a
MATCH(ee:Person) where name = 'Johan'
SET ee.name = '張三' RETURN ee; 
# 將name = Johan 的name改為張三
    • 查詢單個結點
    MATCH (ee:Person) WHERE ee.name = "Johan" RETURN ee; # 查出結點中name叫Johan的,然後返回 
    • 查詢所有具有Knows關係的結點
    MATCH (n)-[:KNOWS]-() RETURN n
    • 查詢某人朋友的朋友, 這裡我們將KNOWS看做朋友
    MATCH (a:Person {name:'Ian'})-[r1:KNOWS]-()-[r2:KNOWS]-(friend_of_a_friend) RETURN friend_of_a_friend.name AS fofName
    # friend_of_a_friend 是返回的變數

Java操縱Neo4J

Java操縱任何資料庫都需要驅動, Neo4j的免費試用版有對應的語言示例.

各種示例

Neo4J 的驅動跟JDBC類似,原生都是我們拼SQL,然後提交。依賴:

 <dependency>
      <groupId>org.neo4j.driver</groupId>
      <artifactId>neo4j-java-driver</artifactId>
      <version>4.1.0</version>
 </dependency>

示例:

public class DriverIntroduction implements AutoCloseable{

    private final Driver driver;

    public DriverIntroduction(String uri, String user, String password, Config config) {
        // The driver is a long living object and should be opened during the start of your application
        driver = GraphDatabase.driver(uri, AuthTokens.basic(user, password), config);
    }
    @Override
    public void close() throws Exception {
        driver.close();
    }
    public void createFriendship(final String person1Name, final String person2Name) {
        // 建立結點 然後建立聯絡,並返回結點
        String createFriendshipQuery = "CREATE (p1:Person { name: $person1_name })\n" +
                "CREATE (p2:Person { name: $person2_name })\n" +
                "CREATE (p1)-[:KNOWS]->(p2)\n" +
                "RETURN p1, p2";

        Map<String, Object> params = new HashMap<>();
        params.put("person1_name", person1Name);
        params.put("person2_name", person2Name);
        try (Session session = driver.session()) {
            Record record = session.writeTransaction(tx -> {
                Result result = tx.run(createFriendshipQuery, params);
                return result.single();
            });
        } catch (Neo4jException ex) {
            throw ex;
        }
    }
}

總結一下

本文簡單介紹了一下Neo4J的由來,以及基本的增刪改查,Java操縱資料庫。Neo4j預設只有一個資料庫,目前社群版似乎不支援語法建立資料庫,需要修改配置檔案。Neo4J也沒有關係型資料庫對應表的概念。資料庫下面直接就是節點資料。Neo4j也有中文文件:

http://neo4j.com.cn/public/do...

參考資料

相關文章