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

Posted

技术标签:

【中文标题】如何递归扩展 SPARQL 构造查询中的空白节点?【英文标题】:How to recursively expand blank nodes in SPARQL construct query? 【发布时间】:2012-04-04 12:59:31 【问题描述】:

这个问题可能很容易回答,但我什至不知道如何制定 Google 查询来找到它。

我正在针对包含空白节点的数据集编写 SPARQL 构造查询。因此,如果我进行类似的查询

CONSTRUCT ?x ?y ?z . WHERE ?x ?y ?z .

那么我的结果之一可能是:

nm:John nm:owns _:Node

如果所有的都是问题

_:Node nm:has nm:Hats

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

有没有办法编写我的原始 CONSTRUCT 查询以递归地添加附加到任何 bnode 结果的所有三元组,这样在我的新图中没有 bnode 悬空?

【问题讨论】:

如果您这样做的方式可以运行 CONSTRUCT 查询,然后针对该查询运行 SPARQL 查询,您实际上 可以 获得简明的有界描述 ( CBD)使用 SPARQL 查询。看看Sparql query to return all triples recursively that make up an rdfs:class definition(也许还有Implementing Concise Bounded Description in SPARQL)。 上一条评论中答案背后的想法是,您可以创建一个新属性,它可以有效地充当属性路径中的谓词。例如,对于每个 IRI 节点 i,您添加一个三元组“i selfIRI i”。然后,当您编写类似“?x p/selfIRI ?y”这样的路径时,您已确保 ?y 是一个 IRI 节点。 【参考方案1】:

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

您可以删除带有尾随 bnode 的语句:

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 可能会更好,因为它通常会跳过 bnode。

【讨论】:

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

正如 user205512 所建议的那样,递归地执行该抓取是不可能的,并且正如他们所指出的那样,使用 optional(s) 将任意级别深入到您的数据中获取节点是不可行的,但在非平凡大小的数据库上是不可行的。

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

这并不能帮助您向下递归并获取数据,但为此,我不建议您进行此类一般查询;它们不能很好地扩展,并且通常返回比您想要或需要的更多。我建议你做更多的定向查询。您最初的构造查询将拉下整个数据库的内容,这通常不是您想要的。

最后,虽然 describe 很有用,但没有标准的实现; SPARQL 规范没有定义任何特定的行为,因此它返回的内容留给数据库供应商,并且可能有所不同。如果您计划在您的应用程序中尝试不同的数据库,这可能会降低您的代码的可移植性。如果你想要一个无法描述的特定行为,你最好自己实现它。为资源做简洁的有界描述之类的事情是一段简单的代码,尽管您可能会在 Bnodes 周围遇到一些令人头疼的问题。

【讨论】:

感谢迈克尔的回答。我的示例查询有点不精确......我实际上正在尝试做的是在我下载的数据集中提取有关给定实体的所有信息。但该数据集包含诸如“John 是 _:1234 的创建者”之类的条目。所以我想我的替代方案是使用两级查询并希望它不会破坏性能,或者只是重组数据库以命名所有 bnode。 SPARQL 规范似乎可以对此使用更强大的支持,因为这似乎不是一个特别不常见的问题。【参考方案3】:

关于使用 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

【讨论】:

以上是关于如何递归扩展 SPARQL 构造查询中的空白节点?的主要内容,如果未能解决你的问题,请参考以下文章

知识工程基础-作业1

知识图谱学习与实践——通过例句介绍Sparql的使用

如何迭代引用虚拟图的 SPARQL 查询?

SPARQL 查询返回邻居数

如何在递归 SQL 查询中查找子树中的所有节点?

为啥这个 SPARQL 查询超时以及如何优化这个查询?