使用Neo4j构建豆瓣电影知识图谱


使用Neo4j构建豆瓣电影知识图谱

知识图谱在大语言模型兴起之前,一度被作为最有可能实现AGI的技术之一,只是大语言模型火了之后,这项技术慢慢没那么受关注了,但这项技术本身毋庸置疑还是可以解决一些问题的。本文使用一份开源的豆瓣电影数据,使用当前最流行的图数据库Neo4j,构建一个电影知识图谱,并在上面做一些简单的分析,诸如“演员最多的电影”、“参演电影超过10部的演员及其参演的电影”、“黄渤和莱昂纳多·迪卡普里奥的最短距离”等。

本文清洗好的以便于导入Neo4j的数据已经在Github开源,地址为:https://github.com/Steven-Luo/neo4j-douban-movies

1 环境说明

  • Neo4j数据库版本:5.20.0 Enterprise,企业版个人可以免费使用

2 数据

原始数据来源:http://data.openkg.cn/dataset/douban-movie-kg原始数据大家可以访问上面的链接,或者到Github仓库中,获取加工好的可以直接导入Neo4j的数据

3 数据预处理

原始数据每条记录只有一个电影记录,并没有抽象出节点和关系,通过数据预处理,对节点和关系进行抽象和重组,消除数据冗余,便于后续分析。具体而言,有以下节点类型:

  • 电影——Movie

  • 人——Person

  • 语言——Language

  • 发行地区——District

  • 电影类型——Category

有以下关系类型:

  • 参演——ACTED_IN

  • 导演——DIRECTED_IN

  • 编剧——COMPOSED

  • 拥有类型——CATEGORIZED_TO

  • 拥有主要语言——HAS_MAIN_LANGUAGE

  • 发行于——RELEASED_IN

schema或者所谓的本体如下:

使用Neo4j构建豆瓣电影知识图谱

             os.makedirs(, exist_ok=)    data = json.load(())        persons = []  categories = []  languages = []  movies = []  districts = []      acted_in = []  categorized_to = []  directed = []  composed = []  released_in = []  has_main_language = []     item  data:   key  (, , , , ):   item[key]  :              item[key] = []        persons.extend(item[])      persons.extend(item[])      persons.extend(item[])        languages.extend(item[])      districts.extend(item[])      categories.extend(item[])        movies.append({  : item[],  : item[],  : item[],  : item[],  : item[],  : (item[])  item[]     ,  : (item[])  item[]     ,  : item[]      })     director  item[]:          directed.append({  : item[],  : director          })     composer  item[]:          composed.append({  : item[],  : composer          })     actor  item[]:          acted_in.append({  : item[],  : actor          })     category  item[]:          categorized_to.append({  : item[],  : category          })     region  item[]:          released_in.append({  : item[],  : region          })     language  item[]:          has_main_language.append({  : item[],  : language          })         item  [      (, persons),      (, categories),      (, languages),      (, movies),      (, districts)  ]:   item[] == :          pd.DataFrame(item[]).to_csv(os.path.join(, item[] + ), index=)  :          pd.DataFrame((item[]), columns=[]).to_csv(os.path.join(, item[] + ), index=)       rel  [      (, acted_in),      (, categorized_to),      (, directed),      (, composed),      (, released_in),      (, has_main_language),  ]:      pd.DataFrame(rel[]).to_csv(os.path.join(, rel[].upper() + ), index=)

4 导入数据

4.1 准备工作

4.1.1 修改配置

准备工作,首先要修改配置,在下图Settings...中,使用Neo4j构建豆瓣电影知识图谱

注释掉如下的语句,这样可以允许导入数据时,从任意路径导入:

server.directories.import=import

使用Neo4j构建豆瓣电影知识图谱

4.1.2 安装插件

另外,需要安装APOC插件

使用Neo4j构建豆瓣电影知识图谱

4.1.3 创建数据库

这一步可选,在Neo4j界面上创建即可

4.2 开始导入

后续所有导入操作,在Neo4j Browser中进行

4.2.1 导入节点信息

4.2.1.1 导入Movie

旧版的Cypher语句

:auto    (:Movie{  id:line[id],  title:line[title],  cover:line[cover],  length:toInteger(line[length]),  rate:toFloat(line[rate]),  showtime:toInteger(line[showtime]),  url:line[url],  othername:line[othername]  })

Neo4j 4.0及以上版本不再支持 USING PERIODIC COMMIT,需要使用 CALL { … } IN TRANSACTIONS 进行替换。上面只是旧版本的示例,后续不再包含旧版本Cypher语句。

注意:路径中不要包含中文,否则会报Bad escape (Transactions committed: 0)

{  line  (:Movie{  id:line[id],  title:line[title],  cover:line[cover],  length:toInteger(line[length]),  rate:toFloat(line[rate]),  showtime:toInteger(line[showtime]),  url:line[url],  othername:line[othername]  })  }TRANSACTIONS

4.2.1.2 导入Person

{  line  (:Person{  name:line[name]  })  }TRANSACTIONS

4.2.1.3 导入District

{  line  (:District{  name:line[name]  })  }TRANSACTIONS

4.2.1.4 导入Language

{  line  (:Language{  name:line[name]  })  }TRANSACTIONS

4.2.1.5 导入Cateogory

:auto LOAD CSV WITH HEADERS FROM  AS line  CALL {    WITH line    CREATE (:Category {          name:line[]  })  } IN TRANSACTIONS

4.2.2 建立索引

4.2.2.1 对节点建立索引

这个地方Cypher语句也跟Neo4j版本有关,旧版本:

indexon:Movie(title);

新版本:

INDEXmovie_title_indexFOR(m:Movie)ON(m.title);  INDEXperson_name_indexFOR(p:Person)ON(p.name);  INDEXcategory_name_indexFOR(c:Category)ON(c.name);  INDEXlanguage_name_indexFOR(l:Language)ON(l.name);  INDEXdistrict_name_indexFOR(d:District)ON(d.name);

4.2.2.2 查看索引状态

:schema

4.2.3 导入关系

4.2.3.1 导入ACTED_IN关系

{  line  (s:Person{name:line[actor]}),(e:Movie{id:line[movie_id]})  (s)-[:ACTED_IN]->(e)  }TRANSACTIONS

4.2.3.2 导入CATEGORIZED_TO关系

{  line  (s:Movie{id:line[movie_id]}),(e:Category{name:line[category]})  (s)-[:CATEGORIZED_TO]->(e)  }TRANSACTIONS

4.2.3.3 导入DIRECTED关系

{  line  (s:Person{name:line[director]}),(e:Movie{id:line[movie_id]})  (s)-[:DIRECTED]->(e)  }TRANSACTIONS

4.2.3.4 导入COMPOSED关系

{  line  (s:Person{name:line[composer]}),(e:Movie{id:line[movie_id]})  (s)-[:COMPOSED]->(e)  }TRANSACTIONS

4.2.3.5 导入RELEASED_IN关系

{  line  (s:Movie{id:line[movie_id]}),(e:District{name:line[region]})  (s)-[:RELEASED_IN]->(e)  }TRANSACTIONS

4.2.3.6 导入HAS_MAIN_LANGUAGE关系

{  line  (s:Movie{id:line[movie_id]}),(e:Language{name:line[language]})  (s)-[:HAS_MAIN_LANGUAGE]->(e)  }TRANSACTIONS

4.2.4 查看schema

db.schema.visualization

或者

CALL apoc.meta.graph

使用Neo4j构建豆瓣电影知识图谱

4.3 分析

4.3.1 演员数量最多的10部电影

p=(:Person)-[r:ACTED_IN]->(m:Movie)  m,count(p)cnt  m.title,cntcnt

使用Neo4j构建豆瓣电影知识图谱

4.3.2 参演电影最多的10个演员

path=(p:Person)-[:ACTED_IN]-()  p,count(path)cnt  p.name,cntcnt

使用Neo4j构建豆瓣电影知识图谱

4.3.3 参演电影超过10部的演员,获取演员、电影

(p:Person)-[:ACTED_IN]->(m:Movie)  p,count(m)rels,collect(m)movies  rels>  p,movies,rels  rels

使用Neo4j构建豆瓣电影知识图谱

4.3.4 导演电影最多的导演

path=(p:Person)-[:DIRECTED]-()  p,count(path)cnt  p.name,cntcnt

使用Neo4j构建豆瓣电影知识图谱

4.3.5 “莱昂纳多·迪卡普里奥”和“黄渤”的最短路径,限定只能是ACTED_IN关系,同时限定必须是1至8度关系

p=shortestPath((:Person{name:})-[:ACTED_IN*..]-(:Person{name:}))  p

使用Neo4j构建豆瓣电影知识图谱

4.3.6 “莱昂纳多·迪卡普里奥”和“黄渤”的最短路径,限定只能是ACTED_IN或DIRECTED关系,同时限定必须是1至8度关系,获取他们的最短距离长度

p=shortestPath((:Person{name:})-[r:ACTED_IN|DIRECTED*..]-(:Person{name:}))  size(r)

使用Neo4j构建豆瓣电影知识图谱

前沿技术提示词技巧新闻资讯

微软CPO专访:Prompt是AI时代的PRD,产品经理的工作方式已经彻底变了

2026-4-17 13:42:00

Agent智能体新闻资讯

4300字长文:使用dify搭建合同审核Agent

2026-4-17 13:49:24

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
购物车
优惠劵
搜索