Faster implementation of TPF for SDB (#54)

* Initial performance improvements to TPF

* Add faster counts for TPF

* Correct end count

* Bug fixes, use hashes for filtering

* Initial LDF SPARQL client

* Handle blank nodes correctly
This commit is contained in:
grahamtriggs 2017-01-31 21:09:49 +00:00 committed by GitHub
parent 0191ae6dbb
commit b5da11c2be
17 changed files with 1446 additions and 112 deletions

View file

@ -8,6 +8,7 @@ import java.util.List;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelChangedListener;
import org.apache.jena.rdf.model.RDFNode;
/**
* Interface for API to write, read, and update Vitro's RDF store, with support
@ -247,7 +248,11 @@ public interface RDFService {
*
* @return ChangeSet an empty ChangeSet object
*/
public ChangeSet manufactureChangeSet();
public ChangeSet manufactureChangeSet();
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException;
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException;
/**
* Frees any resources held by this RDFService object

View file

@ -468,6 +468,16 @@ public class LanguageFilteringRDFService implements RDFService {
return s.manufactureChangeSet();
}
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
return s.countTriples(subject, predicate, object);
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
return s.getTriples(subject, predicate, object, limit, offset);
}
@Override
public void close() {
s.close();

View file

@ -16,6 +16,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceFactory;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
import org.apache.jena.rdf.model.RDFNode;
/**
* An RDFServiceFactory that always returns the same RDFService object
@ -192,6 +193,16 @@ public class RDFServiceFactorySingle implements RDFServiceFactory {
return s.manufactureChangeSet();
}
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
return s.countTriples(subject, predicate, object);
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
return s.getTriples(subject, predicate, object, limit, offset);
}
@Override
public void close() {
// Don't close s. It's being used by everybody.

View file

@ -12,18 +12,24 @@ import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.atlas.io.StringWriterI;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryFactory;
import org.apache.jena.query.QueryParseException;
import org.apache.jena.query.QuerySolution;
import org.apache.jena.query.Syntax;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelChangedListener;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.riot.out.NodeFormatter;
import org.apache.jena.riot.out.NodeFormatterTTL;
import org.apache.jena.vocabulary.RDF;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeListener;
@ -34,6 +40,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import edu.cornell.mannlib.vitro.webapp.utils.logging.ToString;
import org.vivoweb.linkeddatafragments.datasource.rdfservice.RDFServiceBasedRequestProcessorForTPFs;
public abstract class RDFServiceImpl implements RDFService {
@ -340,5 +347,121 @@ public abstract class RDFServiceImpl implements RDFService {
public String toString() {
return ToString.simpleName(this) + "[" + ToString.hashHex(this) + "]";
}
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
StringBuilder whereClause = new StringBuilder();
StringBuilder orderBy = new StringBuilder();
if ( subject != null ) {
appendNode(whereClause.append(' '), subject);
} else {
whereClause.append(" ?s");
orderBy.append(" ?s");
}
if ( predicate != null ) {
appendNode(whereClause.append(' '), predicate);
} else {
whereClause.append(" ?p");
orderBy.append(" ?p");
}
if ( object != null ) {
appendNode(whereClause.append(' '), object);
} else {
whereClause.append(" ?o");
orderBy.append(" ?o");
}
long estimate = -1;
StringBuilder count = new StringBuilder();
count.append("SELECT (COUNT(*) AS ?count) WHERE { ");
count.append(whereClause.toString());
count.append(" . ");
count.append(" }");
CountConsumer countConsumer = new CountConsumer();
this.sparqlSelectQuery(count.toString(), countConsumer);
return countConsumer.count;
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
StringBuilder whereClause = new StringBuilder();
StringBuilder orderBy = new StringBuilder();
if ( subject != null ) {
appendNode(whereClause.append(' '), subject);
} else {
whereClause.append(" ?s");
orderBy.append(" ?s");
}
if ( predicate != null ) {
appendNode(whereClause.append(' '), predicate);
} else {
whereClause.append(" ?p");
orderBy.append(" ?p");
}
if ( object != null ) {
appendNode(whereClause.append(' '), object);
} else {
whereClause.append(" ?o");
orderBy.append(" ?o");
}
StringBuilder constructQuery = new StringBuilder();
constructQuery.append("CONSTRUCT { ");
constructQuery.append(whereClause.toString());
constructQuery.append(" } WHERE { ");
constructQuery.append(whereClause.toString()).append(" . ");
constructQuery.append(" }");
if (orderBy.length() > 0) {
constructQuery.append(" ORDER BY").append(orderBy.toString());
}
if (limit > 0) {
constructQuery.append(" LIMIT ").append(limit);
}
if (offset > 0) {
constructQuery.append(" OFFSET ").append(offset);
}
Model triples = ModelFactory.createDefaultModel();
this.sparqlConstructQuery(constructQuery.toString(), triples);
return triples;
}
private void appendNode(StringBuilder builder, RDFNode node) {
if (node.isLiteral()) {
builder.append(literalToString(node.asLiteral()));
} else if (node.isURIResource()) {
builder.append('<' + node.asResource().getURI() + '>');
}
}
private String literalToString(Literal l) {
StringWriterI sw = new StringWriterI();
NodeFormatter fmt = new NodeFormatterTTL(null, null);
fmt.formatLiteral(sw, l.asNode());
return sw.toString();
}
class CountConsumer extends ResultSetConsumer {
public long count = -1;
@Override
protected void processQuerySolution(QuerySolution qs) {
if (count == -1) {
Literal literal = qs.getLiteral("count");
count = literal.getLong();
}
}
}
}

View file

@ -16,6 +16,9 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.query.QuerySolutionMap;
import org.apache.jena.query.Syntax;
import org.apache.jena.rdf.model.Literal;
import org.apache.jena.riot.RDFDataMgr;
import org.apache.log4j.lf5.util.StreamUtils;
@ -612,6 +615,71 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
return graph.isIsomorphicWith(fromTripleStoreModel);
}
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
Query countQuery = QueryFactory.create("SELECT (COUNT(?s) AS ?count) WHERE { ?s ?p ?o } ORDER BY ?s ?p ?o", Syntax.syntaxSPARQL_11);
QuerySolutionMap map = new QuerySolutionMap();
if ( subject != null ) {
map.add("s", subject);
}
if ( predicate != null ) {
map.add("p", predicate);
}
if ( object != null ) {
map.add("o", object);
}
DatasetWrapper dw = getDatasetWrapper();
try {
Dataset d = dw.getDataset();
try (QueryExecution qexec = QueryExecutionFactory.create(countQuery, d, map)) {
ResultSet results = qexec.execSelect();
if (results.hasNext()) {
QuerySolution soln = results.nextSolution() ;
Literal literal = soln.getLiteral("count");
return literal.getLong();
}
}
} finally {
dw.close();
}
return 0;
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
Query query = QueryFactory.create("CONSTRUCT WHERE { ?s ?p ?o }", Syntax.syntaxSPARQL_11);
QuerySolutionMap map = new QuerySolutionMap();
if ( subject != null ) {
map.add("s", subject);
}
if ( predicate != null ) {
map.add("p", predicate);
}
if ( object != null ) {
map.add("o", object);
}
query.setOffset(offset);
query.setLimit(limit);
Model triples = ModelFactory.createDefaultModel();
DatasetWrapper dw = getDatasetWrapper();
try {
Dataset d = dw.getDataset();
try (QueryExecution qexec = QueryExecutionFactory.create(query, d, map)) {
qexec.execConstruct(triples);
}
return triples;
} finally {
dw.close();
}
}
@Override
public void close() {
// nothing
@ -620,5 +688,4 @@ public abstract class RDFServiceJena extends RDFServiceImpl implements RDFServic
protected QueryExecution createQueryExecution(String queryString, Query q, Dataset d) {
return QueryExecutionFactory.create(q, d);
}
}

View file

@ -4,20 +4,34 @@ package edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.sdb;
import java.io.IOException;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.sql.DataSource;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.jena.datatypes.RDFDatatype;
import org.apache.jena.datatypes.TypeMapper;
import org.apache.jena.datatypes.xsd.XSDDatatype;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
import org.apache.jena.query.QueryExecutionFactory;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.sdb.SDBFactory;
import org.apache.jena.sdb.Store;
import org.apache.jena.sdb.StoreDesc;
import org.apache.jena.sdb.layout2.NodeLayout2;
import org.apache.jena.sdb.layout2.ValueType;
import org.apache.jena.sdb.sql.SDBConnection;
import edu.cornell.mannlib.vitro.webapp.dao.jena.DatasetWrapper;
@ -28,6 +42,8 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.impl.jena.RDFServiceJena;
import org.apache.jena.sdb.store.DatabaseType;
import org.apache.jena.sdb.store.LayoutType;
public class RDFServiceSDB extends RDFServiceJena implements RDFService {
@ -147,8 +163,118 @@ public class RDFServiceSDB extends RDFServiceJena implements RDFService {
// This used to execute against the default model if the query included an OPTIONAL
// However, in recent Jena this turns out to be much slower than executing against the dataset directly
}
@Override
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
if (LayoutType.LayoutTripleNodesHash.equals(storeDesc.getLayout())) {
if (DatabaseType.MySQL.equals(storeDesc.getDbType()) ||
DatabaseType.PostgreSQL.equals(storeDesc.getDbType())) {
SDBConnection sdbConn = getSDBConnection();
try {
String whereClause = makeWhereClause(subject, predicate, object);
Statement stmt = sdbConn.getSqlConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT count(DISTINCT s,p,o) AS tcount FROM Quads" + (StringUtils.isEmpty(whereClause) ? "" : " WHERE " + whereClause));
try {
while (rs.next()) {
return rs.getLong("tcount");
}
} finally {
rs.close();
}
} catch (SQLException sqle) {
throw new RDFServiceException("Unable to retrieve triples", sqle);
} finally {
close(sdbConn);
}
}
} else {
return super.countTriples(subject, predicate, object);
}
return super.countTriples(subject, predicate, object);
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
if (LayoutType.LayoutTripleNodesHash.equals(storeDesc.getLayout())) {
if (DatabaseType.MySQL.equals(storeDesc.getDbType()) ||
DatabaseType.PostgreSQL.equals(storeDesc.getDbType())) {
Model triples = ModelFactory.createDefaultModel();
SDBConnection sdbConn = getSDBConnection();
try {
String whereClause = makeWhereClause(subject, predicate, object);
Statement stmt = sdbConn.getSqlConnection().createStatement();
ResultSet rs = stmt.executeQuery("SELECT \n" +
"N1.lex AS s_lex,\n" +
"N1.lang AS s_lang,\n" +
"N1.datatype AS s_datatype,\n" +
"N1.type AS s_type,\n" +
"N2.lex AS p_lex,\n" +
"N2.lang AS p_lang,\n" +
"N2.datatype AS p_datatype,\n" +
"N2.type AS p_type,\n" +
"N3.lex AS o_lex,\n" +
"N3.lang AS o_lang,\n" +
"N3.datatype AS o_datatype,\n" +
"N3.type AS o_type\n" +
"FROM\n" +
"(SELECT DISTINCT s,p,o FROM Quads" +
(StringUtils.isEmpty(whereClause) ? "" : " WHERE " + whereClause) +
" ORDER BY s,p,o " +
(limit > 0 ? "LIMIT " + limit : "") +
(offset > 0 ? " OFFSET " + offset : "") + ") Q\n" +
"LEFT OUTER JOIN\n" +
"\tNodes AS N1\n" +
"ON ( Q.s = N1.hash )\n" +
"LEFT OUTER JOIN\n" +
"\tNodes AS N2\n" +
"ON ( Q.p = N2.hash )\n" +
"LEFT OUTER JOIN\n" +
"\tNodes AS N3\n" +
"ON ( Q.o = N3.hash )");
try {
while (rs.next()) {
Node subjectNode = makeNode(
rs.getString("s_lex"),
rs.getString("s_datatype"),
rs.getString("s_lang"),
ValueType.lookup(rs.getInt("s_type")));
Node predicateNode = makeNode(
rs.getString("p_lex"),
rs.getString("p_datatype"),
rs.getString("p_lang"),
ValueType.lookup(rs.getInt("p_type")));
Node objectNode = makeNode(
rs.getString("o_lex"),
rs.getString("o_datatype"),
rs.getString("o_lang"),
ValueType.lookup(rs.getInt("o_type")));
triples.add(
triples.asStatement(Triple.create(subjectNode, predicateNode, objectNode))
);
}
} finally {
rs.close();
}
} catch (SQLException sqle) {
throw new RDFServiceException("Unable to retrieve triples", sqle);
} finally {
close(sdbConn);
}
return triples;
}
}
return super.getTriples(subject, predicate, object, limit, offset);
}
@Override
public void close() {
if (conn != null) {
try {
@ -159,4 +285,55 @@ public class RDFServiceSDB extends RDFServiceJena implements RDFService {
}
}
// Copied from Jena SQLBridge2
private static Node makeNode(String lex, String datatype, String lang, ValueType vType) {
switch(vType) {
case BNODE:
return NodeFactory.createBlankNode(lex);
case URI:
return NodeFactory.createURI(lex);
case STRING:
return NodeFactory.createLiteral(lex, lang);
case XSDSTRING:
return NodeFactory.createLiteral(lex, XSDDatatype.XSDstring);
case INTEGER:
return NodeFactory.createLiteral(lex, XSDDatatype.XSDinteger);
case DOUBLE:
return NodeFactory.createLiteral(lex, XSDDatatype.XSDdouble);
case DATETIME:
return NodeFactory.createLiteral(lex, XSDDatatype.XSDdateTime);
case OTHER:
RDFDatatype dt = TypeMapper.getInstance().getSafeTypeByName(datatype);
return NodeFactory.createLiteral(lex, dt);
default:
log.warn("Unrecognized: (" + lex + ", " + lang + ", " + vType + ")");
return NodeFactory.createLiteral("UNRECOGNIZED");
}
}
private String makeWhereClause(RDFNode subject, RDFNode predicate, RDFNode object) {
StringBuilder whereClause = new StringBuilder();
if (subject != null) {
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append("s=").append(NodeLayout2.hash(subject.asNode()));
}
if (predicate != null) {
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append("p=").append(NodeLayout2.hash(predicate.asNode()));
}
if (object != null) {
if (whereClause.length() > 0) {
whereClause.append(" AND ");
}
whereClause.append("o=").append(NodeLayout2.hash(object.asNode()));
}
return whereClause.length() > 0 ? whereClause.toString() : null;
}
}

View file

@ -14,6 +14,7 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.ChangeSet;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import org.apache.jena.rdf.model.RDFNode;
/**
* This RDFService wrapper adds instrumentation to the time-consuming methods of
@ -182,6 +183,16 @@ public class LoggingRDFService implements RDFService {
return innerService.manufactureChangeSet();
}
@Override
public long countTriples(RDFNode subject, RDFNode predicate, RDFNode object) throws RDFServiceException {
return innerService.countTriples(subject, predicate, object);
}
@Override
public Model getTriples(RDFNode subject, RDFNode predicate, RDFNode object, long limit, long offset) throws RDFServiceException {
return innerService.getTriples(subject, predicate, object, limit, offset);
}
@Override
public void close() {
innerService.close();

View file

@ -106,7 +106,7 @@ public class HtmlTriplePatternFragmentWriterImpl extends TriplePatternFragmentWr
// Calculate start and end triple number
Long start = ((tpfRequest.getPageNumber() - 1) * fragment.getMaxPageSize()) + 1;
data.put("start", start);
data.put("end", start + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize()));
data.put("end", (start - 1) + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize()));
// Compose query object
Map query = new HashMap();

View file

@ -6,6 +6,9 @@ import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFService;
import edu.cornell.mannlib.vitro.webapp.rdfservice.RDFServiceException;
import edu.cornell.mannlib.vitro.webapp.rdfservice.ResultSetConsumer;
import org.apache.jena.atlas.io.StringWriterI;
import org.apache.jena.graph.Node;
import org.apache.jena.graph.NodeFactory;
import org.apache.jena.graph.Triple;
import org.apache.jena.query.Dataset;
import org.apache.jena.query.Query;
import org.apache.jena.query.QueryExecution;
@ -19,6 +22,8 @@ import org.apache.jena.rdf.model.Literal;
import org.apache.jena.rdf.model.Model;
import org.apache.jena.rdf.model.ModelFactory;
import org.apache.jena.rdf.model.RDFNode;
import org.apache.jena.rdf.model.Statement;
import org.apache.jena.rdf.model.StmtIterator;
import org.apache.jena.riot.out.NodeFormatter;
import org.apache.jena.riot.out.NodeFormatterTTL;
import org.apache.jena.tdb.TDBFactory;
@ -74,6 +79,32 @@ public class RDFServiceBasedRequestProcessorForTPFs
return sw.toString();
}
private Node skolemize(Node node) {
if (node != null && node.isBlank()) {
return NodeFactory.createURI("bnode://" + node.getBlankNodeLabel());
}
return node;
}
private RDFNode deskolemize(RDFNode node) {
if (node == null) {
return null;
}
if (node.isResource()) {
String uri = node.asResource().getURI();
if (uri != null && uri.startsWith("bnode://")) {
String bnodeId = uri.substring(8);
return ModelFactory.createDefaultModel().asRDFNode(
NodeFactory.createBlankNode(bnodeId)
);
}
}
return node;
}
@Override
protected ILinkedDataFragment createFragment(
final ITriplePatternElement<RDFNode,String,String> subject,
@ -82,100 +113,51 @@ public class RDFServiceBasedRequestProcessorForTPFs
final long offset,
final long limit )
{
StringBuilder whereClause = new StringBuilder();
StringBuilder filter = new StringBuilder();
StringBuilder orderBy = new StringBuilder();
if ( ! subject.isVariable() ) {
appendNode(whereClause.append(' '), subject.asConstantTerm());
} else {
whereClause.append(" ?s");
if (filter.length() > 0) { filter.append(" && "); }
filter.append("!isBlank(?s)");
orderBy.append(" ?s");
}
if ( ! predicate.isVariable() ) {
appendNode(whereClause.append(' '), predicate.asConstantTerm());
} else {
whereClause.append(" ?p");
if (filter.length() > 0) { filter.append(" && "); }
filter.append("!isBlank(?p)");
orderBy.append(" ?p");
}
if ( ! object.isVariable() ) {
appendNode(whereClause.append(' '), object.asConstantTerm());
} else {
whereClause.append(" ?o");
if (filter.length() > 0) { filter.append(" && "); }
filter.append("!isBlank(?o)");
orderBy.append(" ?o");
}
StringBuilder constructQuery = new StringBuilder();
constructQuery.append("CONSTRUCT { ");
constructQuery.append(whereClause.toString());
constructQuery.append(" } WHERE { ");
constructQuery.append(whereClause.toString()).append(" . ");
if (filter.length() > 0) {
constructQuery.append(" FILTER(").append(filter.toString()).append(")");
}
constructQuery.append(" }");
if (orderBy.length() > 0) {
constructQuery.append(" ORDER BY").append(orderBy.toString());
}
if (limit > 0) {
constructQuery.append(" LIMIT ").append(limit);
}
if (offset > 0) {
constructQuery.append(" OFFSET ").append(offset);
}
Model triples = ModelFactory.createDefaultModel();
try {
rdfService.sparqlConstructQuery(constructQuery.toString(), triples);
RDFNode nSubject = subject.isVariable() ? null : deskolemize(subject.asConstantTerm());
RDFNode nPredicate = predicate.isVariable() ? null : deskolemize(predicate.asConstantTerm());
RDFNode nObject = object.isVariable() ? null : deskolemize(object.asConstantTerm());
Model triples = rdfService.getTriples(nSubject, nPredicate, nObject, limit, offset);
if (triples == null || triples.isEmpty()) {
return createEmptyTriplePatternFragment();
}
if (triples.size() > 0) {
Model replacedBlankNodes = ModelFactory.createDefaultModel();
StmtIterator iter = triples.listStatements();
while (iter.hasNext()) {
Statement oldStmt = iter.next();
Triple t = oldStmt.asTriple();
replacedBlankNodes.add(
replacedBlankNodes.asStatement(
new Triple(
skolemize(t.getSubject()),
skolemize(t.getPredicate()),
skolemize(t.getObject())
)
)
);
}
triples = replacedBlankNodes;
}
long size = triples.size();
long estimate = -1;
estimate = rdfService.countTriples(nSubject, nPredicate, nObject);
// No estimate or incorrect
if (estimate < offset + size) {
estimate = (size == limit) ? offset + size + 1 : offset + size;
}
// create the fragment
final boolean isLastPage = ( estimate < offset + limit );
return createTriplePatternFragment( triples, estimate, isLastPage );
} catch (RDFServiceException e) {
return createEmptyTriplePatternFragment();
}
if (triples.isEmpty()) {
return createEmptyTriplePatternFragment();
}
// Try to get an estimate
long size = triples.size();
long estimate = -1;
StringBuilder count = new StringBuilder();
count.append("SELECT (COUNT(*) AS ?count) WHERE { ");
count.append(whereClause.toString());
count.append(" . ");
if (filter.length() > 0) {
count.append(" FILTER(").append(filter.toString()).append(") ");
}
count.append(" }");
try {
CountConsumer countConsumer = new CountConsumer();
rdfService.sparqlSelectQuery(count.toString(), countConsumer);
estimate = countConsumer.estimate;
} catch (RDFServiceException e) {
return createEmptyTriplePatternFragment();
}
// No estimate or incorrect
if (estimate < offset + size) {
estimate = (size == limit) ? offset + size + 1 : offset + size;
}
// create the fragment
final boolean isLastPage = ( estimate < offset + limit );
return createTriplePatternFragment( triples, estimate, isLastPage );
}
} // end of class Worker
@ -186,16 +168,4 @@ public class RDFServiceBasedRequestProcessorForTPFs
*/
public RDFServiceBasedRequestProcessorForTPFs() {
}
class CountConsumer extends ResultSetConsumer {
public long estimate = -1;
@Override
protected void processQuerySolution(QuerySolution qs) {
if (estimate == -1) {
Literal literal = qs.getLiteral("count");
estimate = literal.getLong();
}
}
}
}

View file

@ -124,7 +124,7 @@ public class HtmlTriplePatternFragmentWriterImpl extends TriplePatternFragmentWr
// Calculate start and end triple number
Long start = ((tpfRequest.getPageNumber() - 1) * fragment.getMaxPageSize()) + 1;
data.put("start", start);
data.put("end", start + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize()));
data.put("end", (start - 1) + (triples.size() < fragment.getMaxPageSize() ? triples.size() : fragment.getMaxPageSize()));
// Compose query object
Map query = new HashMap();