理解SPARQL资料.docx
《理解SPARQL资料.docx》由会员分享,可在线阅读,更多相关《理解SPARQL资料.docx(18页珍藏版)》请在冰豆网上搜索。
理解SPARQL资料
理解SPARQL
使用语义Web建立微型日志博客
作者:
AndrewMatthews,架构师和开发人员,自由职业者
简介:
代表Web未来的语义Web是一个以知识为中心的模型,除了人类可读的文档和XML消息格式之外,它还增加了机器可以理解和处理的数据。
SPARQLProtocolandRDFQueryLanguage(SPARQL)对于语义Web就像SQL对于关系数据库一样重要。
它允许应用程序对分布式RDF数据库进行复杂的查询,并得到了互相竞争的多种框架的支持。
本教程通过一家虚拟公司的团队跟踪和日志系统演示了它的用法。
开始之前
常用缩写词
∙API:
应用程序编程接口
∙DOM:
文档对象模型
∙FTP:
文件传输协议
∙HTTP:
超文本传输协议
∙HTML:
超文本标记语言
∙OWL:
Web本体语言
∙RDF:
资源描述框架
∙URI:
统一资源标识符
∙URL:
统一资源定位符
∙W3C:
万维网联盟
∙XML:
可扩展标记语言
本教程是为那些拥有很少或者没有语义Web应用程序开发经验的开发人员编写的。
不需要任何编程或者开发工具,但是需要对Web基础有所了解。
阅读完本教程后将能够使用Turtle语言生成RDF和OWL本体(ontology)。
还会学到如何使用Jena和Joseki支持本体,以及使用SPARQL进行查询。
关于本教程
本教程介绍了SPARQL以及基础数据格式。
还涵盖了RDF、RDFSchema、OWL以及Turtle知识表示语言。
通过这些语言可以建立本体 或者域模型。
本教程的例子建立了用于日志和预约系统的本体和查询,可以生成带语义标签的类似twitter的微型博客。
通过查询博客记录可以发现公司中具备您的项目所需技能并且可以组成团队的人员。
本教程的主要内容如下:
∙介绍语义Web,包括RDF、OWL和SPARQL
∙如何使用Joseki建立RDFTripleStore
∙编写日志系统的SPARQL查询
前提条件
要学习本教程,需要下列工具:
∙Java™环境— Java运行时环境,用于运行SPARQL的服务器Joseki。
∙Joseki—这是Hewlett-Packard提供的开放源代码的SPARQL服务器。
可从 http:
//www.joseki.org/ 获得。
通过该链接找到SourceForge下载区。
下载最新版本的Joseki到机器上并解压到选定的位置。
记住解压的位置,需要编写一个简单的批处理文件启动服务器。
∙Jena—语义Web框架(作为Joseki的一部分提供)。
∙任何文本编辑器。
Hewlett-PackardLabs的一个团队从2003年开始开发Joseki。
它为Jena语义Web框架提供了HTTP支持层,后者也是由Hewlett-Packard开发的。
这可能是最流行的语义Web平台,它的一位开发者也是SPARQL标准的编辑,因此基本上和标准同步,有时候也作为新想法的实验台。
稍后的 配置Joseki 将详细介绍如何配置Joseki和创建的RDF文件
语义Web
这一节将定义语义Web,说明RDF和OWL是什么,它们是如何工作的,以及如何使用它们为语义Web应用程序建立域模型。
SPARQL的历史
SPARQL建立在多项关键技术的基础之上,就像HTTP和HTML(万维网的基础)依赖于TCP/IP这样的更深和更低层系统一样。
在介绍SPARQL之前首先看看一些重要的标准,它们为什么存在,对于语义Web开发人员来说意味着什么。
1997年,TimBerners-Lee指出HTML和万维网存在着局限性。
其设计的目标不是动态Web应用程序,更不用说现在的复杂分布式系统了。
HTML和HTTP仅仅是迈向更远大的目标——机器与机器之间的半自动通信——的(重要的)一步,对于我们来说就像只有FTP时的WWW一样。
实现这个目标的基础是RDF(资源描述框架)。
RDF可以描述任何事物,包括它自身,因此可以从很小的一层开始逐渐丰富。
这种薄层方法用于建立词汇栈。
图1显示了W3C定义的层。
目前,RDF之上的层包括RDFS和OWL(有人认为将来的工作是在OWL上进行构建)。
RDFS即RDFSchema语言,它为RDF添加类和属性。
OWL(Web本体语言)扩展RDFS,提供了一种更丰富的语言来定义类之间的关系。
更丰富的语言允许使用自动化的推理引擎创建更智能的系统。
图1.语义Web:
W3CWeb体系结构的技术组合
下一节将介绍如何构造RDF,以及如何使用RDF构建世界模型。
RDF
RDF曾经被称为“元描述语言”,但这种有趣的提法不过是说它用于描述事物。
它描述事物的方式和人类一样,比如“乌鸦吃玉米”或“Joni爱Chachi”之类的句子。
每个句子都有主语(乌鸦,Joni)、谓语(吃,爱)和宾语(玉米,Chachi)。
在RDF中这种主谓宾结构称为三元组。
RDF使用三元组描述任何事物。
直观地表达这类三元组的一种方式是RDF图,它是RDF语句的一个集合。
图用节点和弧线定义。
RDF中的节点表示资源,弧线则是谓语—即关于主语和宾语节点之间的关系的陈述。
RDF规范的核心就是定义图,其他(如序列化格式等等)都是次要的。
主语和宾语成分定义了图中的节点(也称为资源,因为它们是URI的目标)。
每个谓语定义了三元组引用的两个节点之间的关系。
为了能够从Web上的其他位置访问图,需要将RDF文件保存到三元组库 —即存储组成图的三元组的地方。
将RDF图存储到三元组库并公开到Web上之后,其他人就可使用SPARQL查询了(如图2所示)。
图2.RDF图的直观表示,包含Subject和Object之间的一个谓语关系陈述
RDF中的每个节点和谓语都用URI标识。
RDF也允许不使用URI标识的节点,称为空白节点(BlankNode)或空白节点标识符(BlankNodeIdentifier),作为用于本地引用的临时的、内部可见的标识符。
RDF规范指出,虽然提供了将RDF序列化为XML的一个标准,但是允许使用任何等价的结构。
下面的RDFXML描述关于作者的一个三元组(如清单1所示)。
清单1.描述作者的三元组的RDFXML
xmlversion="1.0"?
>
RDFxmlns:
rdf="http:
//www.w3.org/1999/02/22-rdf-syntax-ns#"xmlns:
mu="http:
//aabs.purl.org/music#">
Descriptionrdf:
about="http:
//aabs.purl.org/music#andrew">
playsInstrument>
Descriptionrdf:
about="http:
//aabs.purl.org/music#guitar"/>
playsInstrument>
Description>
RDF>
它说明的是“Andrew弹吉他”,或者更精确地说“称为Andrew的某人弹奏称为吉他的乐器”。
必须承认,传递这么少的信息,上述的XML太长了。
下面是Turtle语言的描述,该语言也是由W3C掌控的(如清单2所示)。
清单2.和清单1相同的RDF,使用Turtle描述
@prefix:
//aabs.purl.org/music#>.:
andrew:
playsInstrument:
guitar.
好多了!
这些语句的信噪比高得多,仅此一点就值得使用了。
W3C希望把Turtle作为人类可读和可写的RDF语言。
SPARQL使用Turtle,因此后面所有的例子都将使用它。
一个三元组的后面用句点(.)作为分隔符,因此空格无关紧要(如图3所示)。
图3.用一条语句声明‘Andrew’和‘guitar’通过‘playsInstrument’关系相关联
除了用于在RDF文件中嵌入XML的XMLLiteral类型外,RDF没有内置类型。
可以使用XMLSchema定义的以及那些最常用的数据类型。
在Turtle中,文字的数据类型添加在数据后面,如清单3所示。
清单3.在RDF中使用XMLSchema数据类型
@prefixxsdt:
//www.w3.org/2001/XMLSchema#>.@prefixmu:
//aabs.purl.org/music#>.:
andrew:
boughtInstrument"1987-06-13T10:
30:
00"^^xsdt:
dateTime.
^^ 将数据类型声明(xsdt:
dataType)附加到数据的字符串表示("1987-06-13T10:
30:
00" 部分)上面。
因此它说明的是“Andrewboughttheguitaron13thJune1987attenthirty”。
这一规则的例外是,不需要其类型的相关线索就能够明确解析的类型。
因此像5这样没有任何说明的数字显然是一个整数。
类似的,像 Andrew 这样没有任何说明的字符串也只能是一个字符串。
布尔类型和小数类型也适用于这种情况。
整数、布尔值和小数可以直接给出数字而不需要引号。
语句 :
guitar:
timesRestrung500 使用了整数。
字符串可以用引号引起来(按照TimBerners-Lee目前最青睐的语言Python的一般规则),比如 :
guitar:
makersModel"GL350" 或者清单4。
清单4.使用Python风格的三元组用引号引起多行
:
guitar:
makersModel"""GL350somemoretextonanewline(providedyouusetriplequotes)""".
图4显示了清单 2、3 和 4 对应的RDF图。
图4.关于Andrew和他的吉他的语句
RDF没有定义标准数据结构。
和一般的编程语言一样,语言设计者使用简单的结构进行扩展。
RDF提供了‘包’、‘序列’和‘替换列表’。
这些结构通过三元组实现,暂时先不要管。
如果想进一步研究,请阅读RDFPrimer(链接见 参考资料)。
Turtle为这些数据结构提供了原生的语法支持。
列表的声明为:
:
andrew:
child(:
emily:
thomas)。
它相当于清单5。
清单5.使用列表将一个主语和谓语用于多个宾语
:
andrew:
child:
emily.:
andrew:
child:
thomas.
也可在三元组的主语中使用列表:
(:
thomas:
emily):
parent:
andrew.。
要在RDF中声明没有URI的资源,可以使用空白节点标识符—本地引用的临时名称。
清单6显示了JournalEntries本体的一个例子:
清单6.使用谓语-宾语列表描述单个主语的多条语句
_:
JohnConnorau:
User;u:
domainLogin"someDomain/john.connor";u:
displayName"JohnConnor".
_:
JohnConnor 是一个空白节点(用前导下划线表示),可在本地引用。
您可能希望通过这种方式编写RDF,而不使用“:
JohnConnor”(外部可见的),因为域登录和将资源绑定到了LDAP(轻型目录访问协议),这可能是您的本地资源。
两种选择都可以。
另一种空白节点语法使用“[]”表示您不愿提供本地名称的资源。
仅用于当前的三元组或者谓语-宾语列表(如清单7所示)。
清单7.使用空白节点语法和谓语-宾语列表建立没有标识符的完整主语
[]aj:
JournalEntry;j:
date"20080205T09:
00:
00"^^xsdt:
dateTime;j:
user_:
JohnConnor;j:
notes"""TodayIlearnthowtodefraudATMmachinesandhowtofieldstripamachinegunblindfolded.""";j:
tag"armaments";j:
tag"cash".
如果没有名称,或者不希望用毫无意义的标识符污染应用程序名称空间,则可以采用这种形式。
毕竟,使用SPARQL,可以根据相关的数据而不仅仅是URI来查询资源。
使用空白节点的时候,三元组库将独立地定位空白节点标识符。
这种形式适用于一次性的资源,因为在资源定义后面的句点之后就无法直接链接一次性资源了。
从上面两个例子可以看到,可用分号让多个三元组共享一个主语。
这称为谓语-宾语列表,经常要用到,因为它允许合并关于同一主语的多个语句。
事实上,如果没有谓语-宾语列表这种表示法,上面的空白节点表示法就毫无用处。
RDFS和OWL
RDF特意设计来支持用更抽象的词汇表进行分层扩展。
扩展的第一种方式是使用RDFVocabularyDescriptionLanguage(RDF词汇表描述语言),通常被称为RDFSchema或RDFS。
RDFS为RDF增加了类、属性和继承的特性,几乎是面向对象设计者完备的工具箱。
OWL在RDFS的基础上提供了及其丰富的工具箱来描述类的属性和关系。
OWL提供了大量的属性来准确描述两个类之间关系的特点。
OWL的主要动机是为本体的语义打下坚实的基础,使推理引擎能够对数据进行自由演绎。
RDF类和属性
RDFS定义了一个三元组谓语 rdfs:
type,声明资源的类型。
允许声明类资源继承自其他类。
清单8是类型声明的一个例子。
清单8.使用 rdfs:
type 和 rdfs:
subClassOf 定义类的层次结构
:
HeavenlyBodyrdfs:
typerdfs:
Class.:
Planet,:
Asteroid,:
Comet,:
Meteorrdfs:
subClassOf:
HeavenlyBody.
图5定义了一个小型的类层次结构。
图5.清单8创建的类的层次结构(使用UML表示图)
定义类的实例和类声明非常相似。
RDFS通过 rdfs:
subClassOf 谓语表明主语是一个类而不是实例(如清单9所示),从而区分类声明三元组和成员。
清单9.使用 rdfs:
type 定义类实例
:
Mercury,:
Venus,:
Earth,:
Marsrdfs:
type:
Planet.:
Ceres,:
Vestardfs:
type:
Asteroid#...
Turtle为类型声明提供了方便的简写形式(如清单10所示)。
清单10.使用 a 作为 rdfs:
type 的简写形式
:
GasGiantrdfs:
subClassOf:
Planet.:
Jupiter,:
Saturn,:
Uranus,:
Neptunea:
GasGiant.
a 仅仅是 rdfs:
type 的简写形式,没有其他意义。
RDFS也提供类的属性。
清单11说明了属性的声明方法。
清单11.为类 :
HeavenlyBody 定义属性
:
massKgrdfs:
domain:
HeavenlyBody;rdfs:
rangexsdt:
double.
这里声明的属性 :
massKg 从 :
HeavenlyBody 映射到一个双精度数。
换句话说,它表明所有的天体都能有质量。
前面的实例声明可以改写为清单12。
清单12.在实例中使用属性
:
Eartha:
Planet;:
massKg"5.9742e24"^^xsdt:
double.
RDFS提供的设施和OWL相比非常少。
OWL提供了大量的方法来描述两个类之间的微妙关系。
现在没有足够的时间来讨论,仅通过例子了解一下OWL(如清单13所示)。
清单13.使用OWL更详细地描述属性
:
hasUsedSkillaowl:
ObjectProperty;rdfs:
domain:
User;rdfs:
range:
Skill;owl:
equivalentProperty:
hasSkill;owl:
inverseProperty:
hasBeenUsedBy.
它说明了属性 :
hasUsedSkill 和 :
User、:
Skill 这两类对象有关。
它和 :
hasSkill 属性相同(表示同一件事)。
此外,还断言如果一项技巧被某人 :
hasBeenUsedBy,就意味着此人 :
hasSkill 和 :
hasUsedSkill。
换句话说,OWL允许告诉推理引擎在本体中定义的各种属性之间隐含的等价意义。
OWL非常丰富,不过现在使用RDFS就行了,因为本教程没有涉及推理引擎的使用。
还有一个主题本文不打算讨论,即由来已久的关于面向对象和数据建模哪种方法更好的争论。
目前来看显然面向对象将统治应用程序开发世界,因此能否方便地建立本体和对象域之间的映射至关重要。
LinqToRdf之类的系统表明建立这样的映射是可能的,面临的真正问题是纳入OWL本体的信息太多了。
这是祸中得福。
理论讲得够多了。
对于SPARQL来说,您现在对RDF的了解已经够了。
上面的简要介绍没有涉及RDF之上的层次,也没有讨论已经得到广泛应用的那些本体论,如FOAF、SIOC以及DublinCore。
也没有深入探讨激动人心的推理引擎、规则标记语言或者公式,这些技术都拥有强大的功能,前途无量。
也许会在其他教程中分别讨论。
现在看看更加实际的问题。
需要建立三元组库和SPARQL端点。
之后就可以开发本体了。
用OWL定义本体
本教程开发了一个简单的程序,可以定义日志项说明您做了什么。
然后开发SPARQL查询提出各种问题,看看这家虚构的公司中有谁做了什么。
前面曾经提到这些日志条目是一个类似twitter的微型博客。
需要知道,本教程中所有这些例子都是为了引出后面的解释。
日志系统需要UI来显示记录的内容。
这里不再介绍,因为大多数开发人员都对这方面非常熟悉,而且developerWorks很多教程都有详细的介绍(链接参见 参考资料)。
本体定义后面将创建的数据的格式。
这个本体论定义了带属性的类。
微型博客上的记录将采用符合该本体格式的Turtle片段来编写。
一旦数据存储到三元组库中,就可以使用SPARQL查询。
SPARQL查询返回的结果采用XML格式,封装了和查询匹配的变量。
很容易提取XML信息并显示在Web上。
首先定义日志的本体(如清单14所示)。
实际上非常简单—只有两个类。
这定义分别称为 JournalEntry 和 User 的两个类。
每个User 可以定义任意数量的日志项,每个日志项可以包含日期、对用户的引用和一组标签。
清单14.小而完整的日志系统本体
@prefixlog:
//www.w3.org/2000/10/swap/log#>.
@prefixstring:
//www.w3.org/2000/10/swap/string#>.
@prefixos:
//www.w3.org/2000/10/swap/os#>.
@prefixowl:
//www.w3.org/2002/07/owl#>.
@prefixj:
.
@prefix:
<#>.
#JournalEntryclass :
JournalEntryaowl:
Class.
:
daterdfs:
domain:
JournalEntry;rdfs:
rangexsdt:
datetime;owl:
cardinality1.
:
userrdfs:
domain:
JournalEntry;rdfs:
range:
User;owl:
cardinality1.
:
notesrdfs:
domain:
JournalEntry;rdfs:
rangexsdt:
string;owl:
cardinality1.
:
tagrdfs:
domain:
JournalEntry;rdfs:
rangexsdt:
string.
#Userclass:
Useraowl:
class.
:
domainLoginrdfs:
domain:
User;rdfs:
range
:
xsdt:
string;owl:
cardinality1.
:
displayNamerdfs:
domain:
User;rdfs:
rangexsdt:
string;owl:
maxCardinality1.
下面在JournalEntries.n3文件中定义几个JournalEntry元素。
首先应该定义一个 :
User 实例(如清单15所示)。
清单15.描述作者的本体条目
_:
AndrewMatthewsa:
User;:
domainLogin"someDomain/andrew.matthews";:
displayName"AndrewMatthews".
可以看到这个条目符合Journal.n3中的定义。
有了用户条目之后就可以定义日志条目了(如清单16所示)。
清单16.使用本体创建匿名日志条目
[]a:
JournalEntry;
:
date"20080204"^^xsdt:
datetime;
:
user_:
AndrewMatthews;
:
notes"""TodayIwrotesomemorecontentforthegreatnewSPARQLtutorialthatI'vebeenpreparing.IusedsomeN3todoitin,andIdefinedasimpleontologyfordefiningjournalentries.Thisisanexampleofoneofthoseentries!
""";
:
tag"N3";
:
tag"RDF";
:
tag"OWL";
:
tag"tutorial".
[]a:
JournalEntry;
:
date"20080205"^^xsdt:
datetime;
:
user_:
AndrewMatthews;
:
notes"""Today,Iwrotesomemorecontentforthetu