Jena对本体、RDF三元组的API操作记录

本文详细介绍了如何使用Jena API进行知识图谱的操作,包括导入RDF和本体文件到TDB,通过Statement和Resource方式新增三元组,以及查询三元组数据。强调了TDB的存储结构和新增三元组的不同方法,同时也指出了查询时需要注意的细节,即查询模型应与新增模型一致。

目录

目标

导入RDF

导入本体文件

新增三元组

(1)Statement方式

(2)Resource方式

问题记录

参考文献


目标

        通过这份源码以及对应配套的博客内容的学习,基本上是了解和掌握了知识图谱的一些基本信息,如果你认真看了作者写的那几篇文档,应该有了以下几点的了解和认识:

(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使用。

参考文献

【1】使用Jena-TDB存储RDF本体、知识图谱文件

【2】导入本体到Jena TDB数据库

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值