Neo4j 之水浒传梁山好汉图谱构建与关系推演

Neo4j 之水浒传梁山好汉图谱构建与关系推演
1. 从零构建水浒人物图谱第一次接触Neo4j时我就被它处理复杂关系的能力惊艳到了。当时正好在重读《水浒传》突然想到这不就是现成的绝佳案例吗108将之间错综复杂的关系用传统数据库处理简直是一场噩梦但用Neo4j却能优雅解决。我们先从最基础的节点创建开始。在Neo4j中每个人物都是一个独立节点可以携带各种属性。比如创建宋江这个核心人物CREATE (slj:好汉 { 姓名: 宋江, 绰号: 及时雨, 星宿: 天魁星, 排名: 1, 原职业: 押司, 上山前居住地: 郓城县 })注意到我给节点打了好汉这个标签这是Neo4j中的分类方式。属性部分完全可以根据需要扩展比如加上生辰、武器、特长等。用同样的方式我们可以批量创建其他好汉CREATE (ls:好汉 {姓名: 卢俊义, 绰号: 玉麒麟, 星宿: 天罡星, 排名: 2}) CREATE (wy:好汉 {姓名: 吴用, 绰号: 智多星, 星宿: 天机星, 排名: 3})实际项目中我建议先用CSV文件整理好所有人物数据然后用LOAD CSV命令批量导入效率会高很多。我曾经手动创建30多个节点就花了半小时后来改用批量导入108将的数据5分钟就搞定了。2. 定义复杂关系网络水浒人物关系的精妙之处在于它的多样性。在Neo4j中我们可以用不同类型的关系来准确描述这些连接。先看几个典型关系类型结义兄弟如宋江与李逵师徒关系如林冲与曹正敌对关系如梁山与高俅隶属关系好汉与原来所属山头亲属关系如张清与琼英用Cypher创建关系非常直观。比如建立宋江与李逵的结义关系MATCH (sj:好汉 {姓名: 宋江}), (lk:好汉 {姓名: 李逵}) CREATE (sj)-[r:结义兄弟]-(lk) SET r.结义地点 江州 SET r.结义时间 宣和二年关系也可以带属性这个特性太有用了。我在分析梁山派系时就给不同山头的关系加上了归顺时间属性后来做时序分析派上了大用场。再举个复杂点的例子——三打祝家庄的关系网// 创建祝家庄势力 CREATE (zjz:势力 {名称: 祝家庄}) CREATE (zl:人物 {姓名: 祝龙}) CREATE (zf:人物 {姓名: 祝虎}) CREATE (zb:人物 {姓名: 祝彪}) // 建立关系 MATCH (zjz:势力 {名称: 祝家庄}), (zy:好汉 {姓名: 杨雄}), (ss:好汉 {姓名: 石秀}) CREATE (zl)-[:属于]-(zjz) CREATE (zf)-[:属于]-(zjz) CREATE (zb)-[:属于]-(zjz) CREATE (zy)-[:曾投奔]-(zjz) CREATE (ss)-[:曾投宿]-(zjz) CREATE (zy)-[:义兄弟]-(ss)3. 实战Cypher查询分析有了完整的数据就该玩点有意思的了。Cypher是Neo4j的查询语言读起来就像英语句子一样自然。查询1找出宋江的直系心腹MATCH (sj:好汉 {姓名: 宋江})-[r]-(x:好汉) WHERE r.type IN [结义兄弟, 心腹] RETURN sj, r, x这个查询帮我发现一个有趣现象宋江的核心圈子其实很小真正贴身的只有李逵、花荣等五六人。查询2分析梁山派系构成MATCH (h:好汉)-[r:原属]-(s:山头) RETURN s.名称, count(h) as 人数 ORDER BY 人数 DESC运行后发现二龙山系人数最多有12人难怪鲁智深在梁山话语权这么大。这个发现让我对小说中的权力结构有了新认识。查询3找出关系网中的关键枢纽MATCH (h:好汉) WITH h, size((h)--()) as degree ORDER BY degree DESC LIMIT 5 RETURN h.姓名, h.绰号, degree不出所料宋江、卢俊义、吴用位列前三。但第四名居然是朱仝仔细一查发现他确实串联了不少好汉上山。4. 高级分析与可视化当数据量变大后Neo4j Browser自带的可视化就显得力不从心了。我推荐使用Gephi这类专业工具配合以下查询导出数据MATCH (h1:好汉)-[r]-(h2:好汉) RETURN h1.姓名 as source, h2.姓名 as target, type(r) as type导出为CSV后在Gephi中运行Force Atlas布局算法能清晰看到梁山网络的整体结构。我做了几个有趣的分析派系检测使用模块化算法自动识别出6大派系中心性分析计算每个节点的中介中心度发现柴进这个隐形枢纽路径查找计算任意两人之间的最短关系路径有次我查林冲和关胜的关系路径发现他们居然通过4个人就能联系起来这个六度分隔在水浒世界同样适用。5. 性能优化技巧当关系数据超过1000条后查询速度会明显下降。经过多次实践我总结了几个优化方法索引优化CREATE INDEX ON :好汉(姓名) CREATE INDEX ON :山头(名称)查询优化避免全图扫描尽量用标签限定范围// 不好的写法 MATCH (n)-[r]-(m) WHERE n.姓名 宋江 // 好的写法 MATCH (n:好汉 {姓名: 宋江})-[r]-(m)批量操作使用WITH和UNWIND处理大批量数据WITH [李逵,花荣,戴宗,李俊] as names UNWIND names as name MATCH (sj:好汉 {姓名: 宋江}), (h:好汉 {姓名: name}) CREATE (sj)-[:心腹]-(h)APOC扩展安装APOC库后可以执行更复杂的图算法CALL apoc.path.spanningTree( (sj:好汉 {姓名: 宋江}), {relationshipFilter: 结义兄弟, maxLevel: 3} ) YIELD path RETURN path6. 典型应用场景这个水浒图谱在实际项目中可以有很多延伸应用关系推荐系统基于现有关系推荐可能的新成员影响力分析模拟某个好汉离开对整体网络的影响故事线分析追踪关键事件如何通过关系网络传播时空分析结合上山时间属性分析派系演变有次我用PageRank算法计算好汉影响力发现燕青的排名比预期高很多。仔细一想确实他串联了梁山和朝廷的多条线是个隐形关键人物。7. 常见问题解决在构建过程中遇到过不少坑这里分享几个典型问题的解决方法问题1重复节点批量导入时容易创建重复节点。解决方案MERGE (h:好汉 {姓名: 宋江}) ON CREATE SET h.绰号 及时雨 ON MATCH SET h.last_updated timestamp()问题2性能瓶颈当查询涉及多层关系时可能很慢。可以这样优化MATCH path (sj:好汉 {姓名: 宋江})-[:结义兄弟*1..3]-(other) WITH other, length(path) as depth RETURN other.姓名, depth问题3可视化混乱关系太多时图会变成毛球。建议使用LIMIT限制返回数量先过滤再可视化按属性分组显示记得有次我想看所有天罡星的关系结果一下返回800多条关系浏览器直接卡死。后来学会先做聚合查询再逐步展开细节。