查询语义网
SPARQL 查询语言是专门为 RDF 设计的,它能够让我们通过选择、抽取等方式很容易地从被表示为 RDF 的知识中获得特定的部分。
# 1. SPARQL 基础设施
三元组存储库(triple store)就是一个 RDF 的数据库,也称为图存储库。
查询之前,需要先向其中填充 RDF 数据,SPARQL 更新机制提供了一系列向三元组存储库中插入、加载和删除 RDF 的选项。
当数据被加载后,就可以使用 SPARQL 协议来发送 SPARQL 来实现查询了。每个三元组存储库提供一个端点(endpoint),在此使用 HTTP 协议来向端点发送查询。
dbpedia.org/sparql
提供了一个查询端点来查询一份维基百科的 RDF 表示。在CKAN.org
中可以找到一份完整的 SPARQL 端点清单。
# 2. 基础知识:匹配模式
# 2.1 SPARQL 结构
有一份描述 Baron Way 公寓及其位置的 RDF 数据:
@prefix swp: <http://www.semanticwebprimer.org/ontology/apartments.tl#>.
@prefix dbpedia: <http://dbpedia.org/resource/>.
@prefix dbpedia-owl: <http://dbpedia.org/ontology/>.
swp:BaronWayApartment swp:hasNumberOfBedrooms 3;
swp:isPartOf swp:Baron WayBuilding.
swp:BaronWayBuilding dbpedia-owl:location dbpedia:Amsterdam,
dbpedia:Netherlands.
2
3
4
5
6
7
8
如果我们想找到这幢建筑的位置,即我们想要匹配的是下面这个三元组:swp:BaronWayBuilding dbpedia-owl:location dbpedia:Amsterdam.
,该怎样构建 SPARQL 呢?
在 SPAROL 中,我们可以将三元组中的任何一个元素替换为一个变量。变量的首字符是一个 ?
(问号)。要引入一个变量表示位置,我们可以这样写:
swp:BaronWayBuilding dbpedia-owl:location ?location.
三元组存储库将接收这个图模式并尝试去找到能够匹配这个模式的那些三元组集合。因此,在之前的RDF数据上运行这个模式,一个三元组存储库将会这回 dbpedia:Amsterdam 和 dbpedia:Netherlands。本质上,它找到了所有以 swp:BaronWayBuilding 作为主语、dbpedia-owl:location 作为谓语的三元组。
要构建一个完整的SPARQL查询,还需要增加一些内容:
- PREFIX 关键字指定各种 URL 缩写的前缀
- SELECT 关键字表明了对哪些变量是感兴趣的
- WHERE 关键词指明需要被匹配的图模式
示例如下:
PREFIX swp: <http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX dbpedia: <http://dbpedia.org/resource/>.
PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>.
SELECT ?location
WHERE {
swp:BaronWayBuilding dbpedia-owl:location ?location.
}
2
3
4
5
6
7
8
返回的查询结果是一组称作绑定(binding)的映射,表示了哪些元素对应到一个给定的变量。表格中的每一行是一条结果或一个绑定。因此,这条查询的结果如下:
?location |
---|
http://dbpedia.org/resource/Amsterdam |
http://dbpedia.org/resource/Netherlands |
SPARQL 的全部基础就是这么简单:尝试去找到能够匹配一个给定图模式的那些三元组的集合。无论模式多么复杂,运行的过程都是一样的。
文字也可以直接包含在图模式中。与 Turtle 类似,SPARQL 也允许常见文字的缩写形式,比如 3 是 “3”^^xsd:integer
的缩写表示。SPARQL 和 Turtle 的各种语法缩写表示是一样的。
# 2.2 变量名的重用
再举一个例子,查找到 BaronWayApartment 的位置,其 SPARQL:
PREFIX swp: <http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX dbpedia: <http://dbpedia.org/resource/>.
PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>.
SELECT ?location
WHERE {
swp:BaronWayApartment swp:isPartOf ?building.
?building dbpedia-owl:location ?location.
}
2
3
4
5
6
7
8
9
- 在这个查询中,查询重用了变量名
?building
。这样,三元组存储库就知道它应该找那些第一条的宾语与第二条的主语相同的三元组。
# 2.3 多变量名
我们也可以不局限于只匹配一个变量,比如如果我们想找到关于 Baron Way Apartment 的所有信息,查询为:
...
SELECT ?p ?o
WHERE {}
swp:BaronWayApartment ?p ?o.
}
2
3
4
5
将返回结果:
?p | ?o |
---|---|
swp:hasNumberOfBedrooms | 3 |
swp:isPartOf | swp:BaronWayBuilding |
# 2.4 LIMIT 关键字
在更大的数据集上,我们可能不知道有多少条结果。因此,一个好的做法是限制一条查询能返回的答案的数量,特别是使用公共端点时。这很容易通过 LIMIT 关键词来实现:
...
SELECT ?p ?o
WHERE {}
swp:BaronWayApartment ?p ?o.
}
LIMIT 10
2
3
4
5
6
- 限制了结果条数为 10
# 2.5 属性路径机制
我们已经学会如何去匹配由多个三元组模式构成的链,SPARQL 提供了一种精确表达属性链的方式,这种机制称为属性路径。例如我们想找到所有作为一幢位于 Amsterdam 的建筑的一部分的那些公寓:
PREFIX swp: <http://www.semanticwebprimer.org/ontology/apartments.ttl#>PREFIX dbpedia:<http://dbpedia.org/resource/>.
PREFIX dbpedia-owl: <http://dbpedia.org/ontology/>
SELECT ?apartment
WHERE {
?apartment swp:isPartOf ?building.
?building dbpedia-owl:location dbpedia:Amsterdam.
}
2
3
4
5
6
7
8
我们可以这样表达同样的意思:
PREFIX ex: <http://www.example.org/>
PREFIX dbpedia: <http://dbpedia.org/resource/>
PREFIX geo: <http://www.geonames.org/ontology#>.
SELECT ?tournament
WHERE {
?apartment swp:isPartOf/dbpedia-owl:location dbpedia:Amsterdam..
}
2
3
4
5
6
7
8
事实上,当读者写更复杂的 SPARQL 查询时,这些属性路径的快捷表示可能会变得更有用。
# 3. 过滤器
在很多情况下,我们想要找拥有大于或小于一个特定数量的卧室的那些公寓,这样就要用到 FILTER 关键词:
SELECT ?apartment
WHERE {
?apartment swp:hasNumberOfBedrooms ?bedrooms.
FILTER (?bedrooms > 2).
}
2
3
4
5
数值型数据类型和日期/时间都支持比较运算。SPARQL 也支持字符串的过滤。
我们也可能向找到所有在地址中包含“4 Baron Way”的资源,者可以使用 SPARQL 内置支持的正则表达式:
SELECT ?apartment
WHERE {
?apartment swp:address ?address.
FILTER regex(?address, "^4 Baron Way").
}
2
3
4
5
这里用到了特殊的过滤函数名,其参数在随后的括号中给出:
regex
str
:将资源和文字转换成可以在正则中使用的字符串表示。
# 4. 处理一个开放世界的构造子
# 4.1 UNION 和 OPTIONAL
由于开放世界假设,不是每个资源都会以同样的模式(schema)描述,比如下面这个 RDF:
@prefix swp: <http://www.semanticwebprimer.org/ontology/apartments.ttl#>
@prefix dbpedia: <http://dbpedia.org/resource/>.
@prefix dbpedia-owl: <http://dbpedia.org/ontology/>.
@prefix xsd: <http://www.w3.org/2001/XMLSchema#>.
swp:BaronWayApartment swp:hasNumberOfBedrooms 3.
swp:BaronWayApartment dbpedia-owl:location dbpedia:Amsterdam.
swp:BaronWayApartment refs:label "Baron Way Apartment for Rent".
swp:FloridaAveStudio swp:hasNumberOfBedrooms 1.
swp:FloridaAveStudio dbpedia-owl:locationCity dbpedia:Amsterdam.
2
3
4
5
6
7
8
9
10
11
- 在这个例子中,Florida Ave 单间公寓并没有一个对人类友好的标签,并且它的位置以
dbpedia-owl:locationCity
而非dbpedia-owl:location
为谓语来描述。
即便有这种不一致,我们仍然想要在数据上查询并找到位于 Amsterdam 的公寓并返回它们对人类友好的标签一如果有。SPARQL为表述这个查询提供了两种构造子。让我们来看一个样例查询:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX geo:<http://www.geonames.org/ontology#>.
PREFIX dbpedia:<http://dbpedia.org/resource/>.
PREFIX dbpedia-owl:<http://dbpedia.org/ontology/>.
SELECT ?apartment ?label
WHERE {
{?apartment dbpedia-owl:location dbpedia:Amsterdam.}
UNION
{?apartment dbpedia-owl:locationCity dbpedia:Amsterdam.}
OPTIONAL
{?apartment rdfs:label ?label.
}
2
3
4
5
6
7
8
9
10
11
12
13
这个查询结果是:
?apartment | ?label |
---|---|
swp:BaronWayApratment | Baron Way Apartment for Rent |
swp:FloridaAveStudio |
UNION 关键词告诉三元组存储库返回那些仅匹配一个图模式或两个都匹配的结果。
OPTIONAL 关键词告诉三元组存储库为特定的图模式返回结果——如果能找到。即对于待返回的查询而言,这个图模式未必要被满足。因此,在这个例子中,如果没有这个可选项,这间单间公寓就不会在查询结果中返回。
# 4.2 用“|”运算符来简写
类似地,属性路径也可被用来创建一个更简洁的 SPARQL 查询。使用 | 运算符,我们可以表述一个或更多的可能性。因此,上述SPARQL可以被重写成如下形式:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX dbpedia:<http://dbpedia.org/resource/>.
PREFIX dbpedia-owl:<http://dbpedia.org/ontology/>.
SELECT ?apartment ?label
WHERE {
?apartment dbpedia-owl:location|dbpedia-owl:locationCity dbpedia:Amsterdam.}
OPTIONAL
{?apartment rdfs:label ?label.}
}
2
3
4
5
6
7
8
9
10
# 5. 组织结果集
一种常见的情况是,我们想要查询结果以一种特定的方式返回:分组的、计数的或排序的。SPARQL支持一些函数来帮助我们组织结果集。
# 5.1 DISTINCT
DISTINCT 关键词放在选择关键词之后(例如 SELECT DISTINCT ?name WHERE
)来消除结果集中的重复结果。这将确保返回互不相同的变量绑定。
# 5.2 ORDER BY
SPARQL 也允许使用 ORDER BY 关键词来对返回的结果集排序。例如,我们可以要求公寓按卧室数量排序:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX dbpedia:<http://dbpedia.org/resource/>.
PREFIX dbpedia-owl:<http://dbpedia.org/ontology/>.
SELECT ?apartment ?bedrooms
WHERE {
?apartment swp:hasNumberOfBedrooms ?bedrooms.
}
ORDER BY DESC(?bedrooms)
2
3
4
5
6
7
8
9
其返回结果是:
?apartment | ?bedrooms |
---|---|
swp:BaronWayApartment | 3 |
swp:FloridaAveStudio | 1 |
DESC 关键词指明了按降序排列。类似地,ASC 指的是升序。此外,注意字符串或 URL 的排序是根据字典序。
# 5.3 聚集函数
我们也可以使用聚集(aggregate)函数来汇总结果集。特别地,我们可以计数结果的数量(COUNT)、求和(SUM)以及计算最小值、最大值和平均值(MIN、MAX、AVG)。
这是一个计算我们的数据集中平均卧室数量的例子:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>
PREFIX dbpedia:<http://dbpedia.org/resource/>.
PREFIX dbpedia-owl:<http://dbpedia.org/ontology/>.
SELECT (AVG(?bedrooms) AS ?avgNumRooms)
WHERE {
?apartment swp:hasNumberOfBedrooms ?bedrooms.
}
2
3
4
5
6
7
8
它将返回:
?avgNumRooms |
---|
2 |
这个聚集函数与 AS 关键词组合使用,来指明结果集中的变量。并没有限制我们在整个结果集上运用这些聚集。
我们也可以使用 GROUP BY 关键词来聚集特定的分组。
# 6. ASK 和 CONSTRUCT 查询
# 6.1 ASK
ASK 形式的查询简单地检查一个数据集中是否存在一个图模式,而不是去返回结果。
# 6.2 CONSTRUCT
CONSTRUCT 形式的查询用来从一个更大的 RDF 集中检索出一个 RDF 图。因此,可以查询一个三元组存储库并检索一个RDF图而非一组变量绑定。例如,我们可以创建一个新图,将那些拥有超过 2 间卧室的公寓标记为大公寓:
PREFIX ex:<http://www.example.org/>
PREFIX dbpedia:<http://dbpedia.org/resource/>
PREFIX geo:、<http:/www.geonames.org/ontology:#>
CONSTRUCT {?apartment swp:hasNumberOfBedrooms ?bedrooms. ?apartment swp:isBigApartment true.}
WHERE{
?apartment swp:hasNumberOfBedrooms ?bedrooms.
}
FILTER (?bedrooms >2)
2
3
4
5
6
7
8
9
这将返回如下的图:
@prefix swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
@prefix dbpedia:<http://dbpedia.org/resource/>.
@prefix dbpedia-owl:<http://dbpedia.org/ontology/>.
@prefix xsd:<http://www.w3.org/2001/XMLSchema#>.
swp:BaronWayApartment swp:hasNumberOfBedrooms 3.
swp:BaronWayApartment swp:isBigApartment true.
2
3
4
5
6
7
CONSTRUCT 查询经常用来在模式之间转换——通过查询特定的模式,并用目标模式中的属性替换。
# 7. 查询模式
因为模式(schema)信息也是用 RDF 表示的(用本体表示),SPARQL 也可以用来查询关于模式本身的信息,只需要将本体模式也当成普通的 RDF 一样,查询方式与上面相同。
注:在 SPARQL 中,也可以用
a
来作为rdf:type
的缩写,这与 Turtle 一样。
# 8. 更新信息
SPARQL 更新协议引入了一系列新的关键词来支持三元组的插入、加载和删除。
# 8.1 插入和加载
以下插人一个三元组,阐述 Luxury Apartment 是 Apartment 的一个子类。它将这个三元组加人三元组存储库的已有内容之中:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>.
INSERT DATA {
swp:LuxuryApartment rdfs:subClassOf swp:Apartment.
}
2
3
4
5
6
- 注意,数据本身仍是我们之前中就熟悉的 Turtle 语法。
如果你在万维网上有一个大的包含 RDF 的文件,你可以使用以下命令将它加载进一个三元组存储库:
LOAD <http://example.com/apartment.rdf>
# 8.2 删除
删除三元组从一个三元组存储库中删除三元组有几种方式。
一种是使用 DELETE DATA 关键词准确指定哪些三元组是你想要删除的。将之前插入的三元组删除可以这样:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>.
DELETE DATA {
swp:LuxuryApartment rdfs:subClassOf swp:Apartment.
}
2
3
4
5
6
- 注意,在这种形式下是不允许变量的,所有三元组都必须被完整指定。
一种更加灵活的方式是使用 DELETE WHERE 构造子,它可以删除匹配指定图模式的那些三元组。以下将要删除包含关于拥有超过 2 间卧室的公寓的信息的所有三元组:
PREFIX swp:<http://www.semanticwebprimer.org/ontology/apartments.ttl#>.
PREFIX rdfs:<http://www.w3.org/2000/01/rdf-schema#>.
DELETE
WHERE {
?apartment swp:hasNumberOfBedrooms ?bedrooms.
FILTER(?bedrooms >2)
}
2
3
4
5
6
7
8
在这两种形式中,如果模式未能匹配或者三元组不在三元组存储库中,那么什么都不会发生。
最后,要删除一个三元组存储库中的所有内容,可以如下使用CLEAR构造子:
CLEAR ALL
SPARQL 更新提供了更多的构造子来管理部分三元组存储库。如果向一个三元组存储库中逐步添加数据,那么更新操作就特别有用了。