[英]Why does this federated SPARQL query work in TopBraid but not in Apache Fuseki?
我有以下联邦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,但未分配?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的行为类似。 与在FILTER regex函数中使用三元模式的String变量(?actorName)(在Fuseki中不起作用)相比,这似乎与在两个三元模式(在Fuseki中工作)中使用URI变量(?dbpediaLink)有关。 )。
在我写的原始答案(如下)中,我说过问题是SPARQL查询首先在最里面执行。 我认为这在这里仍然适用,但是我认为可以更轻松地隔离问题。 如果你有
service <ex1> { ... }
service <ex2> { ... }
那么结果必须是您在端点上分别执行每个查询然后合并结果所得到的结果。 联接将合并公共变量具有相同值的所有结果。 例如,
service <ex1> { values ?a { 1 2 3 } }
service <ex2> { values ?a { 2 3 4 } }
将执行,并且在外部查询(2和3)中将有两个可能的?a值。 在您的查询中,第二项服务无法产生任何结果。 如果您采取:
?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 } }
}
第一个子查询将查找所有猫。 第二个子查询查找具有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的绑定,该怎么办? 您肯定不希望为每个服务块执行第二个服务块吗? 取而代之的是,当执行第二服务块作为写入,并且将返回将其结果从所述第一组被接合的结果集。
它在耶拿(Jena)中可能“不起作用”的原因是,第二个查询实际上没有绑定任何变量,因此几乎必须查看数据中的每个三元组 ,这显然将花费很长时间。
我认为您可以通过嵌套服务调用来解决此问题。 如果嵌套服务全部由“本地”端点启动(即,嵌套服务调用不会要求远程端点进行另一个远程查询),那么您可以执行以下操作:
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, " ", "_"))) .
}
}
这是相同的查询,但没有SERVICE调用。 ?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版本强调了这一点,因为各部分分别在不同的机器上执行。
这两个部分的结合发生在每个部分的结果上。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.