2
votes

I am trying to CONSTRUCT complete RDF Collections with a SPARQL 1.1 property path. The property path examples that I have seen are able to get the rdf:first nodes, but I have yet to see one that is able to get the whole chain, including the bnodes. The key parts of the query look like this:

PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX sc: <http://iiif.io/api/presentation/2#>

CONSTRUCT {?range sc:hasCanvases ?listid .
?listid rdf:first ?canvas .
?listid rdf:rest ?mid .
?mid rdf:rest ?node .
?node rdf:first ?canvas .
?node rdf:rest ?last .
[...]}

WHERE {values ?range {<http://some.uri>}
?range sc:hasCanvases ?listid .
values ?e { rdf:first rdf:rest } 
?listid rdf:rest* ?mid . 
?mid ?e ?node FILTER (?mid != ?node). 
?listid rdf:first ?first .
?node rdf:first ?canvas .
?node rdf:rest ?last .
[...]}

It mostly works, except that the ?listid node binds to every rdf:rest ?mid object in the property path.

There is a referencedOnce constraint in the jsonld.fromRDF method that makes these extra head triples problematic for list reconstruction with that library. I have tried a variety of subqueries, property paths and FILTERS, but am quite stuck on this. Is it possible?

2
It would help if you could show the complete construct query you're using. It's not clear what you're trying to construct, since you left that part out. It's also not clear what you mean by "head node"; there's no ?head in the query... - Joshua Taylor
thank you for this solution @JoshuaTaylor the problem was in the CONSTRUCT part of the query (I have added it now for clarity). I simply changed from this: ?range sc:hasCanvases ?listid . ?listid rdf:first ?canvas . ?listid rdf:rest ?mid . to this: ?range sc:hasCanvases ?listid . ?mid rdf:first ?canvas . ?mid rdf:rest ?last . and it works. It seems that the subject of the property path is iteratively bound to every matching object in the path. So when referenced in CONSTRUCT, it shows a pattern like 1-1, 1-2, 1-3, 1-4, 1-5. - Christopher Johnson

2 Answers

3
votes

It's not exactly clear what you're asking, but it sounds like you want to write a construct query that will contain a list that's present in the data. It's much easier if you provide sample data, and if you show the complete query that you're trying to use, because then we have much less to guess about. Under this assumption, let's start with some data. Here are two things with a property relating each one to a list of values:

@prefix : <urn:ex:>

:a :hasList (1 2 3 4 5) .
:b :hasList (6 7 8 9 0) .

Here's a query that retrieves the list associated with :a and constructs it:

prefix : <urn:ex:>
prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>

construct {
  ?mid rdf:first ?value ;
       rdf:rest ?tail .
}
where {
  :a :hasList ?list .
  ?list rdf:rest* ?mid .
  ?mid rdf:first ?value .
  ?mid rdf:rest ?tail .
}

And the results:

<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns="urn:ex:">
  <rdf:Description>
    <rdf:rest rdf:parseType="Resource">
      <rdf:rest rdf:parseType="Resource">
        <rdf:rest rdf:parseType="Resource">
          <rdf:rest rdf:parseType="Resource">
            <rdf:rest rdf:resource="http://www.w3.org/1999/02/22-rdf-syntax-ns#nil"/>
            <rdf:first rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
            >5</rdf:first>
          </rdf:rest>
          <rdf:first rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
          >4</rdf:first>
        </rdf:rest>
        <rdf:first rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
        >3</rdf:first>
      </rdf:rest>
      <rdf:first rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
      >2</rdf:first>
    </rdf:rest>
    <rdf:first rdf:datatype="http://www.w3.org/2001/XMLSchema#integer"
    >1</rdf:first>
  </rdf:Description>
</rdf:RDF>

There where part can even be made a bit more concise, since you don't use the value of ?list:

  :a :hasList/rdf:rest* ?mid .
  ?mid rdf:first ?value .
  ?mid rdf:rest ?tail .
1
votes

I think you're looking for the rdf:rest*/rdf:first idiom to get all members in the list. Try the following (I'm assuming that ?listid contains one or more RDF Collection(list)):

SELECT ?node
WHERE {
   values ?sequence {<http://some.uri>}
   ?sequence eg:hasThings ?listid .
   ?listid rdf:rest*/rdf:first ?node .
}