第一篇
介绍
neo4j是一个图形数据库也可以叫做知识图谱,知识图谱的数据包含实体、属性、关系。知识图谱就是通过不同知识的关联性形成一个网状的知识结构。当前AI领域热门的计算机图像、语音识别甚至是NLP,其实都是AI的感知能力,真正AI的认知能力,就要靠知识图谱。
知识图谱目前的应用主要在搜索、智能问答、推荐系统等方面。
像我们在使用社交软件中经常会推荐你可能认识的人、共同关注的人、你的朋友也关注了他 等等的关系网推荐,这个在社交领域中叫做关注模型,我们下面尝试使用neo4j来实现它。
Neo4J简介
图形数据库也被称为图形数据库管理系统(GDBMS),现发展比较成熟的图数据库有Neo4j、OracleNoSQL、OrientDB、HypherGraphDB和GraphBase等
其中Neo4j是基于Java语言编写的图形数据库,它采用节点和关系的形式存储信息,并在此基础上提供界面友好的可视化演示,Neo4j图形数据库的主要组成有:
- 节点:即实体,用于表示一个单独存在的个体,节点一般包含多个属性
- 关系:也就是“边”,两个节点靠关系才能连接,每个关系也具有它自己的名词,可以通过Cypher检索关系名称来查找具有该关系的所有节点
- 属性:可以看作是节点的扩展描述,id、名称这些也属于节点的属性,详细的属性需要通过数据浏览器的Text标签进行查看
- 标签:即分组,Neo4j在建立节点或关系时要求事先分组
- 数据浏览器:Neo4j自己的可视化界面,用于提供用户执行Cypher查询命令并查看输出文本和图形
Neo4J安装
在我之前的文章中使用linux安装了neo4j,可以借鉴:Linux在线安装一个Neo4j图数据库
Neo4J基础操作
Neo4J安装后已经提供了可视化页面,并且可以直接执行语句来操作数据和查看数据库关系即标签,可谓非常好用。
安装好后访问neo4j的web页面:http://你的ip地址:7474/browser/
主页面是有执行命令的窗口,左侧导航栏有展示选择的数据库及数据库的节点总数和拥有的标签和关系,可以清晰看到我们这个数据库拥有哪些标签跟关系
Cypher查询语言
Cypher是Neo4J的声明式图形查询语言,允许用户不必编写图形结构的遍历代码,就可以对图形数据进行高效的查询。Cypher的设计目的类似SQL,适合于开发者以及在数据库上做点对点模式(ad-hoc)查询的专业操作人员。其具备的能力包括: - 创建、更新、删除节点和关系 - 通过模式匹配来查询和修改节点和关系 - 管理索引和约束等。
常用命令如下:
代码语言:javascript复制# 删除以往的所有节点和关系,MATCH是匹配操作,()表示一个节点,n是标识符
MATCH (n) DETACH DELETE n
# 创建一个标签为Person的节点,节点有一个name属性,属性值为'John'
CREATE (n:Person{name:'John'}) RETURN n
# 从a到b建立起FRIENDS关系,关系有一个since属性,属性值为2001
MATCH (a:Person{name:'Liz'}),(b:Person{name:'Mike'})MERGE (a)-[:FRIENDS{since:2001}]->b
# 查询在Boston出生的所有Person
MATCH (a:Person)-[:BORN_IN]->(b:location{city:'Boston'}) RETURN a,b
# 查询所有具有对外关系的节点
MATCH (a)-->() RETURN a
# 查询所有具有关系的节点
MATH (a)--() RETURN a
# 查询所有具有对外关系的节点,并返回节点的name属性值和关系类型
MATCH (a)-[r]->() RETURN a.name, type(r)
# 给a节点设置一个age属性,属性值为34
MATCH (a:Person{name:'Liz'}) SET a.age = 34
# 删除a节点的test属性
MATCH ... REMOVE a.test
# 删除a节点
MATCH ... DELETE a
# 只删除关系
match (n:Person{name:"龙傲天"})<-[r:BIGBROTHER]-(m:Person{name:"龙小弟"}) DELETE r
# 添加关系属性
MATCH p=({name:'龙二弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-02"} RETURN p;
neo4j实战
我们接下来使用neo4j实现社交的关系模型
1. 清除数据库
将数据库初始化一下,保证我们的操作属性不受影响,在运行框内执行如下命令
代码语言:javascript复制MATCH (n) DETACH DELETE n
这条命令中MATCH为匹配,小括号()中写匹配的节点,n为标识符,DETACH DELETE为操作。 组合下来就是匹配标识符为n的进行删除
如上图执行成功清除命令
2. 创建人物
房间腾好了,那就该让我们的人物入住了,先创建我们的第一位嘉宾:龙傲天
代码语言:javascript复制CREATE (n:Person {name:'龙傲天'}) RETURN n
CREATE是创建操作,Person是标签,代表节点的类型。花括号{}代表节点的属性,属性类似Python的字典。 这条语句的含义就是创建一个标签为Person的节点,该节点具有一个name属性,属性值是龙傲天。
创建好的效果如下:
看到我们傲天哥先来了,小弟都还不到,多不合适,赶紧创建天哥的小弟:
代码语言:javascript复制CREATE (n:Person {name:'龙小弟'}) RETURN n;
CREATE (n:Person {name:'龙二弟'}) RETURN n;
CREATE (n:Person {name:'龙三弟'}) RETURN n;
CREATE (n:Person {name:'龙四弟'}) RETURN n;
CREATE (n:Person {name:'龙五弟'}) RETURN n;
创建成功后我们再来看一下:
小弟是有了,但是他们现在跟天哥没关系啊,这可不行,哪有小弟不认大哥的道理,那接下来就该让小弟跟大哥建立关系。
创建小弟跟大哥的关系:
代码语言:javascript复制MATCH (a:Person {name:'龙小弟'}),
(b:Person {name:'龙傲天'})
MERGE (a)-[:BIGBROTHER]->(b)
执行后我们来看看龙小弟拜见大哥了吗:
根据关系图看到龙小弟有一个大哥的关系指向了龙傲天,这样傲天哥就有了一个小弟,接下来将其他小弟也拜见龙傲天,命令如下
代码语言:javascript复制MATCH (a:Person {name:'龙二弟'}), (b:Person {name:'龙傲天'}) MERGE (a)-[:BIGBROTHER]->(b);
MATCH (a:Person {name:'龙三弟'}), (b:Person {name:'龙傲天'}) MERGE (a)-[:BIGBROTHER]->(b);
MATCH (a:Person {name:'龙四弟'}), (b:Person {name:'龙傲天'}) MERGE (a)-[:BIGBROTHER]->(b);
MATCH (a:Person {name:'龙五弟'}), (b:Person {name:'龙傲天'}) MERGE (a)-[:BIGBROTHER]->(b);
看看效果:
这样一看龙傲天有了点大哥的样子,众星捧月,但是我们想知道小弟们都是什么时候跟着龙傲天的,好方便以后排座位,这样就需要给关系加属性了,如下:
代码语言:javascript复制CREATE (n:Person {name:'龙六弟'}) RETURN n;
MATCH (a:Person {name:'龙六弟'}),
(b:Person {name:'龙傲天'})
MERGE (a)-[:BIGBROTHER {since:"2022-01-01"}]->(b)
这时候新来了个龙六弟,并且可以看到新来的龙六弟有了时间的属性,这样以后龙六弟就可以说我跟老大风里来雨里去的时候你们还玩泥巴呢。
这时候其他几个小弟不乐意了,我们最先跟的老大,怎么先有时间铭牌的是这个新来的,这样就需要解决之前兄弟没有身份的问题,但是之前跟的小弟怎么加属性的,总不能让龙六弟在它的五个哥哥之前吧,所以下面就是怎么修改关系的属性
代码语言:javascript复制MATCH p=({name:'龙小弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-01"} RETURN p
龙小弟拿到时间铭牌非常高兴,并且时间也比龙六弟早,不屑的对龙六弟哼了一声,其他几个兄弟看到也赶紧要求加上,我们也给他们分发时间名牌
代码语言:javascript复制MATCH p=({name:'龙二弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-02"} RETURN p;
MATCH p=({name:'龙三弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-03"} RETURN p;
MATCH p=({name:'龙四弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-04"} RETURN p;
MATCH p=({name:'龙五弟'})-[r:BIGBROTHER ]->() SET r={since:"2017-01-05"} RETURN p;
傲天哥的基础团队有了矛盾也消除了,该出去逛逛了,可该去哪呢?
目前我们的图谱中可只有人物的标签,傲天哥想逛逛街发展一下剧情都没法发展,那就应该创建地点的标签和属性建立跟我们傲天哥的关系,这些东西就放到下一章吧,傲天哥也得休息休息。
看到这里的同学如果想要实践一下却没有自己的neo4j可以使用我的,地址放在这里了:http://110.40.220.41:7474/browser/
第二篇
前言
上一篇中我们使用neo4j已经初步建立了人物标签跟关系属性,但是想要建立一个知识图谱集肯定一个标签是不够的,我们还需要与它相关的标签建立节点之间的关系,这一篇文我们将人物标签与其他标签进行关联组合他们之间的关系
创建标签
建立地点标签
创建几个城市标签跟邮编
代码语言:javascript复制CREATE (n:Location {city:'北京', post_code:'100000'});
CREATE (n:Location {city:'成都', post_code:'610000'});
CREATE (n:Location {city:'重庆', post_code:'400000'});
CREATE (n:Location {city:'石家庄', post_code:'050000'});
CREATE (n:Location {city:'深圳', post_code:'518000'});
创建好后看一下效果
我们的地区节点已经都有了,但是有点杂乱无章,那就将他们归集一下,先把国家跟地级市的所属省也创建出来,国家跟省没邮编的使用首都跟盛会邮编
代码语言:javascript复制CREATE (n:Location {city:'中国', post_code:'100000'});
CREATE (n:Location {city:'四川省', post_code:'610000'});
CREATE (n:Location {city:'河北省', post_code:'050000'});
CREATE (n:Location {city:'广东省', post_code:'510000'});
看看效果:
我们的地区节点又多了一些,现在开始安排他们之间的关系,我们的国家肯定是最大的,省跟直辖市指向国家,城市指向省会
代码语言:javascript复制MATCH (a:Location {city:'北京'}), (b:Location {city:'中国'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'重庆'}), (b:Location {city:'中国'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'四川省'}), (b:Location {city:'中国'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'河北省'}), (b:Location {city:'中国'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'广东省'}), (b:Location {city:'中国'}) MERGE (a)-[:SUPERIOR]->(b);
执行后看一下效果
上面的图片看到省跟直辖市单位的上级关系已经指向了我们中国节点,接下来将地级市关联到省的节点上
代码语言:javascript复制MATCH (a:Location {city:'深圳'}), (b:Location {city:'广东省'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'石家庄'}), (b:Location {city:'河北省'}) MERGE (a)-[:SUPERIOR]->(b);
MATCH (a:Location {city:'成都'}), (b:Location {city:'四川省'}) MERGE (a)-[:SUPERIOR]->(b);
看看地区的图谱维护的如何
从图上看到地区的标签关系已经都维护上了,但是人物跟地区之前还没有关系,我们将它们也建立上,这次在建立属性上可以加上时间标签,代表这个人什么时候在这个城市,这时候我们的傲天哥就要出场了,维护傲天哥跟地区的关系
代码语言:javascript复制MATCH (a:Person {name:'龙傲天'}),
(b:Location {city:'石家庄'})
MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b)
上面的命令的意思就是,傲天哥从2022年6月4号到达了石家庄开始了它的闯荡生涯
这样我们的傲天哥就有了第一站的地点,其他小弟也跟着傲天哥进行落地
代码语言:javascript复制MATCH (a:Person {name:'龙小弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
MATCH (a:Person {name:'龙二弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
MATCH (a:Person {name:'龙三弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
MATCH (a:Person {name:'龙四弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
MATCH (a:Person {name:'龙五弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
MATCH (a:Person {name:'龙六弟'}), (b:Location {city:'石家庄'}) MERGE (a)-[:ARRIVE {since:'2022-06-04'}]->(b);
这样人物跟地点的关系就初步维护好了
第三篇
前言
之前的文章中地点跟人物标签已经有了,人物跟地点的关系也已经组织好了,但是像我们的人物在这个地点什么时间做了什么事情我们还是一片空白,所以我们需要建立事件关系,事件关系不只是单标签的关联了。
事件分级
一个事件的简单分布:
- 第一级:事件关系(触发关系、被触发关系)
- 第二级:标签(人物、地点)
- 第三级:属性(时间、事件原因)
解析事件
定义好事件分级后建立事件项,先建立一个事件,比如龙傲天请叶良辰在石家庄的2022年6月6号喝咖啡,我们分解一下这个时间的属性跟关系。
事件属性:
- 事件名称:龙傲天请叶良辰在石家庄的2022年6月6号喝咖啡
- 事件动作:喝咖啡
- 触发时间:2022-06-06
- 事件触发标签:人物标签
- 事件触发节点:龙傲天
- 事件被触发标签:人物标签
- 事件被触发节点:叶良辰
关系属性:
- 时间:2022-06-06
- 标签:触发关系-标签、被触发关系-标签
- 角色:触发关系-节点、被触发关系-节点
创建事件
解析好后我们创建上面规划好属性跟关系的事件节点,先创建叶良辰的人物节点
代码语言:javascript复制CREATE (n:Person {name:'叶良辰'}) RETURN n
建立这个事件节点:
代码语言:javascript复制CREATE (n:Event {name:'龙傲天请叶良辰在石家庄的2022年6月6号喝咖啡',action:'喝咖啡',dateTime:'2022-06-06 15:00:00',triggerTag:'Person',triggerNode:'龙傲天',isTriggerTag:'Person',isTriggerNode:'叶良辰'}) RETURN n;
事件建立好后建立人物标签与事件的触发关系,关系属性为规划好的
- 时间:2022-06-06
- 标签:触发关系-标签、被触发关系-标签
- 角色:触发关系-节点、被触发关系-节点
MATCH (a:Person {name:'龙傲天'}),
(b:Event {name:'龙傲天请叶良辰在石家庄的2022年6月6号喝咖啡'})
MERGE (a)-[:Trigger{dateTime:'2022-06-06 15:00:00',tag:'Person',node:'龙傲天'}]->(b)
关系产生后的效果
建立被触发关系
代码语言:javascript复制MATCH (a:Person {name:'叶良辰'}),
(b:Event {name:'龙傲天请叶良辰在石家庄的2022年6月6号喝咖啡'})
MERGE (b)-[:ISTrigger{dateTime:'2022-06-06 15:00:00',tag:'Person',node:'叶良辰'}]->(a)
看一下执行完成后的关联关系
查询一下事件对外的所以关系节点
代码语言:javascript复制MATCH (a:Event)-->(b) RETURN a,b
目前只有一个事件
如果想查询指定对外关系被触发关系的指令如下
代码语言:javascript复制MATCH (a)-[:ARRIVE]->(b) RETURN a,b
如果想要筛选指定的标签跟关系在a,b项后加需要筛选的标签即可,如下指令
代码语言:javascript复制MATCH (a:Person)-[:ARRIVE]->(b) RETURN a,b
指令内容为筛选标签为Preson的对外关系为ARRIVE的所有节点
第四篇
前言
在前三篇中我们基础学会了怎么创建标签节点以及关联关系及设置属性的操作,本篇我们实际模拟我们的社交软件中的好友推荐、共同关注等的功能实现。
我们在实现前先考虑一下好友的关系及节点属性,人物之间存在好友关系,关系上带有关系类型及建立时间等属性
实现
创建节点
先创建几个社交用户标签的节点
代码语言:javascript复制create (n:SocialUser {name:'李白'}) return n;
create (n:SocialUser {name:'汪伦'}) return n;
create (n:SocialUser {name:'孟浩然'}) return n;
create (n:SocialUser {name:'杜甫'}) return n;
create (n:SocialUser {name:'王昌龄'}) return n;
create (n:SocialUser {name:'贺知章'}) return n;
create (n:SocialUser {name:'高适'}) return n;
create (n:SocialUser {name:'李阳冰'}) return n;
create (n:SocialUser {name:'元丹丘'}) return n;
create (n:SocialUser {name:'孔巢父'}) return n;
create (n:SocialUser {name:'崔成甫'}) return n;
上面的都是李白的好友,所以我们指向关系不需要筛选,直接指向标签
代码语言:javascript复制match (a:SocialUser {name:'李白'}),(b:SocialUser)
where b.name <> '李白' merge (a)-[:FRIEND]->(b)
return a,b
查看结果
接下来创建杜甫的好友,像李白、高适这些已经有的就不需要创建了
代码语言:javascript复制create (n:SocialUser {name:'王维'}) return n;
create (n:SocialUser {name:'严武'}) return n;
建立杜甫的好友关系
代码语言:javascript复制match (a:SocialUser {name:'杜甫'}),(b:SocialUser {name:'李白'}) merge (a)-[:FRIEND]->(b);
match (a:SocialUser {name:'杜甫'}),(b:SocialUser {name:'高适'}) merge (a)-[:FRIEND]->(b);
match (a:SocialUser {name:'杜甫'}),(b:SocialUser {name:'王维'}) merge (a)-[:FRIEND]->(b);
match (a:SocialUser {name:'杜甫'}),(b:SocialUser {name:'严武'}) merge (a)-[:FRIEND]->(b);
从图谱中可以看到高适是李白跟杜甫的共同好友,如果现在加入了一个我的元素,我同时认识李白跟杜甫,那么可以从李白跟杜甫共同关注的人中找到我可能认识的人。 但是在正式的具体的分析过程中还要加入地域、公司、性别、喜好等属性来加强推荐人的准确性。
建立了基础的社交人物标签跟好友关系信息后,我们先补充各个人物的好友节点,丰富我们的知识图谱并可以更好的看到数据的展示效果、然后通过查询语句来查看共同关注、可能认识的人、我有几个好友也关注了他等功能
创建节点
王维的好友:张九龄、李龟年、孟浩然、岑参、元常、神会禅师、晁衡 李白的好友里已经有王维的好友:孟浩然 创建剩余的好友:张九龄、李龟年、岑参、元常、神会禅师、晁衡
代码语言:javascript复制create (n:SocialUser {name:'张九龄'}) return n;
create (n:SocialUser {name:'李龟年'}) return n;
create (n:SocialUser {name:'岑参'}) return n;
create (n:SocialUser {name:'元常'}) return n;
create (n:SocialUser {name:'神会禅师'}) return n;
create (n:SocialUser {name:'晁衡'}) return n;
上面的都是李白的好友,所以我们指向关系不需要筛选,直接指向标签
代码语言:javascript复制match (a:SocialUser {name:'王维'}),(b:SocialUser)
where b.name in ['张九龄','李龟年','孟浩然','岑参','元常','神会禅师','晁衡']
merge (a)-[:FRIEND]->(b)
return a,b
查看结果