为什么这个联合的SPARQL查询在TopBraid中有效,而在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 在结果集中使用,但未分配。

这是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 版本突出显示了这一点,因为这些部件在不同的计算机上单独执行。

两个部分的连接发生在每个部分的结果上。

相关内容

  • 没有找到相关文章

最新更新