天天看點

Neo4j 示例:三國志人物關系圖譜

Neo4j

Neo4j 示例:三國志人物關系圖譜

文章目錄

      • Neo4j
        • 簡介
        • 啟動
        • 導入資料
          • 準備資料集
          • 建立節點
          • 建立關系
        • 索引
          • 建立索引
          • 删除索引
        • CQL(Cypher Query Language)
        • 用戶端驅動
        • 參考

簡介

  • 定義:一種基于圖論實作 NoSQL 的資料庫管理系統,用節點和關系、屬性表現實體内容
    Neo4j 示例:三國志人物關系圖譜
  • 特點:
    • 根據其連接配接的關系可以快速找出其鄰近節點
    • 支援 ACID
    • 社群版隻能單機使用,企業版才能使用分布式安裝(負載均衡與高可用)
  • 應用領域
    • 社交網絡、推薦引擎、交通物流、欺詐風控
  • 不适用場景:
    • 記錄大量基于事件的資料
    • 需要對大規模分布式資料進行處理
    • 二進制資料存儲
    • 适合儲存在關系型資料庫的結構化資料
  • 下載下傳:https://neo4j.com/download-center/#community
    Neo4j 示例:三國志人物關系圖譜

啟動

# 前置依賴 JRE(Java Runtime Environment)

$ cd <NEO4J_HOME>

$ vim ./conf/neo4j.conf
# The name of the default database
dbms.default_database=sanguo

# Bolt connector
dbms.connector.bolt.enabled=true
dbms.connector.bolt.tls_level=DISABLED
dbms.connector.bolt.listen_address=:7687

# HTTP Connector. There can be zero or one HTTP connectors.
dbms.connector.http.enabled=true
dbms.connector.http.listen_address=:7474

$ ./bin/neo4j console
2021-02-27 02:45:15.154+0000 INFO  Starting...
2021-02-27 02:45:21.637+0000 INFO  ======== Neo4j 4.2.3 ========
2021-02-27 02:45:26.105+0000 INFO  Performing postInitialization step for component 'security-users' with version 2 and status CURRENT
2021-02-27 02:45:26.106+0000 INFO  Updating the initial password in component 'security-users'
2021-02-27 02:45:34.404+0000 INFO  Bolt enabled on localhost:7687.
2021-02-27 02:45:35.254+0000 INFO  Remote interface available at http://localhost:7474/
2021-02-27 02:45:35.255+0000 INFO  Started
           

終端 Shell 通路

$ ./bin/cypher-shell -a 127.0.0.1 -u neo4j -p 123456
Connected to Neo4j using Bolt protocol version 4.2 at neo4j://127.0.0.1:7687 as user neo4j.
Type :help for a list of available commands or :exit to exit the shell.
Note that Cypher queries must end with a semicolon.

[email protected]> MATCH (n) RETURN n;
+------------------------+
| n                      |
+------------------------+
| (:Country {name: "魏"}) |
| (:Country {name: "吳"}) |
| (:Country {name: "蜀"}) |
+------------------------+
           

浏覽器通路:

輸入資料庫名(sanguo),預設使用者名及密碼(neo4j)
Neo4j 示例:三國志人物關系圖譜
Neo4j 示例:三國志人物關系圖譜

導入資料

準備資料集
# 檢視資料導入的目錄
$ less ./conf/neo4j.conf
# This setting constrains all `LOAD CSV` import files to be under the `import` directory. Remove or comment it out to
# allow files to be loaded from anywhere in the filesystem; this introduces possible security problems. See the
# `LOAD CSV` section of the manual for details.
dbms.directories.import=import

# 将資料集(csv)下載下傳至 import 目錄
$ wget -O ./import/sanguo.csv 'https://raw.githubusercontent.com/wangfangye/kg-hongloumeng/master/data/data/三國演義/triples.csv'
--2021-02-26 19:21:46--  https://raw.githubusercontent.com/wangfangye/kg-hongloumeng/master/data/data/%E4%B8%89%E5%9B%BD%E6%BC%94%E4%B9%89/triples.csv
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.109.133, 185.199.110.133, 185.199.111.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.109.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 5926 (5.8K) [text/plain]
Saving to: ‘import/sanguo.csv’

import/sanguo.csv                          100%[======================================================================================>]   5.79K  --.-KB/s    in 0s      

2021-02-26 19:21:47 (26.6 MB/s) - ‘import/sanguo.csv’ saved [5926/5926]

$ ls ./import/
sanguo.csv

$ head ./import/sanguo.csv
"head","tail","relation","label"
"關羽","劉備","younger_sworn_brother","義弟"
"張飛","劉備","younger_sworn_brother","義弟"
"關羽","張飛","elder_sworn_brother","義兄"
"張苞","張飛","son","兒子"
"關興","關羽","son","兒子"
"關平","張苞","sworn","結拜"
"關平","關羽","adopted_son","義子"
"盧植","劉備","master","師傅"
"公孫瓒","劉備","friend","朋友"
           
建立節點

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MERGE (:Hero { name: line.tail});

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MERGE (:Hero { name: line.head});

MATCH (n) RETURN n;
Neo4j 示例:三國志人物關系圖譜
建立關系
$ awk -F ',' '{ print $4 }'  ./import/sanguo.csv | sort | uniq
"label"
"主公"
"義兄"
"義女"
"義子"
"義弟"
"義父"
"兒子"
"叔叔"
"同僚"
"同族"
"哥哥"
"堂弟"
"夫"
"女兒"
"女婿"
"妹妹"
"妻"
"妻子"
"妾"
"嫂子"
"嶽母"
"師傅"
"弟弟"
"徒弟"
"朋友"
"母親"
"父親"
"結拜"
"臣"
"讓位"
"諸侯"
           

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘義女’ MERGE (h1)-[:義女]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘義子’ MERGE (h1)-[:義子]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘義父’ MERGE (h1)-[:義父]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘徒弟’ MERGE (h1)-[:徒弟]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘同僚’ MERGE (h1)-[:同僚]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘妾’ MERGE (h1)-[:妾]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘女兒’ MERGE (h1)-[:女兒]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘讓位’ MERGE (h1)-[:讓位]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘哥哥’ MERGE (h1)-[:哥哥]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘義兄’ MERGE (h1)-[:義兄]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘父親’ MERGE (h1)-[:父親]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘朋友’ MERGE (h1)-[:朋友]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘夫’ MERGE (h1)-[:夫]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘主公’ MERGE (h1)-[:主公]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘師傅’ MERGE (h1)-[:師傅]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘臣’ MERGE (h1)-[:臣]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘母親’ MERGE (h1)-[:母親]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘嶽母’ MERGE (h1)-[:嶽母]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘諸侯’ MERGE (h1)-[:諸侯]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘同族’ MERGE (h1)-[:同族]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘嫂子’ MERGE (h1)-[:嫂子]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘兒子’ MERGE (h1)-[:兒子]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘女婿’ MERGE (h1)-[:女婿]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘堂弟’ MERGE (h1)-[:堂弟]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘結拜’ MERGE (h1)-[:結拜]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘叔叔’ MERGE (h1)-[:叔叔]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘妻子’ MERGE (h1)-[:妻子]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘弟弟’ MERGE (h1)-[:弟弟]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘妹妹’ MERGE (h1)-[:妹妹]->(h2);

LOAD CSV WITH HEADERS FROM ‘file:///sanguo.csv’ AS line FIELDTERMINATOR ‘,’

MATCH (h1:Hero),(h2:Hero) WHERE h1.name = line.head AND h2.name = line.tail AND line.label = ‘義弟’ MERGE (h1)-[:義弟]->(h2);

MATCH (n) RETURN n
Neo4j 示例:三國志人物關系圖譜

索引

RETURN (:Hero{name:“曹操”})-[*…1]->(:Hero)
Neo4j 示例:三國志人物關系圖譜

PROFILE RETURN (:Hero{name:“曹操”})-[…1]->(:Hero)

EXPLAIN RETURN (:Hero{name:“曹操”})-[…1]->(:Hero)

Neo4j 示例:三國志人物關系圖譜
建立索引

唯一索引:CREATE CONSTRAINT ON (h:Hero) ASSERT h.name IS UNIQUE

普通索引:CREATE INDEX FOR (n:Hero) ON (n.name)

:schema
Neo4j 示例:三國志人物關系圖譜
RETURN (:Hero{name:“曹操”})-[*…1]->(:Hero)
Neo4j 示例:三國志人物關系圖譜
删除索引

删除唯一索引:DROP CONSTRAINT ON (h:Hero) ASSERT h.name IS UNIQUE

删除普通索引:DRO INDEX index_53dd4bad

CQL(Cypher Query Language)

  • 語句結構
    [MATCH WHERE]
    [OPTIONAL MATCH WHERE]
    [WITH [ORDER BY] [SKIP] [LIMIT]]
    (CREATE [UNIQUE] | MERGE)*
    [SET | DELETE | REMOVE | FOREACH]*
    [RETURN [ORDER BY] [SKIP] [LIMIT]]
               
  • 運算符
    • +, -, *, /, %, ^

    • =, <>, <, >, <=, >=

    • AND, OR, XOR, NOT

    • 字元串運算符:

      +

    • 清單運算符:

      +, IN, [x], [x .. y]

    • 正規表達式運算符:

      =~

    • 字元串比對運算符:

      STARTS WITH, ENDS WITH, CONTAINS

# (變量别名:節點)-[關系别名:關系]->(變量别名2:節點2)-[:關系2]->()-[*..N]->(:節點3)
                  
# 查找
match p=shortestPath((m:Hero{name:"曹操"})-[*]-(n:Hero{name:"袁紹"})) return p;

MATCH (n:Hero) WHERE id(n)= 121 RETURN n;
MATCH (n:Hero) WHERE n.name="劉備" RETURN n;
MATCH (n:Hero{name:"劉備"}) RETURN n;
MATCH (n:Hero) WHERE n.name IN ["曹操", "劉備", "孫權"] RETURN n AS result;
MATCH (n:Hero) WHERE n.name STARTS WITH "曹" RETURN n;
MATCH (n:Hero) WHERE n.name <> "劉備" AND n.name STARTS WITH "劉" RETURN n;
MATCH (n:Hero) WHERE n.name STARTS WITH "劉" RETURN n.name UNION MATCH (n:Hero) WHERE n.name STARTS WITH "曹" RETURN n.name;
MATCH (m:Hero{name:"曹操"})-[:大王]->(n) WHERE n.name STARTS WITH "夏侯" WITH m, COUNT(n) AS count_n RETURN count_n

# 更新
match (n:Hero{name:"超昂"}) set n.name = "曹昂"

# 建立
WITH ["魏", "吳", "蜀"] as col FOREACH (v IN col | CREATE (:Country{name:v}))

match (n:Hero{name:"甄氏"}) return n;
create (:Hero{name:"甄氏"});
match (m:Hero{name:"袁熙"}),(n:Hero{name:"曹丕"}) create (o:Hero{name:"甄氏"}), (o)-[:妻子]->(m),(o)-[:妾]->(n);

match (m:Hero{name:"黃月英"}), (n:Hero{name:"諸葛亮"}) create (m)-[:妻子]->(n);        
match (m:Hero{name:"張飛"}), (n:Hero{name:"劉禅"}) create (m)-[:嶽父]->(n);

match p=((n:Hero{name:"孫權"})-[*..4]-(m:Hero)) return p;
match (m:Hero{name:"孫堅"}),(n:Hero{name:"袁術"}) create (m)-[:臣]->(n);
match (m:Hero{name:"曹嵩"}) create (:Hero{name:"曹騰"})-[:義父]->(m);
match (:Hero{name:"曹騰"})-[r:義父]-(:Hero{name:"曹嵩"}) delete r;
match (m:Hero{name:"曹騰"}) delete m;

# 删除
match (m:Hero{name:"甄氏"}) detach delete m

match (m:Hero)-[r:主公]->(n:Hero) create (m)-[:大王]->(n)
match (m:Hero)-[r:主公]->(n:Hero) delete r
           

用戶端驅動

Neo4j 示例:三國志人物關系圖譜
https://neo4j.com/download-center/#community

以 Node.js 為例

npm install neo4j-driver
           
const neo4j = require("neo4j-driver")

var driver = neo4j.driver("bolt://localhost:7687", neo4j.auth.basic("neo4j", "123456"))

var session = driver.session();

session.run("MATCH (h:Hero) WHERE h.name STARTS WITH '曹' RETURN h")
.then(function(result) {
    var records = result.records
    records.forEach((val, idx) => {
        console.log(val._fields[0].properties)
    })
}, function (err) {
    console.log(err)
})
           
$ node index.js
{ name: '曹操' }
{ name: '曹嵩' }
{ name: '曹真' }
{ name: '曹仁' }
{ name: '曹丕' }
{ name: '曹爽' }
{ name: '曹純' }
{ name: '曹植' }
           

參考

  • Cypher 文法:https://neo4j.com/docs/cypher-manual/4.2/
  • 示例資料集:https://github.com/wangfangye/kg-hongloumeng/tree/master/data/data
  • 《Neo4j 全棧開發》:https://www.jb51.net/books/701559.html
  • https://gitbook.cn/books/5a33782c5778440a9d906017/index.html
  • https://blog.csdn.net/RHJlife/article/details/108586578
  • http://www.openkg.cn/group