知识图谱知识的存储和检索

neo4j简介及安装

图形数据库(Graph Database)是NoSQL数据库家族中特殊的存在,用于存储丰富的关系数据,Neo4j是目前最流行的图形数据库,支持完整的事务,在属性图中,图是由顶点(Vertex),边(Edge)和属性(Property)组成的,顶点和边都可以设置属性,顶点也称作节点,边也称作关系(关系可以有向也可以无向),每个节点和关系都可以由一个或多个属性。Neo4j创建的图是用顶点和边构建一个有向图,其查询语言cypher已经成为事实上的标准。

关系型数据库只对单个Join操作进行优化查询,而多重Join操作查询的性能显著下降。图形数据库适合查询关系数据,由于图形遍历的局部性,不管图形中由多少节点和关系,根据遍历规侧,Neo4j只访问与遍历相关的节点,不受到总数据集大小的影响,从而保持期待的性能;

相应地,遍历的节点越多,遍历速度越慢,但是变慢是线性的,这使得图形数据库不适合做海量数据统计分析。对与存在大量丰富关系的数据,遍历的性能不受图形数据量大小的影响,这使得Neo4j成为解决图形问题的理想数据库。

安装jdk

https://www.cnblogs.com/huzixia/p/10402200.html

注意,如果你的jdk < 11,则安装可能会报错,可以不用管,安装好后修改DBMS的版本在3.x即可解决。

下载neo4j安装包

https://neo4j.com/download/?ref=get-started-dropdown-cta

保存Desktop Key。

运行下载好的可执行文件,填入之前复制的key。

安装完毕。

创建一个图数据库,这里我的jdk版本是1.8,所以我们选择3.x版本的RDBMS。

启动后点击网页浏览:

在网页端写cypher。

neo4j的基础语法

创建节点

 -- 注意这里的n只是一个变量,你也可以用其他字母代替
 create(n:节点分类{}'具体节点属性'})
 create(n:everweekup{name:'个人Blog'})

创建关系

我们再创建一个节点,并给这个节点和先前节点建立关系。

 create(n:article{name:'知识图谱存储和检索'})
 -- 这里加了个"->"表示有向,不加就是无向
 match(a:everweekup),(b:article)create(a)-[r:发表]->(b)

案例1:简单的电影知识图谱

手工创建一个简单小型的电影知识图谱,包括:

  • 创建电影节点
  • 创建导演节点
  • 创建演员节点
  • 创建电影和导演的关系
  • 创建电影和演员的关系

 -- 创建一个节点的命令:
 create(variable:label{key1:value1,key2:value2})
 -- 创建一个电影的命令:
 create(n:film{name:'肖申克的救赎',type:'犯罪',score:9.7})

同时创建多个节点

同时创建多部电影的命令:

 create(:film{name:'肖申克的救赎', type:'犯罪', score:9.7})
 ,(:film{name:'霸王别姬', type:'剧情', score:9.6})
 ,(:film{name:'辛德勒的名单', type:'历史', score:9.5})
 ,(:film{name:'盗梦空间', type:'剧情', score:9.3})
 ,(:film{name:'星际穿越', type:'剧情', score:9.3})
 ,(:film{name:'荆轲刺秦王', type:'历史', score:8.2})
 ,(:film{name:'绿里奇迹', type:'剧情', score:8.9})

同时创建多个导演的命令:

 create(:director{name:'弗兰克·德拉邦特'})
 ,(:director{name:'陈凯歌'})
 ,(:director{name:'史蒂文·斯皮尔伯格'})
 ,(:director{name:'克里斯托弗·诺兰'})

同时创建多个演员的命令:

 create(:actor{name:'蒂姆罗宾斯'})
 ,(:actor{name:'张国荣'})
 ,(:actor{name:'连姆·尼森'})
 ,(:actor{name:'马修·麦康纳'})
 ,(:actor{name:'张丰毅'})

解决重复创建节点问题

在neo4j里,可以两次create同一个节点,neo4j会给两个节点分配不同的id号。

 create(:actor{name:'蒂姆罗宾斯'})

如果我们想避免出现重复创建节点的情况,我们可以在第一个节点create后用merge来创建剩余的节点。

-- 删除节点,重新创建测试
MATCH (n:actor) DELETE n;

create(:actor{name:'蒂姆罗宾斯'});

merge(:actor{name:'蒂姆罗宾斯'})
merge(:actor{name:'张国荣'})
merge(:actor{name:'连姆·尼森'})
merge(:actor{name:'马修·麦康纳'})
merge(:actor{name:'张丰毅'})

创建电影关系

创建电影"肖申克的救赎"和导演"弗兰克·德拉邦特"关系的命令:

MATCH(a:director),(b:film)
WHERE a.name='弗兰克·德拉邦特' AND b.name='肖申克的救赎'
CREATE(a)-[r:direct]->(b)

创建电影和演员关系的命令:

MATCH(a:actor),(b:film)
WHERE a.name='张国荣' AND b.name='霸王别姬'
CREATE(a)-[r:play]->(b)
                     
MATCH(a:actor),(b:film)
WHERE a.name='蒂姆罗宾斯' AND b.name='肖申克的救赎'
CREATE(a)-[r:play]->(b)
                     
MATCH(a:actor),(b:film)
WHERE a.name='张丰毅' AND (b.name='霸王别姬' or b.name='荆轲刺秦王')
CREATE(a)-[r:play]->(b)

查询节点与关系

  • 查询某个电影
MATCH(a:film)
WHERE a.name='霸王别姬'
RETURN a
  • 查询某个标签下的所有节点
MATCH(a:director)
RETURN a
  • 查询两个节点间的关系
MATCH(:director {name:'弗兰克·德拉邦特'})-[r]->(:film {name:'肖申克的救赎'}) RETURN r
{
  "identity": 210113,
  "start": 36972,
  "end": 36952,
  "type": "direct",
  "properties": {

  }
}

也可以直接返回type:

MATCH(:director {name:'弗兰克·德拉邦特'})-[r]->(:film {name:'肖申克的救赎'}) RETURN type(r)
>> "direct"

删除节点和关系

  • 删除某个节点
MATCH(a:actor)
WHERE a.name='马修·麦康纳'
DELETE a
  • 删除某个标签的所有节点
-- 如果删除的节点还有对应关系存在,那么我们无法删除,需要先删除关系后才能和删除节点
MATCH(a:actor)
DELETE a
  • 删除所有节点
-- 同上一样要注意关系
MATCH(n)
DELETE n
  • 删除两个节点的关系
MATCH(:actor{name:'张国荣'})-[r]->(:film{name:'霸王别姬'})
DELETE r

  • 删除某节点相关的关系
MATCH(:actor{name:'张国荣'})-[r]->()
DELETE r
  • 删除某个标签全部的关系
MATCH ()-[r]->(:film)
DELETE r

属性相关操作

  • 增加节点属性
MATCH (n:film) 
WHERE n.name='盗梦空间' SET n.language='english'

  • 删除节点属性
MATCH (n:film) 
WHERE n.name='盗梦空间' REMOVE n.language

neo4j批量构建节点和关系

案例2:股票数据批量导入知识图谱

stock.csv文件

namecode
namech_namecode

stock_exchange.csv文件

stock_codeexchange_code

1.首先把数据文件放到neo4j的对应数据库的import文件下

接着返回neo4j网页,导入数据,根据exchange文件导入exchange节点数据:

load csv with headers from 'file:///exchange.csv' as line 
create(:exchange{name:line.name, ch_name:line.ch_name, code:line.code})

导入股票节点:

load csv with headers from 'file:///stock.csv' as line 
create(:stock{name:line.name, code:line.code})

基于stock_exchange.csv文件批量构建节点关系

load csv with headers from 'file:///stock_exchange.csv' as line 
match (a:stock{code:line.stock_code}),(b:exchange{code:line.exchange_code})
create(a)-[r:交易所]->(b)

py2neo的使用

案例3:批量构建红楼梦人物知识图谱

新建一个数据库

由于我是使用社区版的neo4j+neo4j-desktop,所以我这边需要去社区版修改相应配置才能新建一个图数据库实例。

 D:\cmder\cmder 
$ cd D:\neo4j\neo4j-community-3.5.26\conf

 D:\neo4j\neo4j-community-3.5.26\conf 
$ ls
neo4j.conf

 D:\neo4j\neo4j-community-3.5.26\conf 
$ vim neo4j.conf

>> 配置文件:dbms.active_database=DoRM.db

修改好后,我们在D:\neo4j\neo4j-community-3.5.26\data\databases路径下新建一个文件夹命名DoRM.db

之后在Desktop中新建一个数据库即可。666666

然后启动该数据库,进入neo4j网页即可。

Python连接操作neo4j

python安装pip install py2neo

运行jupyter notebook

import py2neo
from py2neo import Graph

# 初始化graph
domrb_graph = Graph('http://localhost:7474/', user='neo4j', password='xxx')

test_node1 = Node("Person", name="小明")
test_node2 = Node("Person", name="小鸿")
# 可以初始化后再给节点添加属性
test_node1['age'] = 18
test_node2['age'] = 16

test_node1['sex'] = "男"
test_node2['sex'] = "男"

# 创建节点
from py2neo import Node
# 往图中放入节点
domrb_graph.create(test_node1)
domrb_graph.create(test_node2)

# 创建关系
from py2neo import Relationship
friend = Relationship(test_node1, "friend", test_node2)
domrb_graph.create(friend)

查询:

from py2neo import NodeMatcher
matcher = NodeMatcher(domrb_graph)
print(matcher.match("Person", name="小明"))
>> <py2neo.matching.NodeMatch object at 0x0000029C2849D388>
# 要查看具体信息
print(matcher.match("Person", name="小明").first())
>> (_0:Person {age: 18, name: '\u5c0f\u660e', sex: '\u7537'})

查看红楼梦csv文件

构建红楼梦知识图谱

from py2neo import Graph, Node, Relationship, NodeMatcher
import pandas as pd
from tqdm import tqdm

g = Graph("http://localhost:7474/", user="neo4j", password="xxx")

df = pd.read_csv('./DreamOfRedChamber.csv')
df.head()

# 获取df得迭代器,每次生成一行数据
for i, row in tqdm(df.iterrows()):
    # 构建节点
    start = Node("Person", name=row['head'])
    g.create(start)
    end = Node("Person", name=row['tail'])
    g.create(end)
    # 构建节点关系
    relation = Relationship(start, row['label'], end)
    g.create(relation)

去看看Desktop:

# 另一种查询方式:找出林黛玉得丫鬟
g.run("match(n:Person{name:'林黛玉'})-[r:丫鬟]->(k) return k")

neo4j案例

朋友圈关系检测

六度空间理论:

关系数据库不能有效表达和描述用户之间广泛而复杂的网络关系。

而图数据库天然适用关系网络结构,构建方便,查询速度快有非常大的优势。

查找一度关系

match(:Person{name:'小北'})-[:认识]->(n) return n

查找二度关系

-- *..2返回与小北在二级关系内的节点
-- 注意这个时候关系`-`没有箭头
match(:Person{name:'小北'})-[*..2]-(n) return n

查找两个人间最短路径

-- '*'表示路径范围是不限的,会遍历所有路径去找最短路径
match n=shortestPath((:Person{name:'小Y'})-[*]->(:Person{name:'小明'})) return n
-- *..6:事先知道最长路径不会超过六度关系,让其在六度关系内查找最短路径,缩小搜索范围
match n=shortestPath((:Person{name:'小Y'})-[*..6]->(:Person{name:'小明'})) return n

查找相关联的人并确保此人至少有两个外向关系

// otherPerson是目标要查找的人
match (:Person{name:'小明'})--(otherPerson)-->()
// with作为管道,将otherPerson变量传递下去,count(*)指的是`-->()`有多少种关系,查找otherPerson关系并计数
with otherPerson,count(*) as relations
// where过滤符合条件的
where relations > 1
return otherPerson

with:管道传递的作用

欺诈环检测

身份欺诈:一个罪犯可能会从十个不同的人那里窃取身份信息。然后,罪犯将社会安全号码、地址、电话号码和电子邮件地址进行混合和匹配,以创建“新的”合成身份,然后用于开立银行和信用卡账户以及个人信用额度,来骗取资金。

比如说某个信息被重复使用的人很多,那么这可能是一个很大的欺诈环,背后很可能存在大的犯罪团伙。

创建账户

//创建三个人的账户
create (accountHolder1:AccountHolder{
FirstName:"John",
LastName:"Doe",
UniqueId:"JohnDoe"
})
,(accountHolder2:AccountHolder{
FirstName:"Jane",
LastName:"Appleseed",
UniqueId:"JaneAppleseed"
})
,(accountHolder3:AccountHolder{
FirstName:"Matt",
LastName:"Smith",
UniqueId:"MattSmith"
})

创建地址

create (address1:Address{
Street:"123 NW 1st Street",
City:"San Francisco",
State:"California",
ZipCOde:"94101"
})

关联地址

//把账户人1、账户人2、账户人3关联到地址1上
match (a:AccountHolder),(b:Address)
where a.UniqueId='JaneAppleseed' and b.ZipCOde='94101'
create(a)-[r:HAS_ADDRESS]->(b)
                            
match (a:AccountHolder),(b:Address)
where a.UniqueId='JohnDoe' and b.ZipCOde='94101'
create(a)-[r:HAS_ADDRESS]->(b)
                            
match (a:AccountHolder),(b:Address)
where a.UniqueId='MattSmith' and b.ZipCOde='94101'
create(a)-[r:HAS_ADDRESS]->(b)

创建电话号码

//创建电话号码
create (phoneNumber1:PhoneNumber{
PhoneNumber:"555-555-555"
})

//把账户1、账户2关联到电话号码1上
match (a:AccountHolder),(b:PhoneNumber)
where a.UniqueId='JohnDoe' and b.PhoneNumber='555-555-555'
create(a)-[r:HAS_PHONENUMBER]->(b)
                                
match (a:AccountHolder),(b:PhoneNumber)
where a.UniqueId='JaneAppleseed' and b.PhoneNumber='555-555-555'
create(a)-[r:HAS_PHONENUMBER]->(b)

创建社会安全码

//创建社会安码 SSN1
create (ssn1:SSN{
SSN:"241-23-1234"
})
;
//把账户人2、账户人3关联到SSN1
match (a:AccountHolder),(b:SSN)
where a.UniqueId='JaneAppleseed' and b.SSN='241-23-1234'
create(a)-[r:HAS_SSN]->(b)
;               
match (a:AccountHolder),(b:SSN)
where a.UniqueId='MattSmith' and b.SSN='241-23-1234'
create(a)-[r:HAS_SSN]->(b)
;
//创建社会安全码SSN2 关联到账户人1
create (ssn2:SSN{SSN:"241-23-4567"})
;
match (a:AccountHolder), (b:SSN) 
where a.UniqueId='JohnDoe' and b.SSN='241-23-4567'
create (a)-[:HAS_SSN]->(b)

创建信用卡

//创建信用卡1 关联到账户1
create (creditCard1:CreditCard{
AccountNumber:"1234567890123456",
Limit:5000,
Balance:1442.23,
ExpirationData:'02-20',
SecurityCode:'456'
})
;
match (a:AccountHolder), (b:CreditCard) 
where a.UniqueId='JohnDoe' and b.AccountNumber='1234567890123456'
create (a)-[:HAS_CREDITCARD]->(b)
;                
//创建信用卡2 关联到账户2
create (creditCard2:CreditCard{
AccountNumber:"2345678901234567",
Limit:4000,
Balance:2345.56,
ExpirationData:'02-20',
SecurityCode:'456'
})
; 
match (a:AccountHolder), (b:CreditCard) 
where a.UniqueId='JaneAppleseed' and b.AccountNumber='2345678901234567'
create (a)-[:HAS_CREDITCARD]->(b)
;

创建银行账户

///创建银行账户1 关联到账户1
create (bankAccount1:BankAccount{
AccountNumber:"2345678901234567",
Balance:7054.43
})
;
match (a:AccountHolder), (b:BankAccount) 
where a.UniqueId='JohnDoe' and b.AccountNumber='2345678901234567'
create (a)-[:HAS_BANKACCOUNT]->(b)
;
 
//创建银行账户2 关联到账户2
create (bankAccount2:BankAccount{
AccountNumber:"3456789012345678",
Balance:4231.12
})
;
match (a:AccountHolder), (b:BankAccount) 
where a.UniqueId='JaneAppleseed' and b.AccountNumber='3456789012345678'
create (a)-[:HAS_BANKACCOUNT]->(b)
;
 
//创建银行账户3 关联到账户3
create (bankAccount3:BankAccount{
AccountNumber:"4567890123456789",
Balance:12345.45
})
;
match (a:AccountHolder), (b:BankAccount) 
where a.UniqueId='MattSmith' and b.AccountNumber='4567890123456789'
create (a)-[:HAS_BANKACCOUNT]->(b)
;
//创建无抵押贷款2并关联到账户人2
create (unsecuredLoan2:UnsecureLoan{
AccountNumber:'4567890123456789-0',
Balance:90453,
APR:0.0541,
LoanAmount:12000
})
;
match (a:AccountHolder), (b:UnsecureLoan) 
where a.UniqueId='JaneAppleseed' and b.AccountNumber='4567890123456789-0'
create (a)-[:HAS_UNSECUREDLOAN]->(b)
;
 
//创建无抵押贷款3并关联到账户人3
create (unsecuredLoan3:UnsecureLoan{
AccountNumber:'5678901234567890-0',
Balance:16341.95,
APR:0.0341,
LoanAmount:22000
})
;
match (a:AccountHolder), (b:UnsecureLoan) 
where a.UniqueId='MattSmith' and b.AccountNumber='5678901234567890-0'
create (a)-[:HAS_UNSECUREDLOAN]->(b)
;
 
//创建电话号码3 并关联到账户3
create (phoneNumber2:PhoneNumber
{PhoneNumber:'555-555-1234'
})
;
match (a:AccountHolder), (b:PhoneNumber) 
where a.UniqueId='MattSmith' and b.PhoneNumber='555-555-1234'
create (a)-[:HAS_UNSECUREDLOAN]->(b)
;

检测1

查找共享多个合法信息的帐户持有人,共同信息越多,所说明此欺诈环越可疑,并按欺诈环的大小排序。

// 匹配所有与账户所有者有联系的节点
match (a:AccountHolder)-[]->(b)
// 对当前节点,统计当前节点连接的账户,将连接当前节点的账户去重收集
with b,count(a) as RingSize,collect(distinct a.UniqueId) as FraudRing
// 过滤小于1的
where RingSize>1
// 返回结果,labels(b):节点b的类别
return FraudRing,labels(b) as ContractType,RingSize
// 按照RingSize降序排列
order by RingSize desc 

检测2

查找共享多个合法信息的帐户持有人,并查询其可能的金融风险,并按风险大小排序。

// 匹配所有与账户所有者有联系的节点
match (a:AccountHolder)-[]->(b)
// 对当前节点b,统计连接当前节点b的账户所有者数,并把a,b变量通过管道传递下去
with b, count(a) as RingSize
// 继续匹配a->b所有关系
match (a)-[]->(b), 
// 查找匹配a、c两节点是信用卡和信用贷款关系的
(a)-[r:HAS_CREDITCARD|HAS_UNSECUREDLOAN]->(c)
// 收集去重账户所有者id, 节点变量b, 管道传下来的变量RingSize,并将这些变量传到下层管道
with collect(distinct a.UniqueId) as FraudRing,b,RingSize,
// sum(...) as FinancialRisk 整个语句就是我们要传到下层的金融分析管道变量之一
sum (case type(r) 
when 'HAS_CREDITCARD' then c.Limit
when 'HAS_UNSECUREDLOAN' then c.Balance
else 0
end) as FinancialRisk
where RingSize>1
return FraudRing,labels(b) as ContractType,RingSize,round(FinancialRisk) as FinancialRisk 
order by FinancialRisk desc 

参考

官方语法查阅连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

被代码搞废的挖掘机

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值