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分钟),一个演员名单(克拉克·盖博、费雯·丽……),等等。
电影是一个普遍的概念。电影可以有导演、时长和演员阵容,但“电影”本身并没有特定的导演、时长或演员阵容。虽然电影是一件艺术作品,艺术作品通常有创作者,但“电影”本身没有创作者——只有这个概念的特定实例有。
这种差异就是维基数据中存在两个“是”属性的原因:P31
和P279
。《乱世佳人》是“电影”类的一个特定实例;“电影”类是更一般类“艺术作品”的子类(更具体的类;专门化)。
那么,当我们编写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在搜索时要考虑到这一点。
对此的一个可能的解决方案是我们讨论过的[]
语法:《乱世佳人》是“艺术作品”的某个子类的实例。(作为练习,尝试编写这个查询!)但这仍然存在问题
- 我们不再包含直接属于艺术作品的项目。
- 我们仍然错过了“艺术作品”的某个子类的某个其他子类的实例——例如,《白雪公主和七个小矮人》是一部动画电影,动画电影是一部电影,电影是一件艺术作品。在这种情况下,我们需要遵循两个“子类”语句——但它也可能是三个、四个、五个,实际上可能是任何数量。
解决方案:?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。