我有以下联合SPARQL查询,它在TopBraid Composer Free Edition(版本5.1.4)中按预期工作,但在Apache Fuseki(版本2.3.1)中不起作用:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX movie: <http://data.linkedmdb.org/resource/movie/>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?s WHERE {
SERVICE <http://data.linkedmdb.org/sparql> {
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
SERVICE <http://dbpedia.org/sparql?timeout=30000> {
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
}
}
我监视在后台执行的子SPARQL查询,并注意到TopBraid正确地对 http://dbpedia.org/sparql 端点执行了以下查询:
SELECT *
WHERE
{ ?s ?p ?o
FILTER regex(str(?s), replace("Paul Reubens", " ", "_"))
}
而 Apache Fuseki 执行以下子查询:
SELECT *
WHERE
{ ?s ?p ?o
FILTER regex(str(?s), replace(?actorName, " ", "_"))
}
注意差异;TopBraid 将变量 "actorName"替换为特定值"Paul Reubens",而 Apache Fuseki 则不会。这会导致来自 http://dbpedia.org/sparql 终结点的错误,因为 ?actorName 在结果集中使用,但未分配。
这是Apache Fuseki中的错误还是TopBraid中的功能?如何使 Apache Fuseki 正确执行此联合查询。
更新1:进一步澄清TopBraid和Apache Fuseki之间的行为差异。TopBraid 首先执行 linkedmdb.org 子查询,然后对 linkedmdb.org 查询的每个结果执行 dbpedia.org 子查询)(并将 ?actorName 替换为 linkedmdb.org 查询的结果)。我假设 Apache Fuseki 的行为类似,但要 dbpedia.org 的第一个子查询失败(因为 ?actorName 在结果集中使用但未分配),因此它不会继续。但是现在我不确定它是否真的想多次执行子查询来 dbpedia.org,因为它永远不会到达那里。
更新2:我认为TopBraid和Apache Fuseki都使用Jena/ARQ,但我注意到在TopBraid的堆栈跟踪中,软件包名称类似于com.topbraid.jena.*,这可能表明他们使用Jena/ARQ的修改版本?
更新3:约书亚·泰勒(Joshua Taylor)在下面说:"你肯定不希望为每个人执行第二个服务块吗?TopBraid 和 Apache Fuseki 都使用此方法进行以下查询:
PREFIX owl: <http://www.w3.org/2002/07/owl#>
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX movie: <http://data.linkedmdb.org/resource/movie/>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?film ?label ?subject WHERE {
SERVICE <http://data.linkedmdb.org/sparql> {
?film a movie:film .
?film rdfs:label ?label .
?film owl:sameAs ?dbpediaLink
FILTER(regex(str(?dbpediaLink), "dbpedia", "i"))
}
SERVICE <http://dbpedia.org/sparql> {
?dbpediaLink dcterms:subject ?subject
}
}
LIMIT 50
但我同意原则上他们应该执行一次这两个部分并加入它们,但也许出于性能原因,他们选择了不同的策略?
另外,请注意上述查询在 Apache Fuseki 上的工作原理,而本文的第一个查询则没有。因此,Apache Fuseki在这种特殊情况下的行为实际上与TopBraid相似。它似乎与在两种三重模式中使用 URI 变量 (?dbpediaLink) 有关(这在 Fuseki 中有效),而不是在 FILTER 正则表达式函数中使用来自三重模式的字符串变量 (?actorName)(在 Fuseki 中不起作用)。
更新(更简单)响应
在我写的原始答案(下面)中,我说问题是SPARQL查询首先在最里面执行。 我认为这仍然适用于这里,但我认为这个问题可以更容易地孤立出来。 如果你有
service <ex1> { ... }
service <ex2> { ... }
然后,结果必须是在端点上单独执行每个查询然后联接结果所获得的结果。 联接将合并公共变量具有相同值的任何结果。 例如,
service <ex1> { values ?a { 1 2 3 } }
service <ex2> { values ?a { 2 3 4 } }
将执行,并且外部查询中有两个可能的 ?a 值(2 和 3)。 在查询中,第二个服务无法生成任何结果。 如果您采取:
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
并在 DBpedia 上执行它,你不应该得到任何结果,因为 ?actorName 没有绑定,所以过滤器永远不会成功。 TopBraid 似乎首先执行第一个服务,然后将结果值注入到第二个服务中。 这很方便,但我认为这是不正确的,因为它返回的结果与首先执行 DBpedia 查询而第二个查询执行时得到的结果不同。
原始答案
SPARQL 中的子查询首先在最里面执行。 这意味着像这样的查询
select * {
{ select ?x { ?x a :Cat } }
?x foaf:name ?name
}
会先找到所有的猫,然后找到它们的名字。 ?x 的"候选"值首先由子查询确定,然后 ?x 的这些值可供外部查询使用。 现在,当有两个子查询时,例如,
select * {
{ select ?x { ?x a :Cat } }
{ select ?x ?name { ?x foaf:name ?name } }
}
第一个子查询将找到所有的猫。 第二个子查询查找具有名称的所有内容的所有名称,然后在外部查询中,连接结果以仅获取猫的名称。 第一个子查询中的 ?x 值在执行第二个子查询期间不可用。 (至少在原则上,查询优化器可能能够确定某些事情应该受到限制。
我的理解是服务块具有相同的语义。 在查询中,您有:
SERVICE <http://data.linkedmdb.org/sparql> {
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
SERVICE <http://dbpedia.org/sparql?timeout=30000> {
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
}
你说跟踪显示 TopBraid 正在执行
SELECT *
WHERE
{ ?s ?p ?o
FILTER regex(str(?s), replace("Paul Reubens", " ", "_"))
}
如果 TopBraid 已经执行了第一个服务块并获得了唯一的解决方案,那么这可能是一个可以接受的优化,但是,例如,如果第一个查询为 ?actorName 返回了多个绑定呢? 您肯定不希望为每个服务块执行第二个服务块吗? 相反,第二个服务块按写入方式执行,并将返回一个结果集,该结果集将与第一个服务块的结果集联接。
它在耶拿可能"不起作用"的原因是因为第二个查询实际上并没有绑定任何变量,所以它几乎必须查看数据中的每个三元组,这显然需要很长时间。
我认为您可以通过嵌套服务调用来解决此问题。 如果嵌套服务全部由"本地"端点启动(即,嵌套服务调用不会要求远程端点进行另一个远程查询),那么您可以执行以下操作:
SERVICE <http://dbpedia.org/sparql?timeout=30000> {
SERVICE <http://data.linkedmdb.org/sparql> {
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
}
这可能会让你得到你想要的那种优化,但这似乎仍然可能不起作用,除非 DBpedia 有一些有效的方法来根据计算替换来确定要检索的三元组。 您要求 DBpedia 查看其所有三元组,然后保留主题的字符串形式与特定正则表达式匹配的三元组。 最好在子查询中手动构造该 IRI,然后搜索它。 即,
SERVICE <http://dbpedia.org/sparql?timeout=30000> {
{ select ?actor {
SERVICE <http://data.linkedmdb.org/sparql> {
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
bind(iri(concat("http://dbpedia.org/resource",
replace(?actorName," ","_")))
as ?actor)
} }
?actor ?p ?o
}
(长评论)
考虑:
PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>
PREFIX movie: <http://data.linkedmdb.org/resource/movie/>
PREFIX dcterms: <http://purl.org/dc/terms/>
SELECT ?s WHERE {
{
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
{
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
}
}
这是相同的查询,但没有服务调用。 ?actorName
不是内部第二{}
的模式。
由于 join 是一种交换操作,因此它与第一个查询具有相同的答案。
SELECT ?s WHERE {
{
?s ?p ?o .
FILTER(regex(str(?s), replace(?actorName, " ", "_"))) .
}
{
<http://data.linkedmdb.org/resource/film/1> movie:actor ?actor .
?actor movie:actor_name ?actorName .
}
}
SERVICE 版本突出显示了这一点,因为这些部件在不同的计算机上单独执行。
两个部分的连接发生在每个部分的结果上。