本文主要参考SimmerChan大神文章:https://zhuanlan.zhihu.com/p/33224431 做了一些版本更新
在上一步,我们得到了.nt格式的三元组文件。并且学会了如果使用sparsql语句进行三元组查询。
在这篇文章我们将利用框架实现对三元组知识的查询。
我们知道SPARQL是我们最常用的查询三元组的工具,那么我们需要一个平台,这个平台既可以在下层存储三元组,又可以在上层提供SPARQL endpoint接口 让我们写SPARQL语句来查询。
这样一个平台就是JENA。
APACHE JENA,一款免费的JAVA开源框架,用来做语义网(Semantic Web)和数据连接(Linked Data)的应用。Jena支持的RDF格式数据,是一套NoSQL(非关系数据库)、一套可推理的图数据库。Jena框架将这类数据的存储、添加、删除、推理,查询(Sparql)等所有的操作封装进一个框架,大大的方便了做相关应用的开发和使用。
参考: http://www.flykun.com/jena%E6%89%8B%E5%86%8C-%E5%85%A5%E9%97%A8/
Jena是一个框架,则必然有其架构,让我们从下面这个官网提供的架构图来了解Jena结构,了解其在我们应用开发中的位置,是一个非常好的开始。
[外链图片转存失败(img-iUpcrk9g-1566956626363)(http://www.flykun.com/wp-content/uploads/2015/09/jena-architect.png)]
从这个架构图来看,应用程序的入口可以分为4个主要部分:
在官网下载( https://jena.apache.org/download/index.cgi )。注意要同时下载apache-jena和apache-jena-fuseki两个包,下载方式如下:(建议下载3.7版本的)
下载好之后分别解压到D盘,(地址无所谓,可以解压到java所在的目录,方便记忆):
注意java版本必须高于1.8
TDB 是Jena用于存储RDF的组件,是属于存储层面的技术。在单机情况下,它能够提供非常高的RDF存储性能。
我们必须将之前得到的.nt格式的三元组转化为TDB格式的。步骤如下
D:\Programming\d2rq-0.8.1\Hr_HG_QA.nt
在“apache-jena-3.7.0”文件夹下创建一个子文件夹(我这里命名为“test-tdb”)用于存放tdb数据。
进入“apache-jena-3.7.0”文件夹的bat目录,可以看到很多批处理文件,我们使用“tdbloader.bat”将之前我们的RDF数据以TDB的方式存储。
在这个文件夹按住键盘shift并点击鼠标右键 -> 在此处打开命令行,输入命令如下:
tdbloader.bat --loc="D:\Programming\apache-jena-3.7.0\test-tdb" "D:\Programming\d2rq-0.8.1\Hr_HG_QA.nt"
其中,“–loc”指定tdb文件存储的位置,即刚才我们创建的文件夹;第二个参数是我们之前由Mysql数据转换得到的RDF数据。
这样在整个3.1这一节,我们就得到了 jena 所需的TBD格式的三元组文件。
使用jena这样一个框架,而不是直接将rdf数据导入图数据库,其中一个主要原因是:jena支持OWL语言的推理,可以按照你自己定义的本体类型和推理规则 方便的进行简单推理。
这一步我们 添加之前使用protege创建的本体文件。
进入D盘“apache-jena-fuseki”文件夹,
双击运行“fuseki-server.bat”,程序会为我们在当前目录自动创建“run”文件夹。
然后关闭命令窗。
我们之前通过protege设计得到的本体文件,按照turtle syntax 格式保存,得到hr-kg-ontology-en-turtle.owl
使用记事本打开,一定要把
@prefix hr_kg_ontology: .
改为自己的前缀,与上一步的三元组的@prefix对应
将“hr-kg-ontology-en-turtle.owl”移动到“fuseki/run”文件夹下的“databases”文件夹中。
并直接将“owl”后缀名改为“ttl”。即hr-kg-ontology-en-turtle.owl.ttl
在“run”文件夹下的“configuration”中,我们创建名为“fuseki_conf.ttl”的文本文件(取名没有要求),加入如下内容: 注意,千万别忘记修改 本体文件地址 为你本机的地址!
@prefix : .
@prefix tdb: .
@prefix tdb2: .
@prefix rdf: .
@prefix ja: .
@prefix rdfs: .
@prefix fuseki: .
<#service1> rdf:type fuseki:Service ;
fuseki:name "Hr_HG_QA" ;
fuseki:serviceQuery "sparql", "query" ;
fuseki:serviceReadGraphStore "get" ;
fuseki:serviceReadWriteGraphStore "data" ;
fuseki:serviceUpdate "update" ;
fuseki:serviceUpload "upload" ;
fuseki:dataset <#dataset> ;
.
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph
# 这样做可以不抱错。
[ a ja:MemoryModel ;
# 加载本体文件
ja:content [ja:externalContent ] ;
# 不用jena转换,直接加载三元组
ja:content [ja:externalContent ] ;
] ;
.
<#model_inf> a ja:InfModel ;
#打开OWL推理机
ja:reasoner [ja:reasonerURL ] ;
#关闭规则推理机,并指定规则文件路径
#ja:reasoner [ ja:reasonerURL ;
# ja:rulesFrom ] ;
.
注意,千万别忘记修改 本体文件地址、三元组TDB 为你本机的地址! 这一步可以先把推理机 那两行注销掉
再次运行“fuseki-server.bat”,如果在命令窗中没有error则算成功。
Fuseki默认的端口是3030,打开浏览器输入:
http://localhost:3030/
,我们可以进行SPARQL查询等操作。在Python中用SPARQLWrapper向Fuseki server发送查询请求:
PREFIX :
PREFIX rdf:
PREFIX rdfs:
SELECT * WHERE {
?x :movieTitle '功夫'.
?x ?p ?o.
}
即查询电影《功夫》的所有属性。返回的结果:
x p o
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasGenre file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#genre/14
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasGenre file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#genre/28
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasGenre file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#genre/35
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasGenre file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#genre/80
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://www.kgdemo.com#Movie
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#movieRating 7.2E0
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#movieIntroduction 1940年代的上海,自小受尽欺辱的街头混混阿星(周星驰)为了能出人头地,可谓窥见机会的缝隙就往里钻,今次他盯上行动日益猖獗的黑道势力“斧头帮”,想借之大名成就大业。 阿星假冒“斧头帮”成员试图在一个叫“猪笼城寨”的地方对居民敲诈,不想引来真的“斧头帮”与“猪笼城寨”居民的恩怨。“猪笼城寨”原是藏龙卧虎之处,居民中有许多身怀绝技者(元华、梁小龙等),他们隐藏于此本是为远离江湖恩怨,不想麻烦自动上身,躲都躲不及。而在观战正邪两派的斗争中,阿星逐渐领悟功夫的真谛。
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#movieTitle 功夫
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#movieReleaseDate 2004-02-10
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://www.w3.org/2002/07/owl#Thing
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/25251
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/57609
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/118745
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/57607
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/65975
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/78878
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/83635
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/119426
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/545277
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/576408
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1136808
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1173200
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1173216
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1173223
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1173224
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1287732
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.kgdemo.com#hasActor file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1676386
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.w3.org/1999/02/22-rdf-syntax-ns#type http://www.w3.org/2000/01/rdf-schema#Resource
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470 http://www.w3.org/2002/07/owl#sameAs file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#movie/9470
运行fuseki-server.bat” 命令窗中很可能会报错:
Server ERROR Exception in initialization: the loading of content into
file:///D:/AppsPath/apache-jena-fuseki-3.8.0/run/configuration/fuseki_conf.ttl#model_inf
was aborted because of Read-only object file
这是由于fuseki版本升级所导致的问题:jena版本高于3.7
的时候 InfModel里baseModel和content属性两者不兼容。
两种办法,第一种重新下载jena低版本的(比如3.7)。 但这也不是长远的办法。
第二种办法:
将content注释掉(前面加#),也就是暂时不上传本体文件,等服务器成功启动后,再手动上传本体文件,即:
第一步:将content注释掉
#本体文件的路径
#ja:content [ja:externalContent ] ;
注意 如果注释了content本体文件,并且不手动上传,则无法推理。比如:电影的“hasActor”属性是通过OWL推理机得到的,即我们原本的RDF数据里面是没有的。
使用新版本的配置方式ja:MemoryModel ; 避免了加载冲突。
并且同步加载 本体和 三元组。
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph
[ a ja:MemoryModel ;
ja:content [ja:externalContent ] ;
ja:content [ja:externalContent ] ;
] ;
.
这样
删除tdb路径下所有prefixXXX文件,就可以启动fuseki服务。(再次启动会重新生成这些文件,不用担心)
参考:https://blog.csdn.net/u012052268/article/details/88919129
参考 :https://www.zhihu.com/question/47323119
除了使用protege进行简单推理,我们还可以使用jena的推理机进行复杂规则的推理,比如要实现以下推理:
如果有一个演员,出演了一部喜剧电影,那么他就是一位喜剧演员
在“databases”文件夹下新建一个文本文件“rules.ttl”,填入如下内容:
@prefix : .
@prefix owl: .
@prefix rdf: .
@prefix xsd: .
@prefix rdfs: .
[ruleComedian: (?p :hasActedIn ?m) (?m :hasGenre ?g) (?g :genreName '喜剧') -> (?p rdf:type :Comedian)]
[ruleInverse: (?p :hasActedIn ?m) -> (?m :hasActor ?p)]
在上面这个文件里我们定义了一个名为“ruleComedian”的规则,
它的意思是:如果有一个演员,出演了一部喜剧电影,那么他就是一位喜剧演员。
接下来,如果想让这个规则文件生效,则需要修改配置文件“fuseki_conf.ttl”:
修改配置文件“fuseki_conf.ttl中关于推理机的代码:
关闭OWL推理机,开启规则推理机。
#关闭OWL推理机
#ja:reasoner [ja:reasonerURL ]
#开启规则推理机,并指定规则文件路径
ja:reasoner [ ja:reasonerURL ;
ja:rulesFrom ] ;
我们只能启用一种推理机。前面也提到,OWL的推理功能也可以在规则推理机里面实现,因此我们定义了“ruleInverse”来表示“hasActedIn”和“hasActor”的相反关系。更多细节读者可以参考文档。
也就是说,如果你习惯于用protege的规则,就不要开启这种推理机。还是用之前的owl推理机比较好。
此时,我们执行如下SPARQL查询,喜剧演员有哪些:
PREFIX :
PREFIX rdf:
PREFIX rdfs:
SELECT * WHERE {
?x rdf:type :Comedian.
?x :personName ?n.
}
limit 10
查询结果:
x n
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/111298 郑丹瑞
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/70591 陈欣健
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/116351 沈殿霞
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/116052 鲍汉琳
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1002925 张同祖
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/62423 林正英
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1614091 林琪欣
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/224929 陈法蓉
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/1135398 叶世荣
file:///D:/d2rq/d2rq-0.8.1/kg_demo_movie.nt#person/119426 元秋
参考文献:https://blog.csdn.net/qingdujun/article/details/82501166
参考文献:https://stackoverflow.com/questions/52204735/no-data-available-in-table-when-i-customize-the-rules-of-jena-fuseki
在阅读《实践篇(四):Apache jena SPARQL endpoint及推理》 一文后, 发现最新版本 JENA FUSEKI (v3.8.0)自定义推导功能无法正常使用。
造成该问题,主要两大缘由:
其一,推导规则(rules.ttl)需要用逗号隔开。
@prefix : .
@prefix rdf: .
[ruleComedian: (?p :hasActedIn ?m), (?m :hasGenre ?g), (?g :genreName '喜剧')
-> (?p rdf:type :Comedian)]
[ruleInverse: (?p :hasActedIn ?m) -> (?m :hasActor ?p)]
其二,配置问题。以下为查阅官网后,重写的配置文件(fuseki_conf.ttl)。配置的主要解析过程为,
service>dataset>defaultGraph>infModel>
也就是 根据fuseki_conf.ttl文件中的<# > 符号来确定的, 每两个一组,守卫照应,且每段结束的时候有一个小数点结尾。
本次实践介绍了如何使用Jena来开启endpoint服务,提供高效的查询;并介绍了如何加入推理引擎。我们是用Jena提供的命令行工具来完成上述操作。实际上,jena提供了所有工具的API接口,读者可以用Java编写程序,进行开发。相关文件已上传至github。大家都可以按照这个学习。
最后,衷心真心感谢SimmerChan的文章和代码,我和我的小伙伴都是看你的文章代码入门,多谢!
附件一:可以推理,但是每次tdb都要重新删除prefixXXX文件。
@prefix : .
@prefix tdb: .
@prefix tdb2: .
@prefix rdf: .
@prefix ja: .
@prefix rdfs: .
@prefix fuseki: .
<#service1> rdf:type fuseki:Service ;
fuseki:name "Hr_HG_QA" ;
fuseki:serviceQuery "sparql", "query" ;
fuseki:serviceReadGraphStore "get" ;
fuseki:serviceReadWriteGraphStore "data" ;
fuseki:serviceUpdate "update" ;
fuseki:serviceUpload "upload" ;
fuseki:dataset <#dataset> ;
.
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph <#model_inf> ;
.
<#model_inf> a ja:InfModel ;
#本体文件的路径
ja:content [ja:externalContent ] ;
#打开OWL推理机
ja:reasoner [ja:reasonerURL ] ;
#开启规则推理机,并指定规则文件路径
#ja:reasoner [ ja:reasonerURL ;
# ja:rulesFrom ] ;
# 基本的模型
ja:baseModel <#tdbGraph> ;
.
<#tdbGraph> rdf:type tdb:GraphTDB ;
tdb:dataset <#tdbDataset> ;
.
<#tdbDataset> rdf:type tdb:DatasetTDB ;
tdb:location "D:/Programming/apache-jena-3.7.0/test-tdb" ;
.
附件二:每次不用重新载入,但是无法推理。
@prefix : .
@prefix tdb: .
@prefix tdb2: .
@prefix rdf: .
@prefix ja: .
@prefix rdfs: .
@prefix fuseki: .
<#service1> rdf:type fuseki:Service ;
fuseki:name "Hr_HG_QA" ;
fuseki:serviceQuery "sparql", "query" ;
fuseki:serviceReadGraphStore "get" ;
fuseki:serviceReadWriteGraphStore "data" ;
fuseki:serviceUpdate "update" ;
fuseki:serviceUpload "upload" ;
fuseki:dataset <#dataset> ;
.
<#dataset> rdf:type ja:RDFDataset ;
ja:defaultGraph
# 这样做可以不抱错。
[ a ja:MemoryModel ;
# 加载本体文件
ja:content [ja:externalContent ] ;
# 不用jena转换,直接加载三元组
ja:content [ja:externalContent ] ;
] ;
.
<#model_inf> a ja:InfModel ;
#打开OWL推理机
ja:reasoner [ja:reasonerURL ] ;
#关闭规则推理机,并指定规则文件路径
#ja:reasoner [ ja:reasonerURL ;
# ja:rulesFrom ] ;
.