基本 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 种来源提供的链接。您可询问的查询的复杂性与您拥有的数据的复杂性直接相关。请记住,您可获取数据,然后向它询问它所提及的内容,涉及的属性等。