How to maintain order while retrieving the data in sparql

Viewed 454

Is there a way to ensure the order during the retrieval in sparql?

Say I want to store List<Literals>(order important) as an object

{:subj :pred 'o1'. :subj :pred 'o2'. :subj :pred 'o3'}

How can I insert the above data such that when I perform select ?s ?p ?o where {?s ?p ?o} the order shouldn't change?

Do sparql always preserve the insertion data order when retrieved?

In the above example, I used literals as an example. Is there a way of preserving the order of IRIs as well?

I have seen sparql documentation on RDF.Seq, But I didn't understand exactly how to use that. Please provide some simple insert and select queries for ensuring order or share any relevant resources.

2 Answers

If you need to preserve order you can represent the list as an RDF Collection. If you have a list of strings, e.g.:

 List<String> objects = Arrays.asList("o1", "o2", "o3");

representing it as an RDF collection would look something like this (in Turtle syntax):

_:node a rdf:List;
       rdf:first "o1" ;
       rdf:rest [ 
          rdf:first "o2";
          rdf:rest [
             rdf:first "o3";
             rdf:rest rdf:nil .
          ]
       ].          

That may look cumbersome, but the RDF4J libraries offer some RDF Collection utilities to make handling this easier from code. To transform your Java list to an RDF Collection, you can do something like this:

// starting node of the RDF collection
Resource head = valueFactory.createBNode();
// convert our list and add it to a newly-created Model
Model collection = RDFCollections.asRDF(objects, head, new LinkedHashModel());

Here, collection is just an RDF model (a set of RDF statements) that you can add to your triplestore like any other RDF data (see the RDF4J docs for some further tips on how to work with collections when storing and retrieving from a repository).

To convert a RDF collection back to a Java list (preserving order):

List<Value> values = RDFCollections.asValues(collection, head, new ArrayList<Value>());

Or if you want them back as a list of string objects again:

List<String> values = new ArrayList<>();
RDFCollections.consumeValues(collection, head, v -> values.add(v.stringValue()));

Edit it is also possible to retrieve such RDF collection items in order using SPARQL 1.1, though it is not straightforward. See Is it possible to get the position of an element in an RDF Collection in SPARQL? for a good solution.

All that being said though, it's probably a good idea to stop and think if you actually need to store things in a fixed order. RDF is by its nature unordered, and while RDF Collection modelling provide a mechanism, it's not efficient to store everything as collections.

In most common cases, the order is not really part of the actual data model, but is a presentation issue. In those cases, you can most of the time use a SPARQL query with an ORDER BY clause instead, to sort the query results.

The use-case does not seem to fit well with the RDF model. Since RDF describes a graph, order of its edges is not important.

Having this said, one simple solution to your particular use-case might be e.g. using a reified node with two extra RDF properties :order and :value :

RDF:

:subj :pred [:order 1; :value 'o1']. 
:subj :pred [:order 2; :value 'o2']. 
:subj :pred [:order 3; :value 'o3'].

SPARQL:

SELECT ?s ?p ?o {
  ?s ?p [:order ?order; :value ?o]
} ORDER BY ?order
Related