基本 SPARQL 查询
许多客户端库或应用程序都可以执行 SPARQL 查询。这里将重点介绍如何使用来自 Apache Jena 的 sparql
sparql --query query.rq --data basic.nt
sparqlSELECT
SELECT variable-list
WHERE {
graph pattern
}
图形模式使用图形表达一种结构关系,引用了节点和链接节点的圆弧。回想一下在 RDF 中,节点转换为主语实体,连接到属性的圆弧将它们连接到图形中的其他节点。如果想要询问特定节点的问题,您可以在模式的主语位置指定这些节点。如果想要知道一个特定属性的值,可以在模式的谓语位置指定该属性。任何您不想指定的元素都可由一个变量来表示,该变量将映射到该位置存在的任何可能值。如果不指定模式的任何部分,则会实际要求图形中的所有元组扁平化到一个结果集中。结果集的内容取决于您选择了哪些变量。
假设您拥有清单 1 中所示的 Turtle 文件(名为 basic.ttl)。
清单 1. 示例 Turtle 文件
<https://w3id.org/people/bsletten>
a <http://xmlns.com/foaf/0.1/Person> ;
<http://xmlns.com/foaf/0.1/birthday> "05-26" ;
<http://xmlns.com/foaf/0.1/name> "Brian Sletten" .
<https://w3id.org/people/mcarducci>
a <http://example.com/ns/Magician> ;
<http://xmlns.com/foaf/0.1/homepage> <http://trulymagic.com> ;
<http://xmlns.com/foaf/0.1/name> "Michael Carducci" .
basic.ttl 文件包含少量有关两个不同实体的事实。这两个实体的图形目前没有连接,所以 basic.ttl 中的完整数据集有两个不同的根节点。如果我想请求我拥有的所有信息,那么我可以避免指定图形模式的任何特定部分,而选择所有变量。
SELECT ?s ?p ?o WHERE { ?s ?p ?o }
对我的数据文件运行前面的查询,会得到清单 2 中所示的结果:
清单 2. 查询 basic.ttl 中的所有信息的结果
清单 2. 查询 basic.ttl 中的所有信息的结果
> sparql --query query.rq --data basic.ttl
--------------------------------------------------------------------------------------------------------------------------------
| s | p | o |
================================================================================================================================
| <https://w3id.org/people/mcarducci> | <http://xmlns.com/foaf/0.1/name> | "Michael Carducci" |
| <https://w3id.org/people/mcarducci> | <http://xmlns.com/foaf/0.1/homepage> | <http://trulymagic.com> |
| <https://w3id.org/people/mcarducci> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://example.com/ns/Magician> |
| <https://w3id.org/people/bsletten> | <http://xmlns.com/foaf/0.1/name> | "Brian Sletten" |
| <https://w3id.org/people/bsletten> | <http://xmlns.com/foaf/0.1/birthday> | "05-26" |
| <https://w3id.org/people/bsletten> | <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://xmlns.com/foaf/0.1/Person> |
--------------------------------------------------------------------------------------------------------------------------------
?s ?p ?o),所以清单 2 有 3 列。因为我没有为任何主语、谓语或宾语指定值,图形中的每个事实有一行。该数据集在两个图形中有 3 个元组,所以结果有 6 行。 基本上讲,此查询要求 SPARQL 处理器 “告诉我一切信息” — 不是一个对大型数据集运行的友好的查询。了解 LIMIT 子句,您将避免执行令人讨厌的查询(具体而言,可通过 SPARQL 协议在其他人的系统上个执行查询)。 定性地讲,我将获取一个名为 Michael Carducci 的魔术师的姓名和主页,以及一个名为 Brian Sletten 的人的姓名和生日。通过 上一篇文章,您了解了这些关系的含义。但是,如果您无法识别结果集中的一个实体或关系,因为这些实体或关系(在理想情况下)是可解析的 URI,所以您可以简单地对该 URI 发出一个 GET
“对不同数据集运行同一个查询将会扁平化所有元组,无论提到了哪些主语以及存在哪些与它们相关的信息。”
对不同数据集运行同一个查询将会扁平化所有元组,无论提到了哪些主语以及存在哪些与它们相关的信息。这具有重要意义。RDF 为您提供了可移植的数据。SPARQL 可为您提供可移植的查询(但这没有必要)。您不需要理解特定于领域的关系或类型,即可获取该数据和询问相关问题。
找到所有主语或所有关系
与请求所有信息相比,询问什么问题可能更有用?可否查找数据集中讨论了哪些主语?在这种情况下,您不关心各种关系或值;您只想要在任何图形中充当着主语的全面的节点列表。图形模式不会更改(因为您仍希望知道任何充当主语的实体),但您仅选择 subject 变量来将图形中的值映射到结果集中。查询变成:
SELECT ?s WHERE { ?s ?p ?o }
运行该查询会得到清单 3 中所示的(可能不符合预期的)结果。
清单 3. 仅选择 subject 变量所得到的结果集
> sparql --query subjects.rq --data basic.ttl
---------------------------------------
| s |
=======================================
| <https://w3id.org/people/mcarducci> |
| <https://w3id.org/people/mcarducci> |
| <https://w3id.org/people/mcarducci> |
| <https://w3id.org/people/bsletten> |
| <https://w3id.org/people/bsletten> |
| <https://w3id.org/people/bsletten> |
---------------------------------------
该数据仅包含两个主语,但对于每个匹配的图形模式,清单 3 中的结果都包含其主语的一个副本。因为已知每个主语的 3 个(不同)方面,所以每个主语会在结果中获得 3 个引用。为了避免此冗余性,可以使用 DISTINCT 关键词来为结果集中的每行请求一个不同的值。通过请求单个变量,您仅获得对图形中的每个惟一主语的一个引用。查询变成:
SELECT DISTINCT ?s WHERE { ?s ?p ?o }
(预期的)结果为:
> sparql --query distinct-subjects.rq --data basic.ttl
---------------------------------------
| s |
=======================================
| <https://w3id.org/people/mcarducci> |
| <https://w3id.org/people/bsletten> |
---------------------------------------
?p 变量。因为 ?p 提供图形中将主语 (?s) 连接到宾语 (?o) 的所有位置,所以 ?p
SELECT DISTINCT ?p WHERE { ?s ?p ?o }
查询结果显示了图形中使用的属性:
> sparql --query predicates.rq --data basic.ttl
-----------------------------------------------------
| p |
=====================================================
| <http://xmlns.com/foaf/0.1/name> |
| <http://xmlns.com/foaf/0.1/homepage> |
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> |
| <http://xmlns.com/foaf/0.1/birthday> |
-----------------------------------------------------
结果表明该数据使用了 FOAF 的 name、homepage 和 birthday 属性和 RDF 的 type
指定图形模式中的一个主语
现在假设您在对数据运行一个主语查询,而且知道所描述的实体的身份。如果您想要询问一个特定主语的信息,可以指定图形模式中的该值,并选择与该主语关联的所有谓语-宾语对。在这里您可以说,“告诉我您知道某个特定资源的所有信息。”要找到 Michael Carducci 资源的更多信息,查询将类似于:
SELECT ?p ?o
WHERE
{
<https://w3id.org/people/mcarducci> ?p ?o
}
结果将类似于:
> sparql --query carducci.rq --data basic.ttl
----------------------------------------------------------------------------------------
| p | o |
========================================================================================
| <http://xmlns.com/foaf/0.1/name> | "Michael Carducci" |
| <http://xmlns.com/foaf/0.1/homepage> | <http://trulymagic.com> |
| <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> | <http://example.com/ns/Magician> |
----------------------------------------------------------------------------------------
这种请求某个特定资源的信息的想法非常有用,以至于另一种 SPARQL 查询格式(称为 DESCRIBE)提供了一种替代方法:
DESCRIBE <https://w3id.org/people/mcarducci>
DESCRIBE 不会像 SELECT 查询一样生成结果集表。相反,DESCRIBE
DESCRIBE
> sparql --query describe.rq --data basic.ttl
<https://w3id.org/people/mcarducci>
a <http://example.com/ns/Magician> ;
<http://xmlns.com/foaf/0.1/homepage>
<http://trulymagic.com> ;
<http://xmlns.com/foaf/0.1/name>
"Michael Carducci" .
找到一个主语标识符
PREFIX 来简化图形模式的表达。如果使用 PREFIX 关键字来表明 foaf: 前缀指向http://xmlns.com/foaf/0.1/ 命令空间,那么可以引用更简单的 foaf:name 或 foaf:Person:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?s
WHERE
{
?s foaf:name "Michael Carducci"
}
结果与您预期的一样。在知道要使用哪个标识符后,您可以运行之前的查询来获取 Michael 的信息。或者可以组合这两步,运行一个更复杂的查询:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?p ?o
WHERE
{
?s foaf:name "Michael Carducci";
?p ?o .
}
在这里,您可以说,“告诉我您知道的所有名叫 Michael Carducci 的人的所有信息。”如果有多个有关名叫 Michael Carducci 的人的资源,您可以获得所有这些人的结果。因为姓名不是惟一标识符,所以现在您不一定知道这些资源是否指的是同一个人。
?s 变量添加到 SELECT 子句中。但即使您没有选择该标识符,仍然需要 ?s 在图形模式中发挥其作用。一个主语必须通过 foaf:nameDESCRIBE 格式。完全可以。在这里,我请求有关 ex:Magician
PREFIX ex: <http://example.com/ns/>
DESCRIBE ?s
WHERE {
?s a ex:Magician
}
a 语法糖来避免完整地指定 RDF type 属性。这里的另一个好处是,您可以利用您对该领域的理解来对该数据询问针对性的问题:为我找到某个是 Engineer 的人。为我找到任何具有此姓名的 Person。为我找到一位居住在休斯顿的 Magician。
如果您不知道使用了哪些领域类型,您可以发出另一个查询来获取它们。可以如何请求所有类型的列表?(提示:基本查询是 “向我显示包含一种类型的所有类型。”您希望指定谓语(或使用一种语法糖捷径),但不使用主语或宾语。
查询多个数据集
如果我出于某个荒唐的原因而不能更改我的 basic.ttl 文件,我仍可以扩展其中的事实,方法是将这些事实放在一个不同的文件中并将该文件添加到命令行。假设我希望捕获我所知道的 Michael 的信息。在一个不同的文件 (knows.ttl) 中,我可以说:
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
<https://w3id.org/people/bsletten>
foaf:knows <https://w3id.org/people/mcarducci> .
foaf:knows
清单 5. 查询链接的图形模式
PREFIX ex: <http://example.com/ns/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?magician ?name
WHERE {
?s foaf:name "Brian Sletten" ;
foaf:knows ?magician .
?magician a ex:Magician ;
foaf:name ?name .
}
magician 变量链接的不同的图形模式。我会说,“告诉我任何 (?s) 名叫 Brian Sletten 的人,这个人需要知道某个是 ex:Magician 类的实例 (?magician) 的人。另外,获取另一个人的姓名。”没有来自 knows.ttl 文件的元组,此查询就无法返回任何结果。但是,如果我将它包含在我的运行时模型中,就会出现奇:
> sparql --query complex.rq --data basic.ttl --data knows.ttl
------------------------------------------------------------
| magician | name |
============================================================
| <https://w3id.org/people/mcarducci> | "Michael Carducci" |
------------------------------------------------------------
查询远程数据
您刚看到了积累来自各种文件的数据的好处,这也适用于可进行 Web 寻址的资源。但是,在这里我会给您出一个难题。还记得在 上一篇文章 中,我使用了一个来自 W3ID 社区的标识符来引用我自己吗?如果您请求该资源,像清单 6 中一样,将会发生什么?
清单 6. 请求 W3ID 资源的查询
> http get https://w3id.org/people/bsletten
HTTP/1.1 303 See Other
Access-Control-Allow-Origin: *
Content-Length: 314
Content-Type: text/html; charset=iso-8859-1
Date: Tue, 31 Mar 2015 22:33:15 GMT
Location: http://bosatsu.net/foaf/brian.rdf
Server: Apache/2.4.7 (Ubuntu)
...
303 See Other 响应包含一个 Location
清单 7. 请求 W3ID 资源的查询
> http get http://bosatsu.net/foaf/brian.rdf
HTTP/1.1 200 OK
Accept-Ranges: bytes
Access-Control-Allow-Origin: *
Content-Length: 8507
Content-Type: application/rdf+xml
Date: Tue, 31 Mar 2015 22:40:04 GMT
ETag: "402ab-213b-508583ad90a40"
Last-Modified: Fri, 21 Nov 2014 06:05:21 GMT
Server: Apache/2.2.16 (Debian)
<?xml version="1.0" ?>
<rdf:RDF xmlns:cert="http://www.w3.org/ns/auth/cert#"
xmlns:contact="http://www.w3.org/2000/10/swap/pim/contact#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:foaf="http://xmlns.com/foaf/0.1/"
xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
xmlns:loc="http://simile.mit.edu/2005/05/ontologies/location#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
xmlns:wot="http://xmlns.com/wot/0.1/">
<foaf:Person rdf:about="https://w3id.org/people/bsletten">
...
</foaf:Person>
...
</rdf:RDF>
RDF 生态系统
SPARQL 查询是一种查询完全不同的数据的强大而又有效的方式,尤其在使用扩大了 RDF 数据范围的支持性技术时。
- RDFa 可用于从面向 XML 和 HTML 的文档提取 RDF。
- D2RQ 等工具或支持 R2RML 标准的工具可包装 RDBMS 数据库。
- 您可以使用 JSON-LD 将 JSON API 链接到语义 Web 标准。
- 来自 CSV on the Web WG 的新兴工具支持将电子表格转换为 RDF。
- 原生的元组库(例如 Stardog、Virtuoso 和 AllegroGraph)可直接处理 RDF。
- 网络上发布的文件始终可以存储为 RDF 序列化格式。
https://w3id.org/people/bsletten 的资源识别为 foaf:Person 类的一个实例。(这是 RDF/XML 表格一个主要类型的 rdf:type
但是,不要忘这种思路的更大背景。我为自己提供的稳定的标识符 303-重定向到一个描述我的文档(使用同一个标识符)。一个理解 Web 重定向的 SPARQL 客户端(它们大部分都理解)将解决所有这些问题。所以,在命令行上添加一个对我的引用,也会添加(在跟随重定向后)我公开宣传的有关我自己的所有信息。现在您可连接 3 个不同的数据集并询问问题:“告诉我任何知道一位名叫 Michael Carducci 的魔术师的人的兴趣。”这些兴趣来自这个新重定向的 RDF 文件。这个新查询类似于清单 8。
清单 8. 链接 3 个资源的查询
PREFIX ex: <http://example.com/ns/>
PREFIX foaf: <http://xmlns.com/foaf/0.1/>
SELECT ?interest WHERE {
?s foaf:interest ?interest ;
foaf:knows ?magician .
?magician a ex:Magician ;
foaf:name "Michael Carducci" .
}
清单 9 显示了结果。
清单 9. 来自清单 8 的查询的结果
> sparql --query interests.rq --data basic.ttl --data knows.ttl --data https://w3id.org/people/bsletten
-----------------------------------------------
| interest |
===============================================
| <http://dbpedia.org/page/Fish_%28singer%29> |
| <http://dbpedia.org/resource/Sushi> |
| <http://dbpedia.org/resource/Phish> |
| <http://www.w3.org/2000/01/sw/> |
| <http://www.w3.org/Metadata/> |
| <http://www.w3.org/RDF/> |
-----------------------------------------------
“您可询问的查询的复杂性与您拥有的数据的复杂性直接相关。”
发生了许多事情。basic.ttl 中的元组建立了一些有关 Michael 和我的简单事实。来自 knows.ttl 文件的单一事实捕获了我知道的 Michael 的事实,并将 basic.ttl 文件中的两个(平时未连接的)图形连接在一起。最后,我在支持我的 W3ID 标识符的 FOAF 文件中发布的数据表达了我的一些兴趣(以及其他趣闻)。清单 8 中的查询之所有有效,离不开从所有这 3 种来源提供的链接。您可询问的查询的复杂性与您拥有的数据的复杂性直接相关。请记住,您可获取数据,然后向它询问它所提及的内容,涉及的属性等。