耶拿天气数据集
资源描述框架(RDF)最近成为W3C的推荐,与XML和SOAP等其他Web标准一起被取代。 RDF可以应用于处理临时传入数据的领域(例如CRM),并且已经广泛用于社交网络和LiveJournal和TypePad等自发布软件。
Java程序员将具有使用RDF模型的技能,从而逐渐受益。 在本文中,我将带您了解HP Labs的开源Jena Semantic Web Framework的一些功能(请参阅参考资料 )。 您将学习如何创建和填充RDF模型,如何将其持久化到数据库中以及如何使用RDQL查询语言以编程方式查询它们。 最后,我将演示如何使用Jena的推理功能从本体论中推断有关模型的知识。
本文假定您对RDF有所了解-包括图形,三元组和模式之类的概念-以及Java编程的基本知识。
创建一个简单的RDF模型
让我们从基础开始:从头开始创建模型并向其中添加RDF语句。 在本节中,我将展示如何创建一个模型来描述一组虚构的家庭成员之间的关系,如图1所示:
图1.虚构的家谱
![小家谱](https://i-blog.csdnimg.cn/blog_migrate/9d96689f03a814a887ac99b08f626f55.png)
你会描述使用性质不同的关系类型siblingOf
, spouseOf
, parentOf
和childOf
,从“关系”词汇拍摄(见相关主题 )。 为简单起见,使用来自虚构名称空间http://family/
URI标识家庭成员。 词汇URI在Jena代码中经常使用,因此将它们声明为Java常量很有用,从而减少了混淆它们的风险。
耶拿的ModelFactory
类是创建不同类型模型的首选方法。 在这种情况下,您需要一个空的内存模型,因此ModelFactory.createDefaultModel()
是要调用的方法。 此方法返回Model
的实例,您将使用该实例创建代表家庭中每个人的Resource
。 创建资源后,可以对它们进行声明并添加模型。
在耶拿(Jena)中,语句的主题始终是Resource
,谓词由Property
表示,而对象是另一个Resource
或文字值。 文字在耶拿用Literal
类型表示。 所有这些类型共享一个公共接口RDFNode
。 您将需要四个不同的Property
实例来表示家族树中的关系。 这些实例是使用Model.createProperty()
创建的。
向模型添加语句的最简单方法是调用Resource.addProperty()
。 此方法在模型中创建一个以“ Resource
为主题的语句。 该方法有两个参数,一个表示语句谓词的Property
和该语句的对象。 addProperty()
方法已重载:一个重载将RDFNode
作为对象,因此可以使用Resource
或Literal
。 还有一些方便重载,它们采用Java原语或String
表示的文字。 在该示例中,语句的对象是Resource
代表其他家庭成员。
通过调用带有三元组的主题,谓词和对象的Model.createStatement()
,也可以在模型上直接创建语句。 请注意,以这种方式创建一个Statement
不会将其添加到模型中。 如果要将其添加到模型中,请使用创建的Statement
调用Model.add()
,如清单1所示:
清单1.创建一个代表虚构家庭的模型
// URI declarations
String familyUri = "http://family/";
String relationshipUri = "http://purl.org/vocab/relationship/";
// Create an empty Model
Model model = ModelFactory.createDefaultModel();
// Create a Resource for each family member, identified by their URI
Resource adam = model.createResource(familyUri+"adam");
Resource beth = model.createResource(familyUri+"beth");
Resource chuck = model.createResource(familyUri+"chuck");
Resource dotty = model.createResource(familyUri+"dotty");
// and so on for other family members
// Create properties for the different types of relationship to represent
Property childOf = model.createProperty(relationshipUri,"childOf");
Property parentOf = model.createProperty(relationshipUri,"parentOf");
Property siblingOf = model.createProperty(relationshipUri,"siblingOf");
Property spouseOf = model.createProperty(relationshipUri,"spouseOf");
// Add properties to adam describing relationships to other family members
adam.addProperty(siblingOf,beth);
adam.addProperty(spouseOf,dotty);
adam.addProperty(parentOf,edward);
// Can also create statements directly . . .
Statement statement = model.createStatement(adam,parentOf,fran);
// but remember to add the created statement to the model
model.add(statement);
完整的代码示例FamilyModel.java还演示了如何将批处理语句立即作为数组或java.util.List
添加到模型java.util.List
。
建立家庭模型后,让我们看看如何使用Jena的查询API从信息中提取信息。
询问RDF模型
以编程方式询问Jena模型主要是通过Model
和Resource
接口上的list()
方法执行的。 这些方法可用于获取与某些条件匹配的主题,对象和Statement
。 它们还返回java.util.Iterator
,它具有返回特定对象类型的额外方法。
让我们回到清单1的家庭模型示例,看看可以如何查询它,如清单2所示:
清单2.查询族模型
// List everyone in the model who has a child:
ResIterator parents = model.listSubjectsWithProperty(parentOf);
// Because subjects of statements are Resources, the method returned a ResIterator
while (parents.hasNext()) {
// ResIterator has a typed nextResource() method
Resource person = parents.nextResource();
// Print the URI of the resource
System.out.println(person.getURI());
}
// Can also find all the parents by getting the objects of all "childOf" statements
// Objects of statements could be Resources or literals, so the Iterator returned
// contains RDFNodes
NodeIterator moreParents = model.listObjectsOfProperty(childOf);
// To find all the siblings of a specific person, the model itself can be queried
NodeIterator siblings = model.listObjectsOfProperty(edward, siblingOf);
// But it's more elegant to ask the Resource directly
// This method yields an iterator over Statements
StmtIterator moreSiblings = edward.listProperties(siblingOf);
最通用的查询方法是Model.listStatements(Resource s, Property p, RDFNode o)
,是在此演示的便捷方法的基础。 这些参数中的任何一个都可以保留为null
,在这种情况下,它们可以用作通配符,匹配任何内容。 清单3显示了一些Model.listStatements()
用法的示例:
清单3.使用选择器查询模型
// Find the exact statement "adam is a spouse of dotty"
model.listStatements(adam,spouseOf,dotty);
// Find all statements with adam as the subject and dotty as the object
model.listStatements(adam,null,dotty);
// Find any statements made about adam
model.listStatements(adam,null,null);
// Find any statement with the siblingOf property
model.listStatements(null,siblingOf,null);
导入和保留模型
并非所有应用程序都将以空模型开头。 更常见的是,在启动时会从现有数据中填充模型。 在这种情况下使用内存模型的缺点是,每次启动应用程序时,都必须从头开始重新填充模型。 此外,每次关闭应用程序时,对内存模型所做的任何更改都将丢失。
一种解决方案是使用Model.write()
将模型序列化到文件系统,并使用Model.read()
在启动时反序列化它。 但是,Jena还提供了持久化模型,这些模型被连续透明地持久化到后备存储中。 Jena可以将其模型持久保存在文件系统或关系数据库中。 当前支持的数据库引擎是PostgreSQL,Oracle和MySQL。
为了演示如何导入和持久化模型,我将以WordNet 1.6数据库的RDF表示形式导入MySQL。 因为我使用的WordNet表示形式采用几个单独的RDF文档的形式,所以将它们导入到单个Jena模型中会合并它们的语句。 图2展示了将名词和词汇表模型合并在一起后,WordNet模型的片段结构:
图2.合并的WordNet名词和词汇表模型的结构
![该图说明了WordNet模型的一部分中的语句](https://i-blog.csdnimg.cn/blog_migrate/435f9dacf97aa38279ca24220af245a0.png)
创建数据库支持的模型的第一步是实例化MySQL驱动程序类,并创建一个DBConnection
实例。 DBConnection
构造函数使用用户的ID和密码来登录数据库。 它还带有一个数据库URL参数,该参数包含Jena要使用的MySQL数据库的名称,格式为"jdbc:mysql://localhost/dbname"
。 Jena可以在一个数据库中创建多个模型。 最终的DBConnection
参数是数据库类型,对于MySQL来说是"MySQL"
。
然后,可以将DBConnection
实例与Jena的ModelFactory
一起使用,以创建数据库支持的模型。
一旦创建了模型,就可以从文件系统中读取WordNet RDF文档。 各种Model.read()
方法可以从Reader
, InputStream
或URL
填充模型。 可以从Notation3,N-Triples或默认情况下从RDF / XML语法解析模型。 WordNet模型已序列化为RDF / XML,因此您无需指定语法。 读取模型时,可以提供基本URI。 基本URI用于将模型中的任何相对URI转换为绝对URI。 由于WordNet文档不包含任何相对URI,因此可以将此参数指定为null
。
清单4显示了将WordNet RDF / XML文件导入到MySQL持久模型中的完整过程:
清单4.导入和保留WordNet模型
// Instantiate the MySQL driver
Class.forName("com.mysql.jdbc.Driver");
// Create a database connection object
DBConnection connection = new DBConnection(DB_URL, DB_USER, DB_PASSWORD, DB_TYPE);
// Get a ModelMaker for database-backed models
ModelMaker maker = ModelFactory.createModelRDBMaker(connection);
// Create a new model named "wordnet." Setting the second parameter to "true" causes an
// AlreadyExistsException to be thrown if the db already has a model with this name
Model wordnetModel = maker.createModel("wordnet",true);
// Start a database transaction. Without one, each statement will be auto-committed
// as it is added, which slows down the model import significantly.
model.begin();
// For each wordnet model . . .
InputStream in = this.getClass().getClassLoader().getResourceAsStream(filename);
model.read(in,null);
// Commit the database transaction
model.commit();
现在已经填充了wordnet模型,您可以稍后通过调用ModelMaker.openModel("wordnet",true);
来访问它ModelMaker.openModel("wordnet",true);
仅使用Jena的API来查询与WordNet一样大和丰富的模型将是限制性的,因为要执行的每种查询类型都需要编写几行定制代码。 幸运的是,Jena提供了一种以RDQL形式表达通用查询的机制。
RDF数据查询语言(RDQL)
RDQL是RDF的查询语言。 尽管尚未成为正式标准,但RDQL已由RDF框架广泛实施。 RDQL允许查询引擎执行访问数据模型的艰苦工作,从而简洁地表达复杂的查询。 RDQL的语法从表面上类似于SQL的语法,实际上,使用关系数据库查询的任何人都会熟悉它的某些概念。 可以在Jena网站上找到出色的RDQL教程,但是一些简短的示例对说明基础知识大有帮助。
可以使用jena.rdfquery
工具针对Jena模型在命令行上执行RDQL查询。 RDFQuery从文本文件获取RDQL查询,并针对指定的模型运行它。 要针对数据库支持的模型运行,需要几个参数。 清单5显示了运行以下示例所需的完整命令行:
清单5.从命令行运行RDQL查询
$java jena.rdfquery --data jdbc:mysql://localhost/jena --user dbuser --password dbpass
--driver com.mysql.jdbc.Driver --dbType MySQL --dbName wordnet --query example_query.rdql
如您所见,大多数这些参数提供了创建与MySQL的连接所需的详细信息。 重要的部分是--query example_query.rdql
,它是RDQL文件的位置。 还要注意,运行jena.rdfquery
需要Jena的lib目录中的所有JAR文件。
清单6显示了您将检查的第一个查询:
清单6. RDQL查询以查找“本地狗”的WordNet词汇表条目
SELECT
?definition
WHERE
(?concept, <wn:wordForm>, "domestic dog"),
(?concept, <wn:glossaryEntry>, ?definition)
USING
wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
SELECT
部分声明要由查询输出的变量-在本例中为名为definition
的变量。 WHERE
子句引入了第二个变量concept
,并定义了与图匹配的三元组。 该查询在图中找到WHERE
子句中所有三元组均成立的语句。 因此,用英语来说, WHERE
子句类似于“找到带有“家犬”作为词形的概念,并找到这些概念的词汇表条目”之类的东西,如图3所示USING
子句是一种便利,用于声明命名空间的前缀。
图3.与清单6中的WHERE子句匹配的图
![匹配狗的词汇表条目的图](https://i-blog.csdnimg.cn/blog_migrate/b0dadca107e6b9c54102f93742ac8ee1.png)
运行查询结果为:
definition
===============================================================================
"a member of the genus Canis (probably descended from the common wolf) that has
been domesticated by man since prehistoric times; occurs in many breeds; "the
dog barked all night""
因此,在这种情况下只有一个结果。 清单7中显示的下一个查询说:“查找以单词“ bear”代表的概念,并找到这些概念的词汇表条目。
清单7. RDQL查询以查找“ bear”的WordNet词汇表条目
SELECT
?definition
WHERE
(?concept, <wn:wordForm>, "bear"),
(?concept, <wn:glossaryEntry>, ?definition)
USING
wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
该查询返回15个结果,因为此字形代表几个不同的概念。 结果开始:
definition
===============================================================================
"massive plantigrade carnivorous or omnivorous mammals with long shaggy coats
and strong claws"
"an investor with a pessimistic market outlook"
"have on one's person; "He wore a red ribbon"; "bear a scar""
"give birth (to a newborn); "My wife had twins yesterday!""
清单8中显示了一个更复杂的示例,它找到另外两个示例的上位词(父词):
清单8. RDQL查询以找到“ panther”和“ tiger”的WordNet上位词
SELECT
?wordform, ?definition
WHERE
(?firstconcept, <wn:wordForm>, "panther"),
(?secondconcept, <wn:wordForm>, "tiger"),
(?firstconcept, <wn:hyponymOf>, ?hypernym),
(?secondconcept, <wn:hyponymOf>, ?hypernym),
(?hypernym, <wn:wordForm>, ?wordform),
(?hypernym, <wn:glossaryEntry>, ?definition)
USING
wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
在这里,查询显示为“找到单词'panther'和'tiger'所指的概念,找到第三个概念,其中前两个是下义词,并找到该概念的可能单词和词汇表条目”,如图所示在图4中:
图4.清单8中的WHERE子句匹配的图
![图匹配豹和虎的上影](https://i-blog.csdnimg.cn/blog_migrate/72f66962a8bc34e5e85c062fb6e116cb.png)
在SELECT
子句中都声明了wordform
和definition
,因此都将其输出。 尽管此查询仅匹配单个WordNet概念,但查询的图形可以两种方式匹配,因为该概念具有两种不同的字形:
wordform | definition
=====================================================================================
"big cat" | "any of several large cats typically able to roar and living in the wild"
"cat" | "any of several large cats typically able to roar and living in the wild"
在耶拿使用RDQL
Jena的com.hp.hpl.jena.rdql
软件包包含在Java代码中使用RDQL所需的所有类和接口。 要创建RDQL查询,请将RDQL放入String
,然后将其传递给Query
的构造函数。 通常,除非在RDQL本身中使用FROM
子句另外指定,否则显式设置模型以用作查询源。 准备好Query
, QueryEngine
可以从中创建一个QueryEngine
并执行查询。 清单9演示了此过程:
清单9.创建和运行RDQL查询
// Create a new query passing a String containing the RDQL to execute
Query query = new Query(queryString);
// Set the model to run the query against
query.setSource(model);
// Use the query to create a query engine
QueryEngine qe = new QueryEngine(query);
// Use the query engine to execute the query
QueryResults results = qe.exec();
与Query
一起使用的一种有用技术是在执行之前将其某些变量设置为固定值。 此用法模式类似于javax.sql.PreparedStatement
用法。 变量通过ResultBinding
对象绑定到值,该对象在执行时传递给QueryEngine
。 可以将变量绑定到Jena Resource
或文字值。 在将文字绑定到变量之前,将其包装为对Model.createLiteral
的调用。 清单10说明了预绑定方法:
清单10.将查询变量绑定到值
// Create a query that has variables x and y
Query query = new Query(queryString);
// A ResultBinding specifies mappings between query variables and values
ResultBinding initialBinding = new ResultBinding() ;
// Bind the query's first variable to a resource
Resource someResource = getSomeResource();
initialBinding.add("x", someResource);
// Bind the query's second variable to a literal value
RDFNode foo = model.createLiteral("bar");
initialBinding.add("y", foo);
// Execute the query with the specified values for x and y
QueryEngine qe = new QueryEngine(query);
QueryResults results = qe.exec(initialBinding);
QueryEngine.exec()
返回的QueryResults
对象实现java.util.Iterator
。 它的next()
方法返回ResultBinding
对象。 可以从ResultBinding
按名称获取查询中使用的所有变量,无论它们是否是SELECT
子句的一部分。 清单11显示了如何执行此操作,再次使用清单6中的RDQL查询:
清单11. RDQL查询以查找“本地狗”的WordNet词汇表条目
SELECT
?definition
WHERE
(?concept, <wn:wordForm>, "domestic dog"),
(?concept, <wn:glossaryEntry>, ?definition)
USING
wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>";
通过运行此查询获得的ResultBinding
如预期的那样包含该单词的文字词汇表条目。 另外,您可以访问变量concept
。 通过调用ResultBinding.get()
按名称获取变量。 此方法返回的所有变量都可以RDFNode
为RDFNode
,如果要将它们重新绑定到另一个RDQL查询中,这将非常有用。
在这种情况下, concept
变量表示RDF资源,因此从ResultBinding.get()
获得的Object
可以ResultBinding.get()
为Resource
。 然后可以调用Resource
的查询方法以进一步探索模型的这一部分,如清单12所示:
清单12.处理查询结果
// Execute a query
QueryResults results = qe.exec();
// Loop over the results
while (results.hasNext()) {
ResultBinding binding = (ResultBinding)results.next();
// Print the literal value of the "definition" variable
RDFNode definition = (RDFNode) binding.get("definition");
System.out.println(definition.toString());
// Get the RDF resource used in the query
Resource concept = (Resource)binding.get("concept");
// Query the concept directly to find other wordforms it has
List wordforms = concept.listObjectsOfProperty(wordForm);
}
FindHypernym.java包含在源文件下载(请参阅该程序相关信息 ),总结你在这里看到的区域。 它使用清单13中所示的查询查找命令行中给出的单词的上位词:
清单13. RDQL查询以查找概念的上位词的字形和词汇表条目
SELECT
?hypernym, ?definition
WHERE
(?firstconcept, <wn:wordForm>, ?hyponym),
(?firstconcept, <wn:hyponymOf>, ?secondconcept),
(?secondconcept, <wn:wordForm>, ?hypernym),
(?secondconcept, <wn:glossaryEntry>, ?definition)
USING
wn FOR <http://www.cogsci.princeton.edu/~wn/schema/>
命令行上给定的单词与下hyponym
词绑定,查询会找到该词代表的概念,找到第一个概念为下义词的第二个概念,然后输出该概念的字形和定义。 清单14显示了它的输出:
清单14.运行示例FindHypernym程序
$ java FindHypernym "wisteria"
Hypernyms found for 'wisteria':
vine: weak-stemmed plant that derives support from climbing, twining,
or creeping along a surface
用OWL添加含义
您可能想知道为什么搜索“紫藤”的上位词只会返回其直接上位词“藤”。 如果您植物学上的想法,您可能还希望“微量营养体”显示为上调,当然还有“植物”。 确实,WordNet模型说“紫藤”是“藤”的下义词,“藤”是“微量营养体”的下义词。 凭直觉,您知道“紫藤”因此是“示营养体”的下义词,因为您知道“关系的别名”是可传递的 。 因此,您需要一种将这些知识整合到FindHypernym程序中的方法,这是OWL的所在。
Web本体语言或OWL是W3C建议书,旨在“明确表示词汇中术语的含义以及这些术语之间的关系”。 OWL与RDF Schema一起提供了一种正式描述RDF模型内容的机制。 除了定义资源可以属于的分层类之外,OWL还允许表达资源属性的特征。 例如,在所使用的关系词汇清单1 ,OWL可以用于状态下childOf
属性是的逆parentOf
属性。 另一个示例是声明WordNet词汇表的hyponymOf
属性是可传递的。
在耶拿(Jena)中,本体被视为一种特殊的RDF模型OntModel
。 该接口允许使用编程方法来操纵本体,并使用方便的方法来创建类,属性限制等。 另一种方法是将本体视为常规RDF模型,并简单地添加定义其语义规则的语句。 清单15中演示了这两种技术。注意,还可以向现有数据模型中添加本体声明,或者使用Model.union()
将本体模型与数据模型合并。
清单15.为WordNet创建OWL本体模型
// Make a new model to act as an OWL ontology for WordNet
OntModel wnOntology = ModelFactory.createOntologyModel();
// Use OntModel's convenience method to describe
// WordNet's hyponymOf property as transitive
wnOntology.createTransitiveProperty(WordnetVocab.hyponymOf.getURI());
// Alternatively, just add a statement to the underlying model to express that
// hyponymOf is of type TransitiveProperty
wnOntology.add(WordnetVocab.hyponymOf, RDF.type, OWL.TransitiveProperty);
与耶拿的推论
给定一个本体和一个模型,Jena的推理引擎可以导出该模型未明确表达的其他语句。 耶拿(Jena)提供了几种Reasoner
类型,以与不同类型的本体一起使用。 因为您要在WordNet模型中使用OWL本体,所以需要OWLReasoner
。
下面的示例显示如何将OWL WordNet本体应用于WordNet模型本身以创建推理模型。 我实际上将在这里使用WordNet模型的子集,仅包含下义词层次结构中“植物生命”下的那些名词。 仅使用子集的原因是,推理模型需要保存在内存中,而WordNet模型太大,无法使用内存模型。 我用来提取从全共发现模型植物模型的代码包含在本文的源代码,并命名为ExtractPlants.java(参见相关主题 )。
首先,我将从ReasonerRegistry
获得一个OWLReasoner
。 ReasonerRegistry.getOWLReasoner()
以其标准配置返回OWL推理程序,对于这种简单情况而言,这是很好的。 下一步是将推理机绑定到WordNet本体。 此操作将返回准备应用本体规则的推理机。 接下来,我将使用绑定推理InfModel
从WordNet模型创建InfModel
。
从原始数据和OWL本体创建推理模型后,就可以像对待其他任何Model
实例一样对待它。 因此,如清单16所示,可以将FindHypernym.java与常规Jena模型一起使用的Java代码和RDQL查询重新应用于推理模型,而无需进行任何更改:
清单16.创建和查询推理模型
// Get a reference to the WordNet plants model
ModelMaker maker = ModelFactory.createModelRDBMaker(connection);
Model model = maker.openModel("wordnet-plants",true);
// Create an OWL reasoner
Reasoner owlReasoner = ReasonerRegistry.getOWLReasoner();
// Bind the reasoner to the WordNet ontology model
Reasoner wnReasoner = owlReasoner.bindSchema(wnOntology);
// Use the reasoner to create an inference model
InfModel infModel = ModelFactory.createInfModel(wnReasoner, model);
// Set the inference model as the source of the query
query.setSource(infModel);
// Execute the query as normal
QueryEngine qe = new QueryEngine(query);
QueryResults results = qe.exec(initialBinding);
完整的清单可在文章源中找到,名为FindInferredHypernyms.java。 清单17显示了在查询模型中查询“紫藤”的上位词时发生的情况:
清单17.运行示例FindInferredHypernyms程序
$ java FindInferredHypernyms wisteria
Hypernyms found for 'wisteria':
vine: weak-stemmed plant that derives support from climbing, twining, or creeping along a surface
tracheophyte: green plant having a vascular system: ferns, gymnosperms, angiosperms
vascular plant: green plant having a vascular system: ferns, gymnosperms, angiosperms
plant life: a living organism lacking the power of locomotion
flora: a living organism lacking the power of locomotion
plant: a living organism lacking the power of locomotion
OWL本体中包含的信息使Jena可以推断出“紫藤”在整个模型中都有上位词。
摘要
本文展示了Jena Semantic Web Toolkit的一些最重要的功能,并通过示例展示了如何创建,导入和保留RDF模型。 您还研究了查询模型的不同方法,并了解了如何使用RDQL简洁表达任意查询。 此外,您已经了解了如何使用Jena的推理引擎对基于本体的模型进行推理。
本文中的示例演示了将数据表示为RDF模型的某些功能,以及RDQL从中提取数据的灵活性。 在您自己的Java应用程序中使用RDF模型时,此处说明的基本方法将是一个有用的起点。
Jena是一个全面的RDF工具集,其功能超出了您在此处学到的知识。 耶拿项目的主页是开始学习更多有关其功能的好地方。
翻译自: https://www.ibm.com/developerworks/xml/library/j-jena/index.html
耶拿天气数据集