Skip to content

Commit

Permalink
Add paging support.
Browse files Browse the repository at this point in the history
  • Loading branch information
RubenVerborgh committed Apr 16, 2014
1 parent abb880c commit 3100978
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 11 deletions.
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@
<artifactId>jena-arq</artifactId>
<version>2.11.1</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.3.3</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand All @@ -30,6 +35,7 @@
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
Expand Down
7 changes: 5 additions & 2 deletions src/org/linkeddatafragments/datasource/DataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@
*/
public interface DataSource {
/**
* Gets the Basic Linked Data Fragment matching the specified triple pattern.
* Gets a page of the Basic Linked Data Fragment matching the specified triple pattern.
* @param subject the subject (null to match any subject)
* @param predicate the predicate (null to match any predicate)
* @param object the object (null to match any object)
* @param offset the triple index at which to start the page
* @param limit the number of triples on the page
* @return the first page of the fragment
*/
public BasicLinkedDataFragment getFragment(Resource subject, Property predicate, RDFNode object);
public BasicLinkedDataFragment getFragment(Resource subject, Property predicate, RDFNode object,
long offset, long limit);
}
32 changes: 27 additions & 5 deletions src/org/linkeddatafragments/datasource/HdtDataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@
* @author Ruben Verborgh
*/
public class HdtDataSource implements DataSource {
private final static int TRIPLES_LIMIT = 100;
private final HDT datasource;
private final NodeDictionary dictionary;

Expand All @@ -36,7 +35,10 @@ public HdtDataSource(String hdtFile) throws IOException {
}

@Override
public BasicLinkedDataFragment getFragment(Resource subject, Property predicate, RDFNode object) {
public BasicLinkedDataFragment getFragment(Resource subject, Property predicate, RDFNode object, final long offset, final long limit) {
if (offset < 0) throw new IndexOutOfBoundsException("offset");
if (limit < 1) throw new IllegalArgumentException("limit");

// look up the result from the HDT datasource
final int subjectId = subject == null ? 0 : dictionary.getIntID(subject.asNode(), TripleComponentRole.SUBJECT);
final int predicateId = predicate == null ? 0 : dictionary.getIntID(predicate.asNode(), TripleComponentRole.PREDICATE);
Expand All @@ -50,9 +52,29 @@ public BasicLinkedDataFragment getFragment(Resource subject, Property predicate,
@Override
public Model getTriples() {
final Model triples = ModelFactory.createDefaultModel();
result.goToStart();
for (int i = 0; i < TRIPLES_LIMIT && result.hasNext(); i++)
triples.add(triples.asStatement(toTriple(result.next())));

// try to jump directly to the offset
boolean atOffset;
if (result.canGoTo()) {
try {
result.goTo(offset);
atOffset = true;
}
// if the offset is outside the bounds, this page has no matches
catch (IndexOutOfBoundsException exception) { atOffset = false; }
}
// if not possible, advance to the offset iteratively
else {
result.goToStart();
for (int i = 0; !(atOffset = i == offset) && result.hasNext(); i++)
result.next();
}

// add `limit` triples to the result model
if (atOffset) {
for (int i = 0; i < limit && result.hasNext(); i++)
triples.add(triples.asStatement(toTriple(result.next())));
}
return triples;
}

Expand Down
37 changes: 33 additions & 4 deletions src/org/linkeddatafragments/servlet/BasicLdfServlet.java
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.http.client.utils.URIBuilder;
import org.linkeddatafragments.config.ConfigReader;
import org.linkeddatafragments.datasource.BasicLinkedDataFragment;
import org.linkeddatafragments.datasource.DataSource;
Expand All @@ -37,6 +38,7 @@ public class BasicLdfServlet extends HttpServlet {
private final static long serialVersionUID = 1L;
private final static Pattern STRINGPATTERN = Pattern.compile("^\"(.*)\"(?:@(.*)|\\^\\^<(.*)>)?$");
private final static TypeMapper types = TypeMapper.getInstance();
private final static long TRIPLESPERPAGE = 100;

private ConfigReader config;
private HashMap<String, DataSource> dataSources = new HashMap<String, DataSource>();
Expand Down Expand Up @@ -76,18 +78,21 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
final Resource subject = parseAsResource(request.getParameter("subject"));
final Property predicate = parseAsProperty(request.getParameter("predicate"));
final RDFNode object = parseAsNode(request.getParameter("object"));
final BasicLinkedDataFragment fragment = dataSource.getFragment(subject, predicate, object);
final long page = Math.max(1, parseAsInteger(request.getParameter("page")));
final long limit = TRIPLESPERPAGE, offset = limit * (page - 1);
final BasicLinkedDataFragment fragment = dataSource.getFragment(subject, predicate, object, offset, limit);

// fill the output model
final Model output = fragment.getTriples();
final boolean isEmpty = output.size() == 0;
output.setNsPrefixes(config.getPrefixes());

// add dataset metadata
final String hostName = request.getHeader("Host");
final String datasetUrl = request.getScheme() + "://" +
(hostName == null ? request.getServerName() : hostName) + request.getRequestURI();
final String fragmentUrl = query == null ? datasetUrl : (datasetUrl + "?" + query);
final Resource datasetId = output.createResource(datasetUrl);
final Resource datasetId = output.createResource(datasetUrl + "#dataset");
final Resource fragmentId = output.createResource(fragmentUrl);
output.add(datasetId, RDF_TYPE, VOID_DATASET);
output.add(datasetId, RDF_TYPE, HYDRA_COLLECTION);
Expand All @@ -99,6 +104,20 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
final Literal total = output.createTypedLiteral(fragment.getTotalSize(), XSDDatatype.XSDinteger);
output.add(fragmentId, VOID_TRIPLES, total);
output.add(fragmentId, HYDRA_TOTALITEMS, total);
output.add(fragmentId, HYDRA_ITEMSPERPAGE, output.createTypedLiteral(limit, XSDDatatype.XSDinteger));

// add pages
final URIBuilder pagedUrl = new URIBuilder(fragmentUrl);
pagedUrl.setParameter("page", "1");
output.add(fragmentId, HYDRA_FIRSTPAGE, output.createResource(pagedUrl.toString()));
if (offset > 0) {
pagedUrl.setParameter("page", Long.toString(page - 1));
output.add(fragmentId, HYDRA_PREVIOUSPAGE, output.createResource(pagedUrl.toString()));
}
if (offset + limit < fragment.getTotalSize()) {
pagedUrl.setParameter("page", Long.toString(page + 1));
output.add(fragmentId, HYDRA_NEXTPAGE, output.createResource(pagedUrl.toString()));
}

// add controls
final Resource triplePattern = output.createResource();
Expand All @@ -118,17 +137,27 @@ public void doGet(HttpServletRequest request, HttpServletResponse response) thro
output.add(objectMapping, HYDRA_PROPERTY, RDF_OBJECT);

// serialize the output as Turtle
response.setStatus(fragment.getTotalSize() == 0 ? 404 : 200);
response.setStatus(isEmpty ? 404 : 200);
response.setHeader("Server", "Linked Data Fragments Server");
response.setContentType("text/turtle");
response.setCharacterEncoding("utf-8");
output.write(response.getWriter(), "Turtle");
output.write(response.getWriter(), "Turtle", fragmentUrl);
}
catch (Exception e) {
throw new ServletException(e);
}
}

/**
* Parses the given value as an integer.
* @param value the value
* @return the parsed value
*/
private int parseAsInteger(String value) {
try { return Integer.parseInt(value); }
catch (NumberFormatException ex) { return 0; }
}

/**
* Parses the given value as an RDF resource.
* @param value the value
Expand Down
4 changes: 4 additions & 0 deletions src/org/linkeddatafragments/util/CommonResources.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ public class CommonResources {
public final static Property HYDRA_PROPERTY = createProperty(HYDRA + "property");
public final static Property HYDRA_COLLECTION = createProperty(HYDRA + "Collection");
public final static Property HYDRA_PAGEDCOLLECTION = createProperty(HYDRA + "PagedCollection");
public final static Property HYDRA_FIRSTPAGE = createProperty(HYDRA + "firstPage");
public final static Property HYDRA_LASTPAGE = createProperty(HYDRA + "lastPage");
public final static Property HYDRA_NEXTPAGE = createProperty(HYDRA + "nextPage");
public final static Property HYDRA_PREVIOUSPAGE = createProperty(HYDRA + "previousPage");

private final static Property createProperty(String uri) {
return ResourceFactory.createProperty(uri);
Expand Down

0 comments on commit 3100978

Please # to comment.