This query generates an RDF list from an RDF graph that is a linear path. To set up initial data:
stardog query test "insert data { :a :p :b . :b :p :c . :c :p :d }"
The list generation query:
construct {
?start rdf:first ?s ;
rdf:rest ?end .
?end rdf:first ?o .
?last rdf:rest rdf:nil .
}
where {
{
?s :p ?o
bind(bnode(str(?s)) as ?start)
bind(bnode(str(?o)) as ?end)
}
UNION {
?s :p ?o
FILTER NOT EXISTS { ?o :p [] }
BIND(bnode(str(?o)) as ?last)
}
}
The result (note the pretty Turtle formatting to validate the well-formedness):
stardog query test -f PRETTY_TURTLE ~/cp/tmp/list.rq
@prefix ...
( :a :b :c :d ) .
PS. The awkward bit is appending the rdf:nil
terminator, probably can be done more efficiently...
Turns out this example isn't 100% spec-compliant because the
bnode()
function is scoped to solution. What this means is that when the query generates the next edge of the RDF list, it gets a new bnode for the end of the previous edge, not the bnode that was generated on the previous step. This breaks the continuity of the list.I have created a ticket for Stardog to fix this but it makes this task very difficult (if not impossible) in pure SPARQL 1.1. It'd be easy to achieve without bnodes (ie using auto-generated IRIs) or using a SPARQL Update sequence where the 2nd query would turn the auto-generated IRIs created by the 1st into bnodes.
Thanks John Walker for spotting this.