文章首发于博客du_ok’s Notes,本文链接为知识图谱入门——知识存储
本文首对一些开源数据库(如RDF4J、gStore),商业数据库(如Virtuoso、AllegroGraph和Stardog),以及一些原生的图数据库(如Neo4j、OrientDB和Titan)进行了粗略的介绍;接着介绍了一个使用Apache Jena数据库的例子。
图数据库介绍
图数据库源起欧拉和图理论 (graph theory),也可称为面向/基于图的数据库,对应的英文是Graph Database。图数据库的基本含义是以“图”这种数据结构存储和查询数据。它的数据模型主要是以节点和关系 (边)来体现,也可处理键值对。它的优点是快速解决复杂的关系问题。
图具有如下特征:
- 包含节点和边
- 节点上有属性 (键值对)
- 边有名字和方向,并总是有一个开始节点和一个结束节点
- 边也可以有属性
图数据库的分类如下:
- 原生数据库:包括基于main memory和基于disk。
- 非原生数据库
- RDBMS:基于关系数据库
- Schema-based:需要设计表结构
- Schema-free:不需要设计表结构
- 基于NoSQL
- Key-value:如Redis
- Column family
- Document store:如MongoDB
- Graph database:如Neo4j(广义上的原生数据库)
- RDBMS:基于关系数据库
开源数据库
开源数据库——RDF4J
- 处理RDF数据的Java框架
- 使用简单可用的API来实现
RDF存储 - 支持SPARQL endpoints
- 支持两种RDF存储机制
- 支持所有主流的RDF文件格式
开源图数据库——gStore
- gStore从图数据库角度存储和检索RDF知识图谱数据;
- gStore支持W3C定义的SPARQL 1.1标准,包括含有Union,OPTIONAL,FILTER和聚集函数的查询;gStore支持有效的增删改操作
- gStore单机可以支持1Billion(十亿)三元组规模的RDF知识图谱的数据管理任务。
商业图数据库
Virtuoso
- 智能数据,可视化与整合
- 可扩展和高性能的数据管理
- 支持Web 扩展和安全
Allgrograph
- 一个现代的,高性能的,支持永久存储的图数据库
- 基于Restful接入支持多语言编程
Stardog
原生图数据库介绍
Neo4J
参见百度百科:Neo4j是一个高性能的,NOSQL图形数据库,它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。
Neo4j的数据结构:
- 在一个图中包含两种基本的数据类型:Nodes (节点) 和Relationships (关系)。
- Nodes 和 Relationships 包含key/value形式的属性。Nodes通过Relationships所定义的关系相连起来,形成关系型网络结构。
Neo4j数据导入:
- Cypher CREATE语句,为每一条数据写一个CREATE
- Cypher LOAD CSV 语句,将数据转成CSV格式,通过LOAD CSV读取数据
- 官方提供的Java API — Batch Inserter
- 官方提供的 neo4j-import 工具
- 第三方开发者编写的 Batch Import 工具
Neo4j数据存储:
**Neo4j查询数据:**使用Cypher查询语言(基于遍历)
OrientDB
OrientDB是一个用Java实现的开源NoSQL数据库管理系统。它是一个多模式的数据库,支持图形、文档、键值对、对象模型和关系,也可以为图数据库的管理与记录之间的提供连接。
Titan(小规模不推荐使用)
- 弹性和线性增长的数据和用户的可扩展性
- 数据分布和复制性能和容错性
- 支持增删改查,支持一致性
- 支持各种后端存储
- 支持全局图数据分析,报告,并通过ETL连接大数据平台
- 支持全文检索
Benchmark
常用的Benchmark
常用衡量指标
- Load Time
- Repository Size
当知识图谱越大时,希望读写性能呈线性增长时的斜率低 - Query Response Time
- 单Query:是否使用了Cache
- 多Query:mix query使得cache失效,大多存在与冷启动,即cache无预先保存与Query相关的结果情况
- Throughputs
- 单Query
- 多Query
- Inference Support
一个知识存储的例子
Jena是一个免费开源的支持构建语义网络和数据链接应用的Java框架。
- 底层存储支持基于内存、基于SDB(导入关系数据库)、基于TDB(导入原生的三元组数据)和基于custom的存储;
- 同时Jena还支持一些推理的API;
- RDF API,支持基于SPARQL语言的查询;
- 可使用Fuseki进行增删查改。
前期工作
定义一个音乐知识图谱的Schema如下:
Schema中的蓝线指向属性,绿线指向实体,虚线指向图谱生成之后添加的属性,后面通过
SPARQL Update语句添加。
我们使用python脚本生成了1000个音乐知识图谱的三元组:
数据导入:
- 使用TDB导入
使用tdbloader工具的命令为:/jena-fuseki/tdbloader --loc=/jena-fuseki/data filename'/jena-fuseki/data’是存储的位置。
- 使用Fuseki导入
启动Fuseki服务:
在导入数据之后我们可以使用接口对我们的数据进行查询,于是我们使用Fuseki Server进行查询,启动命令如***意此处需要指定TDB生成的文件路径和数据库名:
/jena-fuseki/fuseki-server --loc=/jena-fuseki/data --update /music
其中/music是刚刚导入的数据
数据库查询方法:
- Fuseki界面查询
- 使用endpoint接口查询,endpoint地址为:
SPARQL Query: http://localhost:3030/music/query
SPARQL Update: http://localhost:3030/music/update - 使用SPARQLWrapper包查询和更新(详见https://rdflib.github.io/sparqlwrapper/)
后两种方法都是可以基于Python语言。
查询示例
- 查询某一艺术家的所有歌曲
PREFIX music: <http://kg.course/music/>
SELECT DISTINCT ?trackID
WHERE {
?trackID music:track_artist music:artist_01
}
- 查询某一艺术家的所有歌曲的歌曲名
PREFIX music: <http://kg.course/music/>
SELECT ?name
WHERE {
?trackID music:track_artist music:artist_01 .
?trackID music:track_name ?name
}
- 查询某一首歌曲名的专辑信息
PREFIX music: <http://kg.course/music/>
SELECT ?trackID ?ablumID ?name
WHERE {
?trackID music:track_name "track_name_00001" .
?trackID music:track_album ?ablumID .
?ablumID music:ablum_name ?name
}
- 查询某一首歌曲名的专辑信息,使用中文来当变量名
PREFIX music: <http://kg.course/music/>
SELECT ?歌曲id ?专辑id ?专辑名
WHERE {
?歌曲id music:track_name "track_name_00001" .
?歌曲id music:track_album ?专辑id .
?专辑id music:album_name ?专辑名
}
- 查询某一首歌曲名的专辑信息,变量名添加描述
PREFIX music: <http://kg.course/music/>
SELECT ?歌曲id ?专辑id (CONCAT("专辑名",":",?专辑名) AS ?专辑信息)
WHERE {
?歌曲id music:track_name "track_name_00001" .
?歌曲id music:track_album ?专辑id .
?专辑id music:album_name ?专辑名
}
- 查询某个专辑里面的所有歌曲
PREFIX music: <http://kg.course/music/>
SELECT ?trackID
WHERE {
?albumID music:album_name "album_name_0002" .
?trackID music:track_album ?albumID
}
- 查询某个专辑里面的所有歌曲,限制前2
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?S ?P
WHERE {
?subject ?predicate "ablum_name_0002" .
?S ?P ?subject
}
limit 2
- 对某个专辑里面的所有歌曲计数
PREFIX music: <http://kg.course/music/>
SELECT (COUNT(?trackID) as ?num)
WHERE {
?albumID music:album_name "album_name_0002" .
?trackID music:track_album ?albumID
}
- 查询某一首歌是哪一个艺术家的作品
PREFIX music: <http://kg.course/music/>
SELECT ?trackID ?artistID
WHERE {
?trackID music:track_name "track_name_00001" .
?trackID music:track_artist ?artistID
}
- s查询某一首歌属于什么歌曲类型
PREFIX music: <http://kg.course/music/>
SELECT ?trackID ?tag_name
WHERE {
?trackID music:track_name "track_name_00001" .
?trackID music:track_tag ?tag_name
}
- 查询某一艺术家唱过歌曲的所有标签
PREFIX music: <http://kg.course/music/>
SELECT DISTINCT ?tag_name
WHERE {
?trackID music:track_artist music:artist_001 .
?trackID music:track_tag ?tag_name
}
- 查询某一艺术家唱过歌曲的所有类型并排序
PREFIX music: <http://kg.course/music/>
SELECT DISTINCT ?tag_name
WHERE {
?trackID music:track_artist music:artist_001 .
?trackID music:track_tag ?tag_name
}
ORDER BY ?tag_name
- 查询某几类歌曲标签中的歌曲的数目
PREFIX music: <http://kg.course/music/>
SELECT (COUNT(?trackID ) AS ?num)
WHERE {
{
?trackID music:track_tag "tag_name_01" .
}
UNION
{
?trackID music:track_tag "tag_name_02" .
}
}
PREFIX music: <http://kg.course/music/>
SELECT (count(?trackID ) as ?num)
WHERE {
?trackID music:track_tag ?tag_name
FILTER (?tag_name = "tag_name_01"|| ?tag_name = "tag_name_02")
}
- 查询所有歌曲中带有’xx’字符的歌曲名
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?subject ?object
WHERE {
?subject <http://kg.course/music/track_name> ?object .
FILTER regex(?object,"088")
}
- 询问是否存在带有’xx’字符的歌曲名
PREFIX music: <http://kg.course/music/>
ASK
{
?trackID music:track_name ?track_name .
FILTER regex(?track_name,"008")
}
增加示例
给艺术家id新增属性艺术家名字
PREFIX music: <http://kg.course/music/>
INSERT DATA
{
music:arttist_01 music:artist_name "artist_name_01" .
music:arttist_02 music:artist_name "artist_name_02" .
music:arttist_03 music:artist_name "artist_name_03" .
}
PREFIX music: <http://kg.course/music/>
SELECT ?artistID ?artist_name
WHERE {
?artistID music:artist_name ?artist_name
}
删除示例
删除增加的属性艺术家名字
PREFIX music: <http://kg.course/music/>
DELETE
{
music:artist_02 music:artist_name ?x .
}
WHERE
{
music:artist_02 music:artist_name ?x .
}
PREFIX music: <http://kg.course/music/>
SELECT ?artistID ?artist_name
WHERE {
?artistID music:artist_name ?artist_name
}