问题
I'm trying to return subjects based on the relative position of their subjects in an ordered list.
A subject can be associated with multiple objects (via a single predicate) and all objects are in an ordered list. Given a reference object in this list I'd like to return the subjects in order of relative distance of their objects from the reference object.
:a : :x
:b : :v
:b : :z
:c : :v
:c : :y
:ls :list (:v :w :x :y :z)
Taking x as our starting object in the list, the code below returns
:a :x :0
:c :y :1
:b :v :2
:b :z :2
:c :v :2
Instead of returning all positions I would like only the objects relating to the subject's minimum object 'distance' to be returned (which may mean up to two objects per subject - both up and down the list). So I'd like to return
:a :x :0
:c :y :1
:b :v :2
:b :z :2
The code so far... (with a lot of help from Find lists containing ALL values in a set? and Is it possible to get the position of an element in an RDF Collection in SPARQL?)
SELECT ?s ?p (abs(?refPos-?pos) as ?dif)
WHERE {
:ls :list/rdf:rest*/rdf:first ?o .
?s : ?o .
{
SELECT ?o (count(?mid) as ?pos) ?refPos
WHERE {
[] :list/rdf:rest* ?mid . ?mid rdf:rest* ?node .
?node rdf:first ?o .
{
SELECT ?o (count(?mid2) as ?refPos)
WHERE {
[] :list/rdf:rest* ?mid2 . ?mid2 rdf:rest* ?node2 .
?node2 rdf:first :x .
}
}
}
GROUP BY ?o
}
}
GROUP BY ?s ?o
ORDER BY ?dif
I've been trying to get a minimum ?dif (difference/distance) by grouping by ?s but because I then have to apply this (something like ?dif = ?minDif) to the ?s ?o grouping from earlier I don't know how to go back and forward between these two groupings.
Thanks for any assistance you can provide
回答1:
All you needed to compound a solution is yet another one Joshua Taylor's answer: this or this.
Here below I'm using Jena functions, but I hope the idea is clear.
Query 1
PREFIX list: <http://jena.hpl.hp.com/ARQ/list#>
SELECT ?s ?el ?dif {
?s : ?el .
:ls :list/list:index (?pos ?el) .
:ls :list/list:index (?ref :x) .
BIND (ABS(?pos -?ref) AS ?dif)
{
SELECT ?s (MIN (?dif_) AS ?dif) WHERE {
?s : ?el_ .
:ls :list/list:index (?pos_ ?el_) .
:ls :list/list:index (?ref_ :x) .
BIND (ABS(?pos_ - ?ref_) AS ?dif_)
} GROUP by ?s
}
}
Query 2
PREFIX list: <http://jena.apache.org/ARQ/list#>
SELECT ?s ?el ?dif {
?s : ?el .
:ls :list/list:index (?pos ?el) .
:ls :list/list:index (?ref :x) .
BIND (ABS(?pos -?ref) AS ?dif)
FILTER NOT EXISTS {
?s : ?el_ .
:ls :list/list:index (?pos_ ?el_) .
BIND (ABS(?pos_ - ?ref) AS ?dif_) .
FILTER(?dif_ < ?dif)
}
}
Update
Query 1 can be rewritten in this way:
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
SELECT ?s ?el ?dif {
?s : ?el
{ select (count(*) as ?pos) ?el {[] :list/rdf:rest*/rdf:rest*/rdf:first ?el} group by ?el }
{ select (count(*) as ?ref) {[] :list/rdf:rest*/rdf:rest*/rdf:first :x} }
BIND (ABS(?pos - ?ref) AS ?dif)
{
SELECT ?s (MIN(?dif_) AS ?diff) {
?s : ?el_
{ select (count(*) as ?pos_) ?el_ {[] :list/rdf:rest*/rdf:rest*/rdf:first ?el_} group by ?el_ }
{ select (count(*) as ?ref_) {[] :list/rdf:rest*/rdf:rest*/rdf:first :x} }
BIND (ABS(?pos_ - ?ref_) AS ?dif_)
} GROUP by ?s
}
FILTER (?dif = ?diff)
}
Notes
- As you can see, this is not what SPARQL was designed for. For example, Blazegraph supports Gremlin...
- Possibly this is not what RDF was designed for. Or try other modeling approach: do you really need RDF lists?
- I haven't tested the above query in Virtuoso.
来源:https://stackoverflow.com/questions/50014338/finding-the-relative-position-of-elements-in-a-list-using-sparql