目录
目标
通过这份源码以及对应配套的博客内容的学习,基本上是了解和掌握了知识图谱的一些基本信息,如果你认真看了作者写的那几篇文档,应该有了以下几点的了解和认识:
(1)清楚的了解RDF/RDFS/OWL是什么,之间是什么关系,以及他们的简单用法
(2)搞清了本体、实体、三元组、TDB、Fuseki这些名词的概念和作用
(3)知道本体文件应该怎么编写,知道怎么从关系mysql关系数据库中将数据导出生成.nt三元组数据
(4)大致了解如何通过sparql通过Fuseki进行三元组的查询
(5)大致知道怎么通过知识库图谱进行对话问答系统的建设
在此,在此感谢作者的总结和贡献。
本篇主要记录下,如何直接通过jena的API进行本体导入、RDF三元组数据导入以及对三元组数据的增删改查操作,实际上网上有很多优秀的博文,我主要是记录下我在学习的过程中遇到的问题。
(1)将ttl本体文件导入到TDB中
(2)将.nt数据直接导入到TDB中
(3)在TDB中创建指定model名称的model,并且支持往model里新增、删除三元组
(4)从TDB中查询新增的三元组数据
注:jena版本3.6
导入RDF
在官网以及一些博客上可以很容易查到关于RDF导入的操作,主要包括两种情况。 一种是直接从.nt文件中加载,加载到内存中,然后进行各种查询。
一种是将.nt文件加载到TDB中,然后通过Dataset进行查询。
我主要考虑第二种,主要是为了持久化做准备,可以新增三元组,而且TDB也可以暴露出去给Fuseki,供上层查询使用。
String tdbPath = "/opt/tdb1";
String rdfFile = "/opt/rdf/kg_movie.nt";
String modelName = "KgMovie";
Dataset ds = TDBFactory.createDataset(tdbPath);
Model model = ds.getNamedModel(modelName);
RDFDataMgr.read(model,rdfFilePath);
ds.cose();
这几行代码非常简单,网上还有很多其他方式的,比如通过FileManager.get()获取文件流,然后再通过model.read去做的。都可以,不过有些方法可能逐步会被遗弃掉。使用的时候,查一下官方的API说明。
另外,有些博客提到事务,比如ds.begin(ReadWrite.WRITE)、model.begin()、model.commit()等,也可以用,但是我没发现有什么很特别的意义,不使用也是没关系。
导入本体文件
public boolean loadOntModel(String modelName, String ttlFilePath)
{
if(!ds.isInTransaction())
ds.begin(ReadWrite.WRITE);
try
{
if(ds.containsNamedModel(modelName))
{
logger.warn("已经存在对应本体描述!");
ds.commit();
return false;
}
Model model = RDFDataMgr.loadModel(ttlFilePath);
ds.addNamedModel(modelName,model);
ds.commit();
logger.info("{}本体描述已导入",modelName);
}
catch (Exception e)
{
logger.error(CommTool.getStackTraceInfo(e));
return false;
}
finally
{
if(ds.isInTransaction())
ds.end();
}
return true;
}
这段代码可以看到,先通过RDFDataMgr.loadModel直接记载ttl的本体文件到一个模型中,然后将模型放到Dataset中,ds是DataSet,通过TDBFactory.createDataset(tdbPath)创建的。 执行完之后,会在对应的TDB目录下创建如下的文件:

同样导入RDF的时候也会创建这些。
TDB存储的本体数据集由node表、Triple和Quad索引、prefixes表组成,存放在指定的文件系统目录下。TDB采用B+树维护三种基本形式的Triple索引:SPO、POS和OSP(S、P、O分别代表Subject、Predicate和Object)。若存在命名图(Named Graph),则同时维护相应的Quad索引(G表示Graph):GOSP、SPOG、GSPO、OSPG、GPOS和POSG。
在实际操作中,以下几点非常重要。
新增三元组
网上关于新增三元组的方式我查到的有三种,一种是通过Statement,mode.add的方式,一种是直接通过Resourece的方式,还有一种是直接通过Sparql的Insert语句。 前面两种我都做了尝试,都是没有问题的。
(1)Statement方式
public static void test4(){
String directory = "D:\\jena\\ctd" ;
Dataset dataset = TDBFactory.createDataset(directory) ;
String perx_s = "http://www.jingu.com#/movie/121";//电影
String perx_p = "http://www.jingu.com#/movieTitle";
String perx_o = "行尸走肉大暴乱";
Model model = dataset.getNamedModel("kgMovie");
Selector selector = new SimpleSelector(model.createResource(perx_s),model.createProperty(perx_p),model.createResource(perx_o));
StmtIterator it = model.listStatements(selector);
if(it.hasNext()){
System.out.println("已存在");
Statement stmt = model.createStatement(model.createResource(perx_s),model.createProperty(perx_p),model.createResource(perx_o));
model.remove(stmt);
System.out.println("已删除");
}else{
Statement stmt = model.createStatement(model.createResource(perx_s),model.createProperty(perx_p),model.createResource(perx_o));
model.add(stmt);
System.out.println("已添加");
}
dataset.close();
}
代码其实很简单,就是直接写入一个三元组数据,这种方式,从我目前理解来说,我觉得不如Resource方式直观,Resource方式可以维护关系,维护属性,维护类型等等。
这里可以看到有model.remove的方法,通过remove将三元组删除掉。 这种删除方式意味着需要先查询,然后再remove掉。
(2)Resource方式
public static void addSPO(){
String NS = "http://www.kgdemo.com#";
String directory = "D:\\jena\\ctd" ;
Dataset dataset = TDBFactory.createDataset(directory) ;
Model model = dataset.getNamedModel("kgMovie");
Resource movieClass = ResourceFactory.createResource(NS+"Movie");
Property movieTitle = ResourceFactory.createProperty(NS,"movieTitle");
Property movieReleaseDate = ResourceFactory.createProperty(NS,"movieReleaseDate");
Resource m1 = model.createResource(NS+"movie/2");
m1.addProperty(RDF.type,movieClass);
m1.addProperty(movieTitle,"大户西游2");
m1.addProperty(movieReleaseDate,"2022-10-22");
model.commit();
dataset.close();
}
这种方式和ttl能够对的上,正常情况下,我们肯定也是先设计ttl本体模式层,然后再进行三元组数据生产。 可以通过mysql导入,也可以直接通过界面化的方式新增三元组。 通过界面方式新增三元的方式,就要求我们需要自己按照规范写入三元组数据。
通过Resource的方式,可以设置class,设置rdf:type,可以设置Property属性等等。 代码可以将整个本体描述的规范都体现出来。还是非常方便。
问题记录
我实际预期想要实现的功能是通过Jena API将ttl本体描述文件或者.nt文件的数据导入到TDB中,往TDB中通过api新增三元组数据,然后通过sparql将新增的三元组数据查询出来。
贴一段查询的代码,如下:
public static void test5(){
String directory = "D:\\jena\\ctd" ;
Dataset dataset = TDBFactory.createDataset(directory) ;
// Potentially expensive query.
String sparqlQueryString = "SELECT distinct ?s ?p ?o where { ?s ?p ?o}" ;
// See http://incubator.apache.org/jena/documentation/query/app_api.html
Model model = dataset.getNamedModel("kgMovie");
Query query = QueryFactory.create(sparqlQueryString) ;
QueryExecution qexec = QueryExecutionFactory.create(query, model) ;
try {
ResultSet results = qexec.execSelect() ;
System.out.println(results.hasNext());
for ( ; results.hasNext() ; )
{
QuerySolution soln = results.nextSolution() ;
String count =soln.get("o").toString();
logger.info("{}-->{}-->{}",soln.get("s"),soln.get("p"),soln.get("o"));
}
} finally { qexec.close() ; }
// Close the dataset.
dataset.close();
}
这段代码中,QueryExecutionFactory.create的第二个入参可以是dataset,也可以是model,如果直接使用的dataset,会发现不管怎样都查询不到新增进来的三元组,如果使用model就可以查询到。
也就是说,在新增三元组的时候,往那个model里新增了,查询的时候就到那个model里查询。 dataset默认应该是defaultModel,并不是全部。
至此,除了查询、推理这块,整个界面化知识图谱所需要的技术基本都调研完毕,后续我们可以进行界面化的构建本体、新增和维护三元组数据到指定modelName的模型中去,并提供查询接口供上层KBQA使用。
本文详细介绍了如何使用Jena API进行知识图谱的操作,包括导入RDF和本体文件到TDB,通过Statement和Resource方式新增三元组,以及查询三元组数据。强调了TDB的存储结构和新增三元组的不同方法,同时也指出了查询时需要注意的细节,即查询模型应与新增模型一致。
6015

被折叠的 条评论
为什么被折叠?



