问题描述
我将 SPARQL 用于 RDF,但在提出一个查询时遇到了问题,该查询将允许我选择树上的一组顶点,并将所有连接(直接和传递)顶点与所选顶点返回,并且按照它们在依赖树上存在的深度顺序(最深的顶点在前)。
这是我正在使用的图形(树)的可视化表示,其中包含 RDF 类型 :dependency
的节点。在这棵树中,依赖项具有到它们所需的每个依赖项的传出边,这在图上使用 :requires
边表示。
这是创建此图的数据:
INSERT DATA {
:A rdf:type :dependency .
:B rdf:type :dependency .
:C rdf:type :dependency .
:D rdf:type :dependency .
:E rdf:type :dependency .
:F rdf:type :dependency .
:G rdf:type :dependency .
:H rdf:type :dependency .
:I rdf:type :dependency .
:J rdf:type :dependency .
:K rdf:type :dependency .
:A :requires :B .
:A :requires :C .
:A :requires :I .
:B :requires :J .
:B :requires :E .
:C :requires :D .
:E :requires :F .
:E :requires :G .
:D :requires :G .
:H :requires :I .
:H :requires :K .
}
我想形成一个查询,允许我选择一组依赖项,并按照首先返回最低的 :required
依赖项的顺序返回该选择的所有依赖项,这样如果“依赖项X" :requires "Dependency Y",那么Dependency X必须在结果集中afterDependency Y。
本质上,我想问图表:
“给我这组顶点需要的所有顶点,并按顺序返回它们,以便最低级别的依赖项在结果集上优先”
例如,在上图中指定这个选择的情况下:[B,H]
一个有效的结果集是:
[G,F,E,J,B,I,K,H]
(因为G
和F
是B
的最低依赖,其次是E
,J
等等...)
以下结果集也将被视为有效:
[J,G,H]
因为在 J
、F
和 G
之前或之后返回 H
并不重要,因为它不依赖于它们(重要的是它在 B
之前返回,因为 B
需要所有这些)
到目前为止,我已经尝试了对选择 [B,H]
的这个查询,它能够返回 B
和 H
的所有必需的依赖项,但它们没有按顺序返回最低依赖优先级。
SELECT ?requires {
?dependency a :dependency .
FILTER (?dependency IN (:B,:H))
?dependency :requires+ ?requires .
}
以错误的顺序返回结果集:
[E,K]
有人知道我如何构造一个查询,该查询基本上执行顶点的深度优先搜索排序吗?
解决方法
感谢@UninformedUser 分享的 this answer,我可以获得与我要查找的内容足够接近的结果集。基本上我们可以使用属性路径来确定所有 :requires 位于一组指定顶点之下的边,同时保留一个计数器来返回每个边的深度:
如果我们想确定这个选择 [B,H]
的顺序,那么我们可以使用这个查询,它是上面答案中查询的稍微修改版本,以允许选择开始顶点从顶部选择所有路径:
select ?begin ?midI ?midJ (count(?counter) as ?position) ?end where {
?begin a :dependency .
FILTER (?begin IN (:B,:H))
?begin :requires* ?counter .
?counter :requires* ?midI .
?midI :requires ?midJ .
?midJ :requires* ?end .
FILTER NOT EXISTS { ?end :requires [] }
}
group by ?begin ?end ?midI ?midJ
order by DESC(?position) ?begin ?end
这将返回以下结果集:
----------------------------------------
| begin | midI | midJ | position | end |
========================================
| :B | :E | :F | 2 | :F |
| :B | :E | :G | 2 | :G |
| :B | :B | :E | 1 | :F |
| :B | :B | :E | 1 | :G |
| :B | :B | :J | 1 | :J |
| :H | :H | :I | 1 | :I |
| :H | :H | :K | 1 | :K |
----------------------------------------
这足以让我在客户端使用代码执行剩余的排序,其中对于 begin
列中的每个唯一值,收集 midJ
的所有值(从顶部开始到底部),直到用完相同 begin
值的所有行,然后最后将该值包含在 begin
列中。
对上述结果集执行此操作时,我们得到了有效答案:
[F,G,E,B,J,I,K,H]