Skip to content

Commit 6bd8fff

Browse files
GH-4920 SPARQLConnection.size() method should not fetch every statement in the repository. Just send a count query instead.
Signed-off-by: Jerven Bolleman <jerven.bolleman@sib.swiss>
1 parent c259fdf commit 6bd8fff

2 files changed

Lines changed: 70 additions & 6 deletions

File tree

core/repository/sparql/src/main/java/org/eclipse/rdf4j/repository/sparql/SPARQLConnection.java

Lines changed: 36 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@
1717
import java.io.InputStream;
1818
import java.io.Reader;
1919
import java.net.URL;
20+
import java.util.Arrays;
2021
import java.util.Objects;
22+
import java.util.stream.Collectors;
2123

2224
import org.apache.http.client.HttpClient;
2325
import org.eclipse.rdf4j.common.iteration.CloseableIteration;
@@ -80,6 +82,8 @@
8082
*/
8183
public class SPARQLConnection extends AbstractRepositoryConnection implements HttpClientDependent {
8284

85+
private static final String COUNT_EVERYTHING = "SELECT (COUNT(*) AS ?count) WHERE { ?s ?p ?o }";
86+
8387
private static final String EVERYTHING = "CONSTRUCT { ?s ?p ?o } WHERE { ?s ?p ?o }";
8488

8589
private static final String EVERYTHING_WITH_GRAPH = "SELECT * WHERE { ?s ?p ?o . OPTIONAL { GRAPH ?ctx { ?s ?p ?o } } }";
@@ -288,14 +292,40 @@ public boolean isEmpty() throws RepositoryException {
288292

289293
@Override
290294
public long size(Resource... contexts) throws RepositoryException {
291-
try (RepositoryResult<Statement> stmts = getStatements(null, null, null, true, contexts)) {
292-
long i = 0;
293-
while (stmts.hasNext()) {
294-
stmts.next();
295-
i++;
295+
String query = sizeAsTupleQuery(contexts);
296+
TupleQuery tq = prepareTupleQuery(SPARQL, query);
297+
try (TupleQueryResult res = tq.evaluate()) {
298+
if (res.hasNext()) {
299+
300+
Value value = res.next().getBinding("count").getValue();
301+
if (value instanceof Literal) {
302+
return ((Literal) value).longValue();
303+
} else {
304+
return 0;
305+
}
306+
}
307+
} catch (QueryEvaluationException e) {
308+
throw new RepositoryException(e);
309+
}
310+
return 0;
311+
}
312+
313+
String sizeAsTupleQuery(Resource... contexts) {
314+
String query = COUNT_EVERYTHING;
315+
if (contexts != null && isQuadMode()) {
316+
if (contexts.length == 1 && contexts[0].isIRI()) {
317+
query = "SELECT (COUNT(*) AS ?count) WHERE { GRAPH <" + ((IRI) contexts[0]).stringValue()
318+
+ "> { ?s ?p ?o}}";
319+
} else if (contexts.length > 0) {
320+
String graphs = Arrays.stream(contexts)
321+
.filter(Resource::isIRI)
322+
.map(Resource::stringValue)
323+
.map(s -> "FROM <" + s + ">")
324+
.collect(Collectors.joining(" "));
325+
query = "SELECT (COUNT(*) AS ?count) " + graphs + "WHERE { ?s ?p ?o}";
296326
}
297-
return i;
298327
}
328+
return query;
299329
}
300330

301331
@Override

core/repository/sparql/src/test/java/org/eclipse/rdf4j/repository/sparql/SPARQLConnectionTest.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,19 @@
1212

1313
import static org.assertj.core.api.Assertions.assertThat;
1414
import static org.eclipse.rdf4j.model.util.Values.iri;
15+
import static org.junit.jupiter.api.Assertions.assertEquals;
16+
import static org.junit.jupiter.api.Assertions.assertNotNull;
1517
import static org.mockito.ArgumentMatchers.any;
1618
import static org.mockito.ArgumentMatchers.anyBoolean;
1719
import static org.mockito.ArgumentMatchers.anyInt;
20+
import static org.mockito.Mockito.atLeastOnce;
1821
import static org.mockito.Mockito.mock;
1922
import static org.mockito.Mockito.never;
2023
import static org.mockito.Mockito.times;
2124
import static org.mockito.Mockito.verify;
25+
import static org.mockito.Mockito.when;
26+
27+
import java.lang.ref.WeakReference;
2228

2329
import org.eclipse.rdf4j.http.client.SPARQLProtocolSession;
2430
import org.eclipse.rdf4j.model.IRI;
@@ -27,10 +33,18 @@
2733
import org.eclipse.rdf4j.model.vocabulary.FOAF;
2834
import org.eclipse.rdf4j.model.vocabulary.RDF;
2935
import org.eclipse.rdf4j.model.vocabulary.RDFS;
36+
import org.eclipse.rdf4j.query.impl.MapBindingSet;
37+
import org.eclipse.rdf4j.query.impl.SimpleBinding;
38+
import org.eclipse.rdf4j.query.impl.TupleQueryResultBuilder;
39+
import org.eclipse.rdf4j.query.parser.ParsedQuery;
40+
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
41+
import org.eclipse.rdf4j.query.parser.sparql.SPARQLParserFactory;
3042
import org.eclipse.rdf4j.rio.ParserConfig;
3143
import org.junit.jupiter.api.BeforeEach;
3244
import org.junit.jupiter.api.Test;
3345
import org.mockito.ArgumentCaptor;
46+
import org.mockito.Mock;
47+
import org.mockito.invocation.InvocationOnMock;
3448

3549
public class SPARQLConnectionTest {
3650

@@ -100,6 +114,26 @@ public void testAddSingleContextHandling() throws Exception {
100114
assertThat(sparqlUpdate).containsPattern(expectedAddPattern).containsPattern(expectedRemovePattern);
101115
}
102116

117+
@Test
118+
public void testSizeQuery() throws Exception {
119+
120+
String sizeAsTupleQuery = subject.sizeAsTupleQuery();
121+
ParsedQuery query = new SPARQLParserFactory().getParser().parseQuery(sizeAsTupleQuery, "http://example.org/");
122+
assertNotNull(query);
123+
124+
sizeAsTupleQuery = subject.sizeAsTupleQuery(vf.createIRI("urn:g1"));
125+
query = new SPARQLParserFactory().getParser().parseQuery(sizeAsTupleQuery, "http://example.org/");
126+
assertNotNull(query);
127+
128+
sizeAsTupleQuery = subject.sizeAsTupleQuery(vf.createIRI("urn:g1"), vf.createIRI("urn:g2"));
129+
query = new SPARQLParserFactory().getParser().parseQuery(sizeAsTupleQuery, "http://example.org/");
130+
assertNotNull(query);
131+
132+
sizeAsTupleQuery = subject.sizeAsTupleQuery(vf.createIRI("urn:g1"), vf.createBNode());
133+
query = new SPARQLParserFactory().getParser().parseQuery(sizeAsTupleQuery, "http://example.org/");
134+
assertNotNull(query);
135+
}
136+
103137
@Test
104138
public void testAddMultipleContextHandling() throws Exception {
105139
ArgumentCaptor<String> sparqlUpdateCaptor = ArgumentCaptor.forClass(String.class);

0 commit comments

Comments
 (0)