2

我有以下联合 SPARQL 查询,它在 TopBraid Composer 免费版(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 的行为类似。与在 FILTER 正则表达式函数中使用三重模式中的字符串变量 (?actorName ) 相比,它似乎与在两个三重模式中使用URI变量 (?dbpediaLink)(在 Fuseki 中有效)有关(在 Fuseki 中无效) )。

4

2 回答 2

3

更新(更简单)的响应

在我写的原始答案(如下)中,我说问题是 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 有一些有效的方法来根据计算replace确定要检索哪些三元组。您要求 DBpedia 查看其所有三元组,然后保留主题的字符串形式与特定正则表达式匹配的三元组。在子查询中手动构建 IRI 然后搜索它可能会更好。IE,

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 
}
于 2016-07-12T14:31:06.857 回答
2

(长评论)

考虑:

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 版本强调了这一点,因为这些部分是在不同的机器上单独执行的。

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

于 2016-07-12T20:26:23.853 回答