问题 如何在SPARQL构造查询中递归扩展空白节点?


可能很容易回答这个问题,但我甚至无法弄清楚如何制定Google查询来找到它。

我正在针对包含空节点的数据集编写SPARQL构造查询。所以,如果我做一个像这样的查询

CONSTRUCT {?x ?y ?z .} WHERE {?x ?y ?z .}

然后我的一个结果可能是:

nm:John nm:owns _:Node

如果全部的话,这是一个问题

_:Node nm:has nm:Hats

三元组也不会以某种方式进入查询结果(因为我使用的一些解析器就像rdflib for Python真的不喜欢悬空的bnodes)。

有没有办法编写我的原始CONSTRUCT查询来递归添加附加到任何bnode结果的所有三元组,以便在我的新图形中没有留下bnodes?


8471
2018-03-20 01:48


起源

如果您正在以可以运行CONSTRUCT查询然后针对该查询运行SPARQL查询的方式执行此操作,那么实际上 能够 使用SPARQL查询获得简洁有界的描述(CBD)。看一下 Sparql查询以递归方式返回构成rdfs:class定义的所有三元组 (有可能 在SPARQL中实现简明有界描述)。 - Joshua Taylor
之前评论中答案背后的想法是你可以创建一个 新房产 它有效地充当属性路径中的谓词。例如,对于每个IRI节点i,您添加三个“i selfIRI i”。然后当你写一个像“?x p / selfIRI?y”这样的路径时,你确保?y是一个IRI节点。 - Joshua Taylor


答案:


递归是不可能的。我能想到的最接近的是 SPARQL 1.1属性路径 (注意:该版本已过期)但是bnode测试不可用(afaik)。

你可以删除带有尾随bnodes的语句:

CONSTRUCT {?x ?y ?z .} WHERE 
{
  ?x ?y ?z .
  FILTER (!isBlank(?z))
}

或试试你的运气下一点:

CONSTRUCT {?x ?y ?z . ?z ?w ?v } WHERE 
{
  ?x ?y ?z .
  OPTIONAL {
    ?z ?w ?v
    FILTER (isBlank(?z) && !isBlank(?v))
  }
}

(最后一个查询非常惩罚,顺便说一下)

你可能会更好 DESCRIBE,这将经常跳过bnodes。


10
2018-03-20 10:19



谢谢用户。我目前的计划是使用两级查询而不必担心递归更深入。 isBlank过滤器可能会有所帮助,但过滤器似乎真的会扼杀性能,因为SPARQL似乎在逐行执行过滤器之前实现了整个预过滤器子图。因此,除非未经过滤的子图很小,否则过滤查询会变得非常密集。 - rogueleaderr
你不能说“SPARQL正在实现整个预过滤器子图...”:不同的SPARQL引擎实现将有不同的算法,具有不同的优点和缺点。它甚至会根据您使用的库的版本而有所不同。 - Ian Dickinson


正如user205512建议的那样,递归执行抓取是不可能的,并且正如他们所指出的那样,使用可选(s)将任意级别下移到您的数据中,获得节点对于除了非平凡大小的数据库之外的任何东西都是不可行的。

Bnode本身是本地作用域,结果集或文件。无法保证您从解析或结果集获得的BNode与数据库中使用的ID相同(尽管某些数据库确保对查询结果保证这一点)。此外,像“select?s {{s?p _:bnodeid1}”这样的查询与“select?where {?s?p?o}”相同 - 请注意,在这种情况下,bnode被视为变量,而不是“具有id'bnodeid1'的东西”这个设计的怪癖使得查询bnodes很困难,所以如果你控制数据,我建议你不要使用它们。为那些本来就是bnodes的东西生成名称并不难,而命名资源v.bnodes在查询期间不会增加开销。

这并没有帮助你递减并抓取数据,但为此,我不建议做这样的一般查询;它们不能很好地扩展,通常返回的比你想要的还要多。我建议你做更多的定向查询。您的原始构造查询将下拉整个数据库的内容,这通常不是您想要的。

最后,虽然描述可能有用,但没有标准的实现; SPARQL规范没有定义任何特定的行为,因此返回的内容留给数据库供应商,它可以是不同的。如果您计划在应用程序中尝试不同的数据库,那么这会使您的代码变得不那么便携如果你想要描述一个特定的行为,你最好自己实现它。对资源进行简洁有界的描述之类的操作是一段简单的代码,尽管你可能会遇到一些关于Bnodes的问题。


3
2018-03-20 15:32



谢谢Michael的回答。我的示例查询有点不精确......我正在尝试实际做的是提取有关我下载的数据集中给定实体的所有信息。但数据集包括“约翰是_的创建者:1234”等条目。因此,我认为我的替代方案是使用两级查询并希望它不会破坏性能,或者只是重构数据库以命名所有bnode。看起来SPARQL规范似乎可以使用更强大的支持,因为这似乎不是一个特别罕见的问题。 - rogueleaderr


关于使用ruby RDF.rb库,它允许在RDF :: Graph对象上使用重要的方便方法进行SPARQL查询,以下内容应该扩展空白节点。

rdf_type = RDF::SCHEMA.Person # for example
rdf.query([nil, RDF.type, rdf_type]).each_subject do |subject|
  g = RDF::Graph.new
  rdf.query([subject, nil, nil]) do |s,p,o|
    g << [s,p,o]
    g << rdf_expand_blank_nodes(o) if o.node?
  end
end

def rdf_expand_blank_nodes(object)
  g = RDF::Graph.new
  if object.node?
    rdf.query([object, nil, nil]) do |s,p,o|
      g << [s,p,o]
      g << rdf_expand_blank_nodes(o) if o.node?
    end
  end
  g
end

1
2018-02-21 03:39