跳转到内容

SPARQL/属性路径

来自维基教科书,开放世界中的开放书籍

属性路径

[编辑 | 编辑源代码]

三元组存储中的语句在三元组中具有特定的属性。在SPARQL查询中,您也可以在三元组中写下属性路径。

属性路径是用来描述两个项目之间属性路径的简写。最简单的路径只是一个单独的属性,它形成一个普通的三元组。

?item wdt:P31 ?class.

您可以使用正斜杠(/)添加路径元素。

?item wdt:P31/wdt:P279/wdt:P279 ?class.

这等效于以下任一内容

?item wdt:P31 ?temp1.
?temp1 wdt:P279 ?temp2.
?temp2 wdt:P279 ?class.
?item wdt:P31 [ wdt:P279 [ wdt:P279 ?class ] ].

练习:(重新)编写“巴赫的孙子”查询以使用此语法。

路径元素后面的星号(*)表示“零个或多个此元素”。

?item wdt:P31/wdt:P279* ?class.
# means:
?item wdt:P31 ?class
# or
?item wdt:P31/wdt:P279 ?class
# or
?item wdt:P31/wdt:P279/wdt:P279 ?class
# or
?item wdt:P31/wdt:P279/wdt:P279/wdt:P279 ?class
# or ...

如果路径中没有其他元素,则?a something* ?b表示?b也可能只是?a,它们之间根本没有路径元素。

加号(+)类似于星号,但表示“一个或多个此元素”。以下查询找到巴赫的所有后代

SELECT ?descendant ?descendantLabel
WHERE
{
  wd:Q1339 wdt:P40+ ?descendant.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

如果我们在这里使用星号而不是加号,查询结果将包括巴赫本人。

问号(?)类似于星号或加号,但表示“零个或一个此元素”。

您可以使用竖线(|)而不是正斜杠来分隔路径元素;这意味着“或”:路径可能使用这两个属性中的任何一个。(但不是两者都使用 - “或”路径段始终匹配长度为一的路径。)

您也可以使用括号(())将路径元素分组,并自由组合所有这些语法元素(/|*+?)。这意味着,找到巴赫所有后代的另一种方法是

SELECT ?descendant ?descendantLabel
WHERE
{
  ?descendant (wdt:P22|wdt:P25)+ wd:Q1339.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

我们不是使用“孩子”属性从巴赫到他的后代,而是使用“父亲”和“母亲”属性从后代到巴赫。路径可能包括两个母亲和一个父亲,或者四个父亲,或者父亲-母亲-母亲-父亲,或者任何其他组合。(当然,巴赫不可能是某人的母亲,所以最后一个元素总是父亲。)

路径元素后面的代码总结

代码 含义
? (问号) 零个或一个此元素
* (星号) 零个或多个此元素
+ (加号) 一个或多个此元素
[编辑 | 编辑源代码]

除了正常的三元组“主体、谓语、宾语”之外,还可以将其写成逆向链接“宾语、谓语、主体”。这可以通过在谓语前面添加^来实现。对于普通三元组来说,这并不是很有用,但对于属性路径来说,它可以避免使用虚拟变量。

例如,此查询通过查询具有相同父亲的兄弟姐妹来找到约翰·塞巴斯蒂安·巴赫的兄弟姐妹。

SELECT ?sibling ?siblingLabel
WHERE
{
  # Bach   father/has father sibling
  wd:Q1339 wdt:P22/^wdt:P22 ?sibling. # ^ = Inverse link
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

使用虚拟变量,可以写成

SELECT ?sibling ?siblingLabel
WHERE
{
  # Bach   father/has father sibling
  wd:Q1339 wdt:P22 ?dummy.
  ?dummy ^wdt:P22 ?sibling. # ^ = Inverse link
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

或者没有逆向链接

SELECT ?sibling ?siblingLabel
WHERE
{
  # Bach   father/has father sibling
  wd:Q1339 wdt:P22 ?dummy.
  ?sibling wdt:P22 ?dummy.
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

代码 含义
^ (插入符) 逆向链接

实例和类

[编辑 | 编辑源代码]

大多数维基数据属性是“有”关系:孩子,父亲,职业。但有时(实际上,经常),您还需要谈论某事物是什么。但实际上,那里存在两种关系

  • 《乱世佳人》一部电影。
  • 一部电影一件艺术作品。

《乱世佳人》是一部特定的电影。它有一个特定的导演(维克多·弗莱明),一个特定的时长(238分钟),一个演员名单(克拉克·盖博、费雯·丽……),等等。

电影是一个普遍的概念。电影可以有导演、时长和演员阵容,但“电影”本身并没有特定的导演、时长或演员阵容。虽然电影一件艺术作品,艺术作品通常有创作者,但“电影”本身没有创作者——只有这个概念的特定实例有。

这种差异就是维基数据中存在两个“是”属性的原因:P31P279《乱世佳人》是“电影”类的一个特定实例;“电影”类是更一般类“艺术作品”的子类(更具体的类;专门化)。

那么,当我们编写SPARQL查询时,这意味着什么呢?当我们要搜索“所有艺术作品”时,仅仅搜索所有直接属于“艺术作品”的项目是不够的

SELECT ?work ?workLabel
WHERE
{
  ?work wdt:P31 wd:Q838948. # instance of work of art
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}

试试看!

当我写这篇文章的时候,这个查询只返回了2815个结果——很明显,艺术作品不止这些!问题是,这错过了像《乱世佳人》这样的项目,它仅仅是“电影”的实例,而不是“艺术作品”的实例。“电影”是“艺术作品”的子类,但我们需要告诉SPARQL在搜索时要考虑到这一点。

对此的一个可能的解决方案是我们讨论过的[]语法:《乱世佳人》是“艺术作品”的某个子类的实例。(作为练习,尝试编写这个查询!)但这仍然存在问题

  1. 我们不再包含直接属于艺术作品的项目。
  2. 我们仍然错过了“艺术作品”的某个子类的某个其他子类的实例——例如,《白雪公主和七个小矮人》是一部动画电影,动画电影是一部电影,电影是一件艺术作品。在这种情况下,我们需要遵循两个“子类”语句——但它也可能是三个、四个、五个,实际上可能是任何数量。

解决方案:?item wdt:P31/wdt:P279* ?class。这意味着,在项目和类之间有一个“实例”和任何数量的“子类”语句。

SELECT ?work ?workLabel
WHERE
{
  ?work wdt:P31/wdt:P279* wd:Q838948. # instance of any subclass of work of art
  SERVICE wikibase:label { bd:serviceParam wikibase:language "en". }
}
LIMIT 1000

试试看!

我不建议对所有艺术作品运行这个查询。WDQS 可以处理它(勉强),但您的浏览器在尝试显示结果时可能会崩溃,因为结果太多了。出于这个原因,插入了LIMIT 1000

现在您知道如何搜索所有艺术作品、所有建筑或所有人类住区:神奇咒语wdt:P31/wdt:P279*,以及相应的类。这使用了更多我尚未解释过的SPARQL功能,但坦率地说,这几乎是这些功能的唯一相关用途,因此您不需要了解它是如何工作的,就可以有效地使用WDQS。

参考文献

[编辑 | 编辑源代码]


华夏公益教科书