目录
前言
本文是SPARQL官方入门教程,通过示例介绍SPARQL的主要功能。SPARQL是一种查询语言,用于查询RDF结构的数据。虽然RDF数据具备推理性,但是SPARQL本身没有推理查询功能,它是面向数据的,只能查询数据中包含的信息。
官方文档:sparql tutorial
1 JENA安装
本教程演示在windows 10系统下安装JENA。JENA官方下载地址:https://jena.apache.org/download/index.cgi#, 选择.zip文件,下载后解压即可。安装JENA的前提是安装了Java运行环境。解压后设置JENA环境变量。
在系统的环境变量中新建变量JENA_HOME,如下图所示,变量值填写你的JENA解压地址。
然后在系统环境变量的“PATH”变量中添加值“;%JENA_HOME%\bat”。最后测试安装是否成功,在cmd终端中输入命令:sparql --version,将出现版本号信息:
2 Data Format
首先我们要清楚待查询数据的结构。SPARQL是在RDF图上查询,一个RDF图就是一组三元组。在Jena数据库中将RDF图称为模型(models),将三元组称为陈述(statements)。有很多种协议用于描述RDF三元组,描述三元组的方式称为序列化(serialization)。用何种序列化方式并不重要,我们关心的是三元组本身内容。RDF\XML是一种序列化方式,但这种方式不适合人类阅读。我们采用另一种序列化方式:Turtle。本文使用的数据如下图所示,数据下载地址:vc-db-1.rdf,格式是RDF/XML。
使用Turtle方式描述上述图,得到如下文件内容:
@prefix vCard: <http://www.w3.org/2001/vcard-rdf/3.0#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
<http://somewhere/MattJones/> vCard:FN "Matt Jones" .
<http://somewhere/MattJones/> vCard:N _:b0 .
_:b0 vCard:Family "Jones" .
_:b0 vCard:Given "Matthew" .
<http://somewhere/RebeccaSmith/> vCard:FN "Becky Smith" .
<http://somewhere/RebeccaSmith/> vCard:N _:b1 .
_:b1 vCard:Family "Smith" .
_:b1 vCard:Given "Rebecca" .
<http://somewhere/JohnSmith/> vCard:FN "John Smith" .
<http://somewhere/JohnSmith/> vCard:N _:b2 .
_:b2 vCard:Family "Smith" .
_:b2 vCard:Given "John" .
<http://somewhere/SarahJones/> vCard:FN "Sarah Jones" .
<http://somewhere/SarahJones/> vCard:N _:b3 .
_:b3 vCard:Family "Jones" .
_:b3 vCard:Given "Sarah" .
上述内容中行的顺序可以打乱,机器在读取数据时并不关心数据的位置,这样写只是为了方便人类阅读。下载上面的数据,保存到本地电脑中。
本文在JENA安装目录下新建doc\Tutorial文件夹,将vc-db-1.rdf文件保存到该文件夹中。
3 A First SPARQL Query
本小节介绍一个简单的查询例子,并展示Jena数据库是如何执行查询过程的。
查询的数据就是7.2节展示的数据,查询语句如下:
SELECT ?x
WHERE { ?x <http://www.w3.org/2001/vcard-rdf/3.0#FN> "John Smith" }
这个语句来自q1.rq。“?”表示这是个变量,“x”是变量名。“<>”中的是一个URI,描述了一个资源地址。双引号中的内容表示字面量(literal)。Where从句中描述了一个RDF三元组,其中主体是要查询的变量,谓语和客体已经给出具体的值。JENA按照谓语和客体区数据库中查找对应的三元组,然后返回找到的三元组的主体。
将这个查询语句保存为文件q1.rq,并保存到本地的doc/Tutorial文件夹下,也就是和7.2节的数据文件vc-db-1.rdf放一起。如下所示:
打开cmd终端,进入doc/Tutorial目录下,执行命令:
sparql --data=vc-db-1.rdf --query=q1.rq
出现如下内容:
图中的“x”对应查询语句中的“x”,下方的值就是返回的结果。Where从句中也可以只指定一个值,其他两个值都是变量,如下查询语句:
SELECT ?x ?fname
WHERE {?x <http://www.w3.org/2001/vcard-rdf/3.0#FN> ?fname}
将上述语句保存为文件q-bp1.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-1.rdf --query=q-bp1.rq
返回查询结果如下图。
4 Basic Patterns
一个基本的pattern就是一组三元组pattern,即在where从句中可以有多个三元组。多个三元组中变量名相同的变量需要匹配同一个实体。如下的查询语句中,两个三元组中的变量y表示的是同一个实体。
SELECT ?givenName
WHERE
{ ?y <http://www.w3.org/2001/vcard-rdf/3.0#Family> "Smith" .
?y <http://www.w3.org/2001/vcard-rdf/3.0#Given> ?givenName .
}
将上述查询语句保存为文件q-bq2.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-1.rdf --query=q-bp2.rq
返回查询结果如下图。
SPARQL支持URI简写,可将上面的查询语句改为如下形式:
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?givenName
WHERE
{ ?y vcard:Family "Smith" .
?y vcard:Given ?givenName .
}
第一行表示用vcard代替http://www.w3.org/2001/vcard-rdf/3.0#。将上述查询语句保存为文件q-bq3.rq,放到doc/Tutorial目录下。在终端执行命令:sparql --data=vc-db-1.rdf --query=q-bp3.rq。
同样也可以返回查询的中间节点,上述查询中的节点‘y’就是中间节点,只要在select从句中加上该变量名即可,如下所示:
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?y ?givenName
WHERE
{ ?y vcard:Family "Smith" .
?y vcard:Given ?givenName .
}
将上述查询语句保存为文件q-bq4.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-1.rdf --query=q-bp4.rq
返回查询结果如下图。
返回的结果中‘y’具有不同的值,这是因为这个变量在数据中是个空白节点,针对空白节点,系统自动安排变量值。即使是同一个空白节点,系统打印的变量值也是不同的。
5 Filters
本文讲解如何给返回结果加上限制条件。第一种是使用正则表达式对返回结果中的字符串作出限制。语法如下:
FILTER regex(?x, "pattern" [, "flags"])
Pattern表示给出的正则表达式,flag是可选项。给出如下查询语句:
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?g
WHERE
{ ?y vcard:Given ?g .
FILTER regex(?g, "r", "i") }
“r”表示返回的字符串中需要包含字母“r”,“i”是一个flag,表示不区分字符串中的大小写。将上述查询语句保存为文件q-f1.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-1.rdf --query=q-f1.rq
返回查询结果如下图。
第二种是按条件返回结果。我们使用新的数据集,在其中加入人物的年龄信息,部分内容如下所示:
<rdf:RDF
xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
xmlns:vCard='http://www.w3.org/2001/vcard-rdf/3.0#'
xmlns:info='http://somewhere/peopleInfo#'
>
<rdf:Description rdf:about="http://somewhere/JohnSmith">
<vCard:FN>John Smith</vCard:FN>
<info:age rdf:datatype='http://www.w3.org/2001/XMLSchema#integer'>25</info:age>
<vCard:N rdf:parseType="Resource">
<vCard:Family>Smith</vCard:Family>
<vCard:Given>John</vCard:Given>
</vCard:N>
</rdf:Description>
<rdf:Description rdf:about="http://somewhere/RebeccaSmith">
<vCard:FN>Becky Smith</vCard:FN>
<info:age rdf:datatype='http://www.w3.org/2001/XMLSchema#integer'>23</info:age>
<vCard:N rdf:parseType="Resource">
<vCard:Family>Smith</vCard:Family>
<vCard:Given>Rebecca</vCard:Given>
</vCard:N>
</rdf:Description>
<rdf:Description rdf:about="http://somewhere/SarahJones">
<vCard:FN>Sarah Jones</vCard:FN>
<vCard:N rdf:parseType="Resource">
<vCard:Family>Jones</vCard:Family>
<vCard:Given>Sarah</vCard:Given>
</vCard:N>
</rdf:Description>
<rdf:Description rdf:about="http://somewhere/MattJones">
<vCard:FN>Matt Jones</vCard:FN>
<vCard:N
vCard:Family="Jones"
vCard:Given="Matthew"/>
</rdf:Description>
</rdf:RDF>
我们查询所有年龄大于24岁的人,查询语句如下所示:
PREFIX info: <http://somewhere/peopleInfo#>
SELECT ?resource
WHERE
{
?resource info:age ?age .
FILTER (?age >= 24)
}
将上述查询语句保存为文件q-f2.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-1.rdf --query=q-f2.rq
返回查询结果如下图。
6 Optional Information
RDF 是半结构化数据,RDF中不同的实体可能具有不同的属性。SPARQL可以查询RDF中存在的信息,但是在查询不存在的信息时并不会显示查询失败,也不会返回任何结果。可以使用Optional关键字,表示该查询是可选的,也就是当实体存在该属性时,就返回结果,不存在时就返回空。
例如下面的查询语句:
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age }
}
Optional后的三元组表示实体存在age属性时就返回该属性结果,不存在就返回空。将上述查询语句保存为文件q-opt1.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-2.rdf --query=q-opt1.rq
返回查询结果如下图。
如果没有optional关键字,上述查询语句就不会返回没有age属性的实体。如下所示,删除optional后的查询语句:
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
?person info:age ?age .
}
将上述查询语句保存为文件q-opt2.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-2.rdf --query=q-opt2.rq
返回查询结果如下图。
Filter关键词也可以在optional中使用,如下所示:
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age . FILTER ( ?age > 24 ) }
}
上述查询将filter关键词放在optional的三元组中,因此filter只对该三元组的结果有效。将上述查询语句保存为文件q-opt3.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-2.rdf --query=q-opt3.rq
返回查询结果如下图。
如果将filter放到外面,filter将对所有返回结果都有效,例如:
PREFIX info: <http://somewhere/peopleInfo#>
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name ?age
WHERE
{
?person vcard:FN ?name .
OPTIONAL { ?person info:age ?age . }
FILTER ( !bound(?age) || ?age > 24 )
}
Filter中的条件表示若实体存在age属性,则必须大于24,否则不返回。对于不存在age属性的实体,仍可以返回。这是因为!bound表示未绑定的意思,!bound(?age)意思是实体可以没有age属性。因此这个filter语句的含义是:保留age大于24的实体或者没有age属性的实体。将上述查询语句保存为文件q-opt4.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-2.rdf --query=q-opt4.rq
返回查询结果如下图。
如果filter中没有!bound(?age),则只会返回一个结果,如下:
7 Alternatives in a Pattern
有时我们需要联合多种条件进行查询,UNION关键词可以实现此目的。UNION用于联合多个查询三元组,只要实体满足其中一个三元组即可返回。本节使用的数据内容如下:
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
@prefix vcard: <http://www.w3.org/2001/vcard-rdf/3.0#> .
_:a foaf:name "Matt Jones" .
_:b foaf:name "Sarah Jones" .
_:c vcard:FN "Becky Smith" .
_:d vcard:FN "John Smith" .
将上述内容保存为文件vc-db-3.ttl,放到doc/Tutorial目录下。查询语句如下:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name
WHERE
{
{ [] foaf:name ?name } UNION { [] vCard:FN ?name }
}
将上述查询语句保存为文件q-union1.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-3.ttl --query=q-union1.rq
返回查询结果如下图。
也可以使用filter关键词实现同样的功能,查询语言如下:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name
WHERE
{
[] ?p ?name
FILTER ( ?p = foaf:name || ?p = vCard:FN )
}
这种方式效率可能没有前一种快,这里是先查询出所有的实体,然后在进行过滤。将上述查询语句保存为文件q-union-1alt.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=vc-db-3.ttl --query=q-union-1alt.rq
返回查询结果和上一个相同。
8 Datasets
有时候一个graph中含有多个graph,例如下面的default graph中含有ds-ng-1.ttl和ds-ng-2.ttl。
@prefix dc: <http://purl.org/dc/elements/1.1/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
<ds-ng-1.ttl> dc:date "2005-07-14T03:18:56+0100"^^xsd:dateTime .
<ds-ng-2.ttl> dc:date "2005-09-22T05:53:05+0100"^^xsd:dateTime .
ds-ng-1.ttl是一个named graph,其内容如下:
@prefix dc: <http://purl.org/dc/elements/1.1/> .
[] dc:title "Harry Potter and the Philospher's Stone" .
[] dc:title "Harry Potter and the Chamber of Secrets" .
ds-ng-2.ttl是一个named graph,其内容如下:
@prefix dc: <http://purl.org/dc/elements/1.1/> .
[] dc:title "Harry Potter and the Sorcerer's Stone" .
[] dc:title "Harry Potter and the Chamber of Secrets" .
返回default graph中的所有三元组,尽管default graph中引用了其他的named graph,这些named graph中的三元组是不会返回的。查询语句如下:
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT *
{ ?s ?p ?o }
其中的“PREFIX : <.>” 仅是为了简化输出格式。将上述查询语句保存为文件q-ds-1.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=ds-dft.ttl --query=q-ds-1.rq
返回查询结果如下。
下面的查询语句将返回引用的named graph内容。查询语句如下所示:
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT *
{
{ ?s ?p ?o } UNION { GRAPH ?g { ?s ?p ?o } }
}
GRPHA关键词表示变量g是一个named graph。将上述查询语句保存为文件q-ds-2.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=ds-dft.ttl --query=q-ds-2.rq --graph ds-dft.ttl --namedgraph ds-ng-1.ttl --namedgraph ds-ng-2.ttl
返回查询结果如下。
也可以指定查询某个named graph,如下查询语句指定查询ds-ng-2.ttl。
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT ?title
{
GRAPH :ds-ng-2.ttl
{ ?b dc:title ?title }
}
将上述查询语句保存为文件q-ds-3.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=ds-dft.ttl --query=q-ds-3.rq --graph ds-dft.ttl --namedgraph ds-ng-1.ttl --namedgraph ds-ng-2.ttl
返回查询结果如下。
Named graph也可以作为一个变量,使用条件查询来选择named graph。查询语句如下:
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT ?date ?title
{
?g dc:date ?date . FILTER (?date > "2005-08-01T00:00:00Z"^^xsd:dateTime )
GRAPH ?g
{ ?b dc:title ?title }
}
Filter关键词筛选变量g,然后将g作为named graph。将上述查询语句保存为文件q-ds-4.rq,放到doc/Tutorial目录下。在终端执行命令:
sparql --data=ds-dft.ttl --query=q-ds-4.rq --graph ds-dft.ttl --namedgraph ds-ng-1.ttl --namedgraph ds-ng-2.ttl
返回查询结果如下。