|
17 | 17 | import java.io.InputStream; |
18 | 18 | import java.io.Reader; |
19 | 19 | import java.net.URL; |
| 20 | +import java.util.Arrays; |
20 | 21 | import java.util.Objects; |
| 22 | +import java.util.stream.Collectors; |
21 | 23 |
|
22 | 24 | import org.apache.http.client.HttpClient; |
23 | 25 | import org.eclipse.rdf4j.common.iteration.CloseableIteration; |
|
39 | 41 | import org.eclipse.rdf4j.model.impl.DynamicModelFactory; |
40 | 42 | import org.eclipse.rdf4j.model.impl.SimpleValueFactory; |
41 | 43 | import org.eclipse.rdf4j.model.util.Literals; |
| 44 | +import org.eclipse.rdf4j.model.vocabulary.RDF4J; |
| 45 | +import org.eclipse.rdf4j.model.vocabulary.SESAME; |
42 | 46 | import org.eclipse.rdf4j.query.BindingSet; |
43 | 47 | import org.eclipse.rdf4j.query.BooleanQuery; |
44 | 48 | import org.eclipse.rdf4j.query.GraphQuery; |
|
79 | 83 | */ |
80 | 84 | public class SPARQLConnection extends AbstractRepositoryConnection implements HttpClientDependent { |
81 | 85 |
|
| 86 | + private static final String COUNT_EVERYTHING = "SELECT (COUNT(*) AS ?count) WHERE { ?s ?p ?o }"; |
| 87 | + |
82 | 88 | private static final String EVERYTHING = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }"; |
83 | 89 |
|
84 | 90 | private static final String EVERYTHING_WITH_GRAPH = "SELECT * WHERE { ?s ?p ?o . OPTIONAL { GRAPH ?ctx { ?s ?p ?o } } }"; |
@@ -281,16 +287,61 @@ public boolean isEmpty() throws RepositoryException { |
281 | 287 |
|
282 | 288 | @Override |
283 | 289 | public long size(Resource... contexts) throws RepositoryException { |
284 | | - try (RepositoryResult<Statement> stmts = getStatements(null, null, null, true, contexts)) { |
285 | | - long i = 0; |
286 | | - while (stmts.hasNext()) { |
287 | | - stmts.next(); |
288 | | - i++; |
| 290 | + String query = sizeAsTupleQuery(contexts); |
| 291 | + TupleQuery tq = prepareTupleQuery(SPARQL, query); |
| 292 | + try (TupleQueryResult res = tq.evaluate()) { |
| 293 | + if (res.hasNext()) { |
| 294 | + |
| 295 | + Value value = res.next().getBinding("count").getValue(); |
| 296 | + if (value instanceof Literal) { |
| 297 | + return ((Literal) value).longValue(); |
| 298 | + } else { |
| 299 | + return 0; |
| 300 | + } |
| 301 | + } |
| 302 | + } catch (QueryEvaluationException e) { |
| 303 | + throw new RepositoryException(e); |
| 304 | + } |
| 305 | + return 0; |
| 306 | + } |
| 307 | + |
| 308 | + String sizeAsTupleQuery(Resource... contexts) { |
| 309 | + |
| 310 | + // in case the context is null we want the |
| 311 | + // default graph of the remote store i.e. ask without graph/from. |
| 312 | + if (contexts != null && isQuadMode() && contexts.length > 0) { |
| 313 | + // this is an optimization for the case that we can use a GRAPH instead of a FROM. |
| 314 | + if (contexts.length == 1 && isExposableGraphIri(contexts[0])) { |
| 315 | + return "SELECT (COUNT(*) AS ?count) WHERE { GRAPH <" + contexts[0].stringValue() |
| 316 | + + "> { ?s ?p ?o}}"; |
| 317 | + } else { |
| 318 | + // If we had an default graph setting that is sesame/rdf4j specific |
| 319 | + // we must drop it before sending it over the wire. Otherwise |
| 320 | + // gather up the given contexts and send them as a from clauses |
| 321 | + // to make the matching dataset. |
| 322 | + String graphs = Arrays.stream(contexts) |
| 323 | + .filter(SPARQLConnection::isExposableGraphIri) |
| 324 | + .map(Resource::stringValue) |
| 325 | + .map(s -> "FROM <" + s + ">") |
| 326 | + .collect(Collectors.joining(" ")); |
| 327 | + return "SELECT (COUNT(*) AS ?count) " + graphs + "WHERE { ?s ?p ?o}"; |
289 | 328 | } |
290 | | - return i; |
| 329 | + } else { |
| 330 | + return COUNT_EVERYTHING; |
291 | 331 | } |
292 | 332 | } |
293 | 333 |
|
| 334 | + /** |
| 335 | + * For the sparql protocol a context must be an IRI However we can't send out the RDF4j internal default graph IRIs |
| 336 | + * |
| 337 | + * @param resource to test if it can be the IRI for a named graph |
| 338 | + * @return true if it the input can be a foreign named graph. |
| 339 | + */ |
| 340 | + private static boolean isExposableGraphIri(Resource resource) { |
| 341 | + // We use the instanceof test to avoid any issue with a null pointer. |
| 342 | + return resource instanceof IRI && !RDF4J.NIL.equals(resource) && !SESAME.NIL.equals(resource); |
| 343 | + } |
| 344 | + |
294 | 345 | @Override |
295 | 346 | public RepositoryResult<Statement> getStatements(Resource subj, IRI pred, Value obj, boolean includeInferred, |
296 | 347 | Resource... contexts) throws RepositoryException { |
|
0 commit comments