说明:本文档实质上是阅读Jena Doc中SPARQL部分学习使用SPARQL的学习笔记,主要想和大家分享学习过程,了解如何在Windows XP中开始使用SPARQL,在命令行(command line)和Jena环境中执行简单SPARQL查询。
可以从http://jena.sourceforge.net免费获得Jena的最新版本,可以从ARQ主页http://jena.hpl.hp.com/~afs/ARQ/index.html下载最新的ARQ 发行包,从而在运行SPARQL查询。当然,在梯队网站的个人主页上,这些资源也是可得的。
SPARQL是W3C的RDF数据工作组设计的一种查询语言和协议,用于RDF数据的查询。经过类似于JDK安装时候的配置,可以在命令行运行SPARQL查询,也可以在安装了Jena API之后,在Java程序用使用SPARQL查询。
在开始学习SPARQL之前,我的机器上已经安装了JDK 5.0并在eclipse 3.2中可以使用Jena API。不知道SPARQL的命令行运行是不是需要Java环境,是不是需要Jena环境?当然,在Jena中使用SPARQL一定是需要Jena API的。我很想实验一下,但是,卸载JDK还得装,麻烦的狠,所以算了。如果谁没有装JDK,可以试一试,然后分享一下结论。
1. 下载和配置SPARQL
在 ARQ's downloads(http://jena.hpl.hp.com/~afs/ARQ/download.html) 页上找到最新的 ARQ 发行包,并解压到某个目录,我解压到了D:\Jena-2.5\ARQ-2.1-beta,实际上,解压到哪里并没有多大关系,不影响使用。
配置环境变量:鼠标右键单击【我的电脑】-【属性】-【高级】-【环境变量】,在系统变量中找到CLASSPATH,将将解压路径中的lib文件夹路径添加到CLASSPATH,对我的机器来说就是D:\Jena-2.5\ARQ-2.1-beta\lib;然后,在系统变量中找到PATH变量,将解压路径中的bat文件夹路径添加到PATH变量,对我的机器来说就是D:\Jena-2.5\ARQ-2.1-beta\bat。
第一个CLASSPATH设置保证机器可以使用开发包中的API,第二个PATH设置使得可以在任意命令行路径使用SPARQL查询。如果不做PATH设置,那么只能在D:\Jena-2.5\ARQ-2.1-beta\bat路径下使用SPARQL查询,那样会很不方便。
在一般的设置建议中,会让建一个ARQROOT变量,其值为D:\Jena-2.5\ARQ-2.1-beta,这样在随后的设置中用ARQROOT代替D:\Jena-2.5\ARQ-2.1-beta,使得环境变量的配置不至于很麻烦。我觉得这只是一个替换作用,在JDK的配置中也有类似的建议,我没有做,只是照搬完整路径。建立ROOT变量的好处在于,以后需要再配置环境变量时,直接JAVAROOT\..\就可以了,不用再去找JDK到底安装在什么目录下。
如果上面的设置成功,那么在命令行下运行sparql命令,会返回
No query string or query file
指示没有查询语句和查询数据文件。如果运行sparql –h(或者sparql –h,sparql –help,sparql –h)就会返回命令sparql的帮助信息。
2. 执行一个简单的查询
SPARQL查询语句的执行格式是:
sparql --data=<file> --query=<query>
file是要查询的数据源,RDF文件或者RDF图文件;query是查询语句文件,以.rq为文件后缀。
2.1 数据源,一个RDF文件,就是帮助文档中的vc-db-1.rdf,文档描述了一些简单的人名信息,下面是类似三元组形式的数据表示。
@prefix vCard: <http://www.w3.org/2001/vcard-rdf/3.0#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix : <#> .
<http://somewhere/MattJones/>
vCard:FN "Matt Jones" ;
vCard:N [ vCard:Family
"Jones" ;
vCard:Given
"Matthew"
] .
<http://somewhere/RebeccaSmith/>
vCard:FN "Becky Smith" ;
vCard:N [ vCard:Family
"Smith" ;
vCard:Given
"Rebecca"
] .
<http://somewhere/JohnSmith/>
vCard:FN "John Smith" ;
vCard:N [ vCard:Family
"Smith" ;
vCard:Given
"John"
] .
<http://somewhere/SarahJones/>
vCard:FN "Sarah Jones" ;
vCard:N [ vCard:Family
"Jones" ;
vCard:Given
"Sarah"
] .
2.2 查询语句q1.rq,记事本创建一个文件,内容如下,文件保存为q1.rq。注意文件后缀是.rq,不是.txt。
SELECT ?x
WHERE { ?x <http://www.w3.org/2001/vcard-rdf/3.0#FN> "John Smith" }
2.3 那么使用上面的查询语句查询vc-db-1.rdf文件中的数据的命令行语句就是
sparql --data=vc-db-1.rdf --query=q1.rq
返回结果是:
---------------------------------
| x |
=================================
| <http://somewhere/JohnSmith/> |
---------------------------------
在执行上面的查询时,要保证数据文件和查询文件在当前目录下,否则命令中应该包括完整路径,即:
sparql --data=d:\sparql\vc-db-1.rdf --query=d:\sparql\q1.rq
2.4 对查询语句和查询结果的理解
查询语句包括查询信息的名称以及名称应该符合的条件。条件子句以三元组形式出现,按照<主语,谓语,宾语>的顺序排列。查询条件也成为一个模式(Pattern)。查询的结果实际就是条件三元组与数据文件(或RDF图)中RDF三元组匹配的结果。
语句中的 ? 加一个字母表示该字母是一个变量,比如 ?x,在SELECT后面的变量会显示在查询结果中,作为列名称出现。
实际上,做到这一步之后,所有的事情只是修改模式,给模式添加一些条件了,非常简单。
3. 命名空间的简写替代
如果查询所有具有名字的实例以及该实例的名字,那么查询语句如下
SELECT ?x ?fname
WHERE {?x <http://www.w3.org/2001/vcard-rdf/3.0#FN> ?fname}
注意,“?x ?fname”之间是空格,不是逗号。如果有多个模式三元组,那么三元组之间用点号“.”隔开,比如
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 .
}
这时候,模式中的谓词的URI都带一个长长的命名空间字符串,“http://www.w3.org/2001/vcard-rdf/3.0#”,能用一个简单的单词代替它应该会比较简单。实现简写URI的的语法是这样的:
PREFIX vcard: http://www.w3.org/2001/vcard-rdf/3.0#
SELECT ?givenName
WHERE
{ ?y vcard:Family "Smith" .
?y vcard:Given ?givenName .
}
语句 PREFIX vcard: http://www.w3.org/2001/vcard-rdf/3.0# 定义了一个前缀单词vcard,在查询语句中,它与后面的命名空间等价。
4. 过滤查询结果
在查询语句中添加过滤条件的语句是
FILTER regex(?x, "pattern" [, "flags"])
FILTER是声明过滤, ?x是过滤模式作用的变量,后面的pattern是具体的限制条件,比如
PREFIX vcard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?g
WHERE
{ ?y vcard:Given ?g .
FILTER regex(?g, "r", "i") }
这是要查询一些名字(Given Name),”r”表示,名字中必须出现的字母”r”或者”R”;”i”表示,对签名的字母限制,对大小写并不敏感。若是要对大小写敏感,则去掉这个限制即可,即FILTER regex(?g, "r")。
上面查询的结果是
-------------
| g |
=============
| "Rebecca" |
| "Sarah" |
-------------
对数值限制的一个例子是:
PREFIX info <http://somewhere/peopleInfo#>
SELECT ?resource
WHERE { ?resource info:age ?age .
FILTER (?age >= 24)}
注意,在RDF文件vc-db-1.rdf中并没有包括年龄age信息,这里要查询vc-db-2.rdf文件,即
sparql --data=vc-db-2.rdf --query=q-f2.rq
查询结果是:
---------------------------------
| resource |
=================================
| <http://somewhere/JohnSmith/> |
---------------------------------
5. 可选的查询信息optional information
5.1 简单的可选信息
在一些查询中,一些需要返回的数据可能不存在,而这些不存在的数据所在的数据元素中有其他需要返回的信息,这时候就可以通过可选查询信息进行查询,比如:
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表示,模式 { ?person info:age ?age } 是可选的,不是必须满足的。这个查询的执行语句是
sparql --data=vc-db-2.rdf --query=q-opt1.rq
查询结果是
------------------------
| name | age |
=======================
| "Becky Smith" | 23 |
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones"| |
-----------------------
如果去点关键字OPTIONAL,那么,查询的结果就是
-----------------------
| name | age |
=======================
| "Becky Smith" | 23 |
| "John Smith" | 25 |
-----------------------
有些人没有年龄信息,那么,这些人的名字也不会被作为查询结果返回。
5.2 对可选模式添加过滤条件
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 ) }
}
这样返回的信息是
-----------------------
| name | age |
=======================
| "Becky Smith" | |
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones" | |
-----------------------
有些人没有年龄信息("Sarah Jones"和"Matt Jones" ),有些人的年龄小于24("Becky Smith"),他们的名字信息也会出现在查询结果中。下面的查询要求,如果有年龄信息,那么年龄必须大于24,否则不是期望的查询结果。
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 )
}
语句 !bound(?age) || ?age > 24 的意思是,没有(未绑定)age或者age大于24。如此,年龄小于24的"Becky Smith"就不会出现在这个查询的结果中了。
-----------------------
| name | age |
=======================
| "Sarah Jones" | |
| "John Smith" | 25 |
| "Matt Jones" | |
-----------------------
6. 联合查询
vCard词汇表和FOAF词汇表都可以表示人的信息,比如vCard中的 vCard:FN, FOAF中的 foaf:name.这一节介绍在一个RDF图同时用vCard:FN和 foaf:name表示人的信息时,如何查询相关数据。
注:vCard是电子商务中卡的一种文件格式标准,一般与邮件信息关联。FOAF(http://xmlns.com/foaf/0.1/)是一种RDF的应用,所列网址有它的规范。
6.1 一个RDF图文件,vc-db-3.ttl,文件的的内容为
@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" .
它只是分别用foaf:name 和vcard:FN描述了四个人名,这一节的查询将针对此文件。
6.2 查询人名信息
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 }
}
查询结果是
-----------------
| name |
=================
| "Matt Jones" |
| "Sarah Jones" |
| "Becky Smith" |
| "John Smith" |
-----------------
一个等价的查询语句是
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 ) }
6.3 记录结果的来源,查询语句和结果分别是
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name1 ?name2
WHERE{
{ [] foaf:name ?name1 } UNION { [] vCard:FN ?name2 }
}
---------------------------------
| name1 | name2 |
=================================
| "Matt Jones" | |
| "Sarah Jones" | |
| | "Becky Smith" |
| | "John Smith" |
---------------------------------
6.4 同时使用OPTIONAL和UNION
查询语句是
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
PREFIX vCard: <http://www.w3.org/2001/vcard-rdf/3.0#>
SELECT ?name1 ?name2
WHERE{
?x a foaf:Person
OPTIONAL { ?x foaf:name ?name1 }
OPTIONAL { ?x vCard:FN ?name2 }
}
查询结果是:
---------------------------------
| name1 | name2 |
=================================
| "Matt Jones" | |
| "Sarah Jones" | |
| | "Becky Smith" |
| | "John Smith" |
---------------------------------
7. 查询命名的图
图是一个RDF数据集,不是一个完整的RDF文件。现在有三个图
Default graph (ds-dft.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 .
Named graph (ds-ng-1.ttl):
@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" .
Named graph (ds-ng-2.ttl):
@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" .
7.1 查询语句是
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT *{ ?s ?p ?o }
对图的查询是(PREFIX : <.>是为了格式化输出???),
sparql --graph=ds-dft.ttl –namedgraph=ds-ng-1.ttl –namedgraph=ds-ng-2.ttl --query=q-ds-1.rq
查询结果是
----------------------------------------------------------------------
| s | p | o |
======================================================================
| :ds-ng-2.ttl | dc:date | "2005-09-22T05:53:05+01:00"^^xsd:dateTime |
| :ds-ng-1.ttl | dc:date | "2005-07-14T03:18:56+01:00"^^xsd:dateTime |
|--------------------------------------------------------------------|
7.2 查询指定图
PREFIX dc: <http://purl.org/dc/elements/1.1/>
PREFIX : <.>
SELECT ?title{
GRAPH :ds-ng-2.ttl
{ ?b dc:title ?title }}
查询结果
---------------------------------------------
| title |
=============================================
| "Harry Potter and the Sorcerer's Stone" |
| "Harry Potter and the Chamber of Secrets" |
--------------------------------------------
8. SPARQL查询结果集
四种形式的结果
¨ SELECT – 返回一个表(table),Tutorial里介绍的主要是这种查询
¨ CONSTRUCT – 返回一个RDF图
¨ DESCRIBE – 返回一个RDF图.
¨ ASK – 布尔查询
结果调整
¨ Projection 投影- keep only selected variables 只保持选择的变量
¨ OFFSET/LIMIT 偏移和限制 - chop the number solutions (best used with ORDER BY) 分解数字结果
¨ ORDER BY 排序- sorted results(同数据类型类的结果???)
¨ DISTINCT - yield only one row for one combination of variables and values.
9. 在Jena中使用SPARQL
在我的机器上,eclipse已经可以运行使用Jena API的Java程序。这样就可以直接编写SPARQL查询程序。一个简单查询的全部代码如下:
import java.io.*;
import com.hp.hpl.jena.query.Query;
import com.hp.hpl.jena.ontology.OntModel;
import com.hp.hpl.jena.ontology.OntModelSpec;
import com.hp.hpl.jena.query.QueryExecution;
import com.hp.hpl.jena.query.QueryExecutionFactory;
import com.hp.hpl.jena.query.QueryFactory;
import com.hp.hpl.jena.query.ResultSet;
import com.hp.hpl.jena.query.ResultSetFormatter;
import com.hp.hpl.jena.rdf.model.ModelFactory;
public class OntoQuery{
public static void main(String[] args) throws IOException{
// 创建一个本体模型,这里使用的是前一段时间设计的IIPO本体,附带实例。
OntModel model = ModelFactory.createOntologyModel(OntModelSpec.OWL_MEM);
model.read("file:./IIPO.v1.1.with_individuals.owl");
// 创建一个查询语句
String queryString = "SELECT ?teacher ?student" +
" WHERE " +
"{ ?teacher <http://www.owl-ontologies.com/IIPO.owl#direct> ?student}";
// 创建一个查询
Query query = QueryFactory.create(queryString);
// 执行查询,获得结果
QueryExecution qe = QueryExecutionFactory.create(query, model);
ResultSet results = qe.execSelect();
// 向控制台输出结果s
ResultSetFormatter.out(System.out, results, query);
// 释放资源
qe.close();
} // the end of main.
} // the end.
返回的结果是:
------------------------------------------------------------------------------------------------------
| teacher | student |
|====================================================================================================|
|<http://www.owl-ontologies.com/IIPO.owl#Teacher_1> | <http://www.owl-ontologies.com/IIPO.owl#Std_1> |
|----------------------------------------------------------------------------------------------------|
总结:并没能够很好的理解SPARQL,纯粹对Tutorial的翻译和rephrasing.
参考资料 (引自一篇文档http://www.ibm.com/developerworks/cn/java/j-sparql/,有删减)