KG

在Neo4j中构建漫威世界的社交网络

2019-03-26  本文已影响0人  今天无Bug

原文链接:https://tbgraph.wordpress.com/2017/06/10/neo4j-marvel-social-graph/
译者言,本文通过构建漫威世界中各英雄之间一起出现在漫画中的次数,计算出了漫威世界中最重要的英雄。在Cypher语法的使用方面,着重介绍了数据导入LOAD CSV、批处理方法apoc.periodic.iterate、以及MERGE语句。

今天我将会演示如如何将漫威世界的数据导入到Neo4j中,同时我将使用一个非常简单的模型,展示出使用图数据库去存储社交网络数据有哪些优越性。

基本要求

  • Neo4j(https://neo4j.com/download/)

  • Apoc插件(https://github.com/neo4j-contrib/neo4j-apoc-procedures/releases)

  • 数据

    我们需要的漫威社交数据(https://www.kaggle.com/csanhueza/the-marvel-universe-social-network)可以从kaggle竞赛的一个贴子中获取。数据里有3个csv文件:

  • nodes.csv --结点名称与类型。

  • edges.csv --英雄与漫画之间的关系。

  • hero-network.csv --在同一部漫画中英雄之间的关系。

  • 我们只需要edges.csv文件,就可以知道每位英雄出现在对应的哪部漫画中,这对于我们社交网络构建就足够了。暂时不需要hero-network.csv文件,因为我们可以使用cypher或apoc去构建我们自己的、带有权重的各英雄之间关系。

    图模型

    数据导入

    首先我们在Comic和Hero标签上创建约束,可以使查询的速度更快。

    CALL apoc.schema.assert(
    {},
    {Comic:['name'],Hero:['name']});

    我已经上传了edges.csv文件到github代码库中,你可以使用下面的语句一步导入。导入语句也非常简单,只是使用MERGE语句进对结点和关系进行合并,当大数据里需要进行导入时,需要使用USING PERIODIC COMMIT语句进行批处理设置。 

    USING PERIODIC COMMIT 5000
    LOAD CSV WITH HEADERS FROM
    "https://raw.githubusercontent.com/tomasonjo/neo4j-marvel/master/data/edges.csv" as row
    MERGE (h:Hero{name:row.hero})
    MERGE (c:Comic{name:row.comic})
    MERGE (h)-[:APPEARED_IN]->(c)

    创建带有权重的无方向性的英雄间关系网络

    现在已经导入数据了,我们要创建一个带权重的英雄社交网络,我们假设所有漫画英雄他们彼此认识,使用KNOWS关系进行表示,且把他们在漫画世界中一起出现同一本漫画中的次数做为KNOWS关系的weight属性值。

    译者言:“weight属性值”指在一本漫画中,有两位英雄出现了,则两位英雄之间的KNOWS关系的weight为1;如在两本漫画中都出现,则两位英雄之间的weight为2。 这个一定要理解清楚,因为后面的分析都要基于这个属性来完成。
    语言表达能力还有待提高,如果有不理解的,欢迎留言讨论。

    由于我们的关系图挺大的, 在一个事务中进行全局性的计划不太可能,所以我们需要分批来处理,此时,在Cypher查询中使用APOC库,进行分批处理是再合适不过了,而APOC库中的 apoc.periodic.iterate 方法正是为此而生。

    译者言:关于高性能数据插入这块可以看这篇文章<关于Neo4j和Cypher批量更新和批量插入优化的5个建议>(https://blog.csdn.net/hwz2311245/article/details/60963383),介绍的相当不错。

    下面节选自APOC官方文档

    apoc.periodic.iterate 方法需要提供两个参数,第一个参数是外部语句,将提供一个数据流,表示哪些数据将会被处理。第二个参数是内部语句,通过参数itertaList:true来进行控制,表示一次处理一个元素或一次处理一批元素。 (译者言:后面将会详细介绍apoc.periodic.iterate 方法)

    我们将使用下面语句创建一个有权重的无方向性的英雄社交网络

    CALL apoc.periodic.iterate(
    "MATCH (p1:Hero)-->(:Comic)<--(p2:Hero) where id(p1) < id(p2) RETURN p1,p2",
    "MERGE (p1)-[r:KNOWS]-(p2) ON CREATE SET r.weight = 1 ON MATCH SET r.weight = r.weight + 1"
    , {batchSize:5000, parallel:false,iterateList:true})

    下面我们来仔细讨论一下这个查询,以便更好的理解。

    第一参数,外部语句

    第一个参数,外部语句,返回一个将要被处理的数据流。这个cypher语句很简单,返回出现在同一部漫画中的两个英雄。这里有一个细节非常重要: WHERE id(p1) < id(p2)条件语句,这是为了避免返回重复结果。如果使用 <> 代表 < ,则MATCH语句会将每个关系返回两次,第一次返回的是hero1作为p1, hero2作为p2。第二次MATCH返回hero2作为p1,hero1作为p2。

    MATCH (p1:Hero)-->(:Comic)<--(p2:Hero) where id(p1) < id(p2) RETURN p1,p2

    第二个参数,内部语句

    第二个参数,内部语句,表示一次处理一个元素,或者通过设置iterateList:true 一次处理一批元素。接下来使用MERGE来创建关系,就是关系在创建前先尝试查询,如查存在则不创建,如果不存在则创建。通过阅读Neo4j官方文档了解,这是Cypher语言一个内置特性。同时,MERGE语句还可以根据结点或关系的实际情况做出相应的动作,当结点不存在需要被创建时,可以在ON CREATE SET子语句中去处理。而如果结点或关系存在时,则可以在ON MATCH SET中进行相应的操作。

    另外,我们也要提醒一点,我们并没有指定关系的方向,因为我们认为社交网络是无方向的,所以关系的方向对我们也就没有什么意义。而MERGE语句可以不用指定方向。在实际应用中,它仅检测给定结点间是否存在关系,无论是哪个方向的,如果有,则不存在,如果没有,则会创建一个随机方向的关系。

    MERGE (p1)-[r:KNOWS]-(p2)
    ON CREATE SET r.weight = 1
    ON MATCH SET r.weight = r.weight + 1

    最重要的英雄

    现在我们来看一下,谁是最重要的英雄?一般我们认为与其他英雄一起出现在漫画中的次数最多的英雄就是最重要的英雄,也就与其他英雄的KNOWS关系weigth属性值相加最大的那位英雄。

    尽管我们的社交网络是无向的,但是Neo4j仍然会按有向的去存储,而MATCH语句支持不指定方向关系的查询,所以,下面的查询没有任何问题:

    MATCH (h:Hero)-[t:KNOWS]-()
    RETURN h.name as hero,sum(t.weight) as weighted_degree
    ORDER BY weighted_degree DESC LIMIT 10

    上面语句查询结果如下: 

    至此,本系列的第一部分结果,我们已经学习了如何导入社交数据,在英雄间创建了一个无向的关系网络,接下来我们将会在这个社交网络上测试各种图算法,如Neo4j图算法和APOC图算法。请继续关系,下次我会介绍更多的算法。

    上一篇下一篇

    猜你喜欢

    热点阅读