Skip to content

Commit 64a5049

Browse files
authored
Merge pull request #349 from eclipse-jnosql/backport-optimized-count-queries
[Backport] Add optimized count query methods to database managers (1.1.x)
2 parents 89c69be + 1bc98a4 commit 64a5049

File tree

17 files changed

+262
-52
lines changed

17 files changed

+262
-52
lines changed

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/DefaultArangoDBDocumentManager.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,14 @@ public long count(String documentCollection) {
154154
.map(Number::longValue).orElse(0L);
155155
}
156156

157+
@Override
158+
public long count(SelectQuery query) {
159+
requireNonNull(query, "query is required");
160+
checkCollection(query.name());
161+
AQLQueryResult aqlQuery = QueryAQLConverter.count(query);
162+
LOGGER.finest("Executing AQL: " + aqlQuery.query());
163+
return aql(aqlQuery.query(), aqlQuery.values(), Long.class).findFirst().orElse(0L);
164+
}
157165

158166
@Override
159167
public Stream<CommunicationEntity> aql(String query, Map<String, Object> params) throws NullPointerException {

jnosql-arangodb/src/main/java/org/eclipse/jnosql/databases/arangodb/communication/QueryAQLConverter.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,23 @@ public static AQLQueryResult select(SelectQuery query) throws NullPointerExcepti
7777

7878
}
7979

80+
public static AQLQueryResult count(SelectQuery query) throws NullPointerException {
81+
82+
AQLQueryResult q = convert(query.name(),
83+
query.condition().orElse(null),
84+
Collections.emptyList(),
85+
0L,
86+
0L,
87+
RETURN, false);
88+
89+
StringBuilder aql = new StringBuilder();
90+
91+
aql.append("RETURN LENGTH(")
92+
.append(q.query())
93+
.append(")");
94+
return new AQLQueryResult(aql.toString(), q.values());
95+
}
96+
8097

8198
private static AQLQueryResult convert(String documentCollection,
8299
CriteriaCondition documentCondition,

jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/ArangoDBDocumentManagerTest.java

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static java.util.Arrays.asList;
4545
import static java.util.Collections.singletonMap;
4646
import static org.assertj.core.api.Assertions.assertThat;
47+
import static org.assertj.core.api.SoftAssertions.assertSoftly;
4748
import static org.eclipse.jnosql.communication.driver.IntegrationTest.MATCHES;
4849
import static org.eclipse.jnosql.communication.driver.IntegrationTest.NAMED;
4950
import static org.eclipse.jnosql.communication.semistructured.DeleteQuery.delete;
@@ -223,6 +224,22 @@ void shouldCount() {
223224
assertTrue(entityManager.count(COLLECTION_NAME) > 0);
224225
}
225226

227+
@Test
228+
void shouldCountWithSelectQuery() {
229+
CommunicationEntity entity = entityManager.insert(createDocumentListNotHavingId());
230+
Element key = entity.find(KEY_NAME).get();
231+
SelectQuery query = select().from("AppointmentBook").where(key.name()).eq(key.get()).build();
232+
233+
assertSoftly(softly -> {
234+
softly.assertThat(entityManager.count(query))
235+
.as("should count documents matching the query")
236+
.isEqualTo(1L);
237+
softly.assertThatThrownBy(() -> entityManager.count((SelectQuery) null))
238+
.as("must not accept null query")
239+
.isInstanceOf(NullPointerException.class);
240+
});
241+
}
242+
226243
@Test
227244
void shouldReadFromDifferentBaseDocumentUsingInstance() {
228245
entityManager.insert(getEntity());
@@ -271,7 +288,7 @@ void shouldInsertNull() {
271288
entity.add(Element.of("name", null));
272289
CommunicationEntity documentEntity = entityManager.insert(entity);
273290
Optional<Element> name = documentEntity.find("name");
274-
SoftAssertions.assertSoftly(soft -> {
291+
assertSoftly(soft -> {
275292
soft.assertThat(name).isPresent();
276293
soft.assertThat(name).get().extracting(Element::name).isEqualTo("name");
277294
soft.assertThat(name).get().extracting(Element::get).isNull();
@@ -284,7 +301,7 @@ void shouldUpdateNull(){
284301
entity.add(Element.of("name", null));
285302
var documentEntity = entityManager.update(entity);
286303
Optional<Element> name = documentEntity.find("name");
287-
SoftAssertions.assertSoftly(soft -> {
304+
assertSoftly(soft -> {
288305
soft.assertThat(name).isPresent();
289306
soft.assertThat(name).get().extracting(Element::name).isEqualTo("name");
290307
soft.assertThat(name).get().extracting(Element::get).isNull();
@@ -359,7 +376,7 @@ void shouldInsertUUID() {
359376
entity.add("uuid", UUID.randomUUID());
360377
var documentEntity = entityManager.insert(entity);
361378
Optional<Element> uuid = documentEntity.find("uuid");
362-
SoftAssertions.assertSoftly(soft -> {
379+
assertSoftly(soft -> {
363380
soft.assertThat(uuid).isPresent();
364381
Element element = uuid.orElseThrow();
365382
soft.assertThat(element.name()).isEqualTo("uuid");
@@ -380,7 +397,7 @@ void shouldFindBetween() {
380397

381398
var result = entityManager.select(query).toList();
382399

383-
SoftAssertions.assertSoftly(softly -> {
400+
assertSoftly(softly -> {
384401
softly.assertThat(result).hasSize(2);
385402
softly.assertThat(result).map(e -> e.find("age").orElseThrow().get(Integer.class)).contains(22, 23);
386403
softly.assertThat(result).map(e -> e.find("age").orElseThrow().get(Integer.class)).doesNotContain(25);
@@ -400,7 +417,7 @@ void shouldFindBetween2() {
400417

401418
var result = entityManager.select(query).toList();
402419

403-
SoftAssertions.assertSoftly(softly -> {
420+
assertSoftly(softly -> {
404421
softly.assertThat(result).hasSize(2);
405422
softly.assertThat(result).map(e -> e.find("age").orElseThrow().get(Integer.class)).contains(22, 23);
406423
softly.assertThat(result).map(e -> e.find("age").orElseThrow().get(Integer.class)).doesNotContain(25);
@@ -416,7 +433,7 @@ void shouldFindContains() {
416433
"lia")), COLLECTION_NAME, Collections.emptyList());
417434

418435
var result = entityManager.select(query).toList();
419-
SoftAssertions.assertSoftly(softly -> {
436+
assertSoftly(softly -> {
420437
softly.assertThat(result).hasSize(1);
421438
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
422439
});
@@ -431,7 +448,7 @@ void shouldStartsWith() {
431448
"Pol")), COLLECTION_NAME, Collections.emptyList());
432449

433450
var result = entityManager.select(query).toList();
434-
SoftAssertions.assertSoftly(softly -> {
451+
assertSoftly(softly -> {
435452
softly.assertThat(result).hasSize(1);
436453
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
437454
});
@@ -446,7 +463,7 @@ void shouldEndsWith() {
446463
"ana")), COLLECTION_NAME, Collections.emptyList());
447464

448465
var result = entityManager.select(query).toList();
449-
SoftAssertions.assertSoftly(softly -> {
466+
assertSoftly(softly -> {
450467
softly.assertThat(result).hasSize(1);
451468
softly.assertThat(result.get(0).find("name").orElseThrow().get(String.class)).isEqualTo("Poliana");
452469
});
@@ -483,7 +500,6 @@ private CommunicationEntity createDocumentList() {
483500
}
484501

485502
private CommunicationEntity createDocumentListNotHavingId() {
486-
String id = UUID.randomUUID().toString();
487503
CommunicationEntity entity = CommunicationEntity.of("AppointmentBook");
488504
entity.add(Element.of("_id", "ids"));
489505
List<List<Element>> documents = new ArrayList<>();

jnosql-arangodb/src/test/java/org/eclipse/jnosql/databases/arangodb/communication/QueryAQLConverterTest.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,4 +155,18 @@ public void shouldNegate() {
155155

156156
}
157157

158+
@Test
159+
public void shouldRunEqualsCountQuery() {
160+
SelectQuery query = select().from("collection")
161+
.where("name").eq("value").build();
162+
163+
AQLQueryResult convert = QueryAQLConverter.count(query);
164+
String aql = convert.query();
165+
Map<String, Object> values = convert.values();
166+
assertEquals("value", values.get("name"));
167+
assertEquals("RETURN LENGTH(FOR c IN collection FILTER c.name == @name RETURN c)", aql);
168+
169+
}
170+
171+
158172
}

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/communication/CassandraColumnManager.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,17 @@ public interface CassandraColumnManager extends DatabaseManager {
101101
*/
102102
Stream<CommunicationEntity> select(SelectQuery query, ConsistencyLevel level) throws NullPointerException;
103103

104+
/**
105+
* Count based on a query using a consistency level
106+
*
107+
* @param query the query
108+
* @param level the consistency level
109+
* @return the query using a consistency level
110+
* @throws NullPointerException when either query or level are null
111+
*/
112+
long count(SelectQuery query, ConsistencyLevel level) throws NullPointerException;
113+
114+
104115
/**
105116
* Executes CQL
106117
*

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/communication/DefaultCassandraColumnManager.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,20 @@ public long count(String columnFamily) {
159159
return execute.one().getLong(0);
160160
}
161161

162+
@Override
163+
public long count(SelectQuery query) {
164+
requireNonNull(query, "query is required");
165+
QueryExecutor executor = QueryExecutor.of(query);
166+
return executor.count(keyspace, query, this);
167+
}
168+
169+
@Override
170+
public long count(SelectQuery query, ConsistencyLevel level) {
171+
requireNonNull(query, "query is required");
172+
requireNonNull(level, "level is required");
173+
QueryExecutor executor = QueryExecutor.of(query);
174+
return executor.count(keyspace, query, level, this);
175+
}
162176

163177
@Override
164178
public void close() {

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/communication/QueryExecutor.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,17 @@ static QueryExecutor of(SelectQuery query) {
2929
return QueryExecutorType.DEFAULT;
3030
}
3131

32-
Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, DefaultCassandraColumnManager manager);
32+
default Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, DefaultCassandraColumnManager manager) {
33+
return execute(keyspace, query, null, manager);
34+
}
3335

3436
Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, ConsistencyLevel level,
35-
DefaultCassandraColumnManager manager);
37+
DefaultCassandraColumnManager manager);
38+
39+
default long count(String keyspace, SelectQuery query, DefaultCassandraColumnManager manager) {
40+
return count(keyspace, query, null, manager);
41+
}
42+
43+
long count(String keyspace, SelectQuery query, ConsistencyLevel level, DefaultCassandraColumnManager manager);
3644

3745
}

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/communication/QueryExecutorType.java

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,9 @@
3030

3131
enum QueryExecutorType implements QueryExecutor {
3232
PAGING_STATE {
33-
@Override
34-
public Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, DefaultCassandraColumnManager manager) {
35-
return execute(keyspace, query, null, manager);
36-
}
37-
3833
@Override
3934
public Stream<CommunicationEntity> execute(String keyspace, SelectQuery q, ConsistencyLevel level,
40-
DefaultCassandraColumnManager manager) {
35+
DefaultCassandraColumnManager manager) {
4136

4237
CassandraQuery query = CassandraQuery.class.cast(q);
4338

@@ -73,14 +68,9 @@ public Stream<CommunicationEntity> execute(String keyspace, SelectQuery q, Consi
7368

7469
},
7570
DEFAULT {
76-
@Override
77-
public Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, DefaultCassandraColumnManager manager) {
78-
return execute(keyspace, query, null, manager);
79-
}
80-
8171
@Override
8272
public Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, ConsistencyLevel level,
83-
DefaultCassandraColumnManager manager) {
73+
DefaultCassandraColumnManager manager) {
8474

8575
Select cassandraSelect = QueryUtils.select(query, keyspace);
8676

@@ -98,5 +88,17 @@ public Stream<CommunicationEntity> execute(String keyspace, SelectQuery query, C
9888
}
9989
return resultSet.all().stream().map(CassandraConverter::toDocumentEntity);
10090
}
91+
};
92+
93+
@Override
94+
public long count(String keyspace, SelectQuery query, ConsistencyLevel level, DefaultCassandraColumnManager manager) {
95+
96+
Select cassandraSelect = QueryUtils.select(query, keyspace).countAll();
97+
SimpleStatement select = cassandraSelect.build();
98+
if (Objects.nonNull(level)) {
99+
select = select.setConsistencyLevel(level);
100+
}
101+
ResultSet resultSet = manager.getSession().execute(select);
102+
return resultSet.one().getLong(0);
101103
}
102104
}

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/mapping/CassandraTemplate.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,17 @@ public interface CassandraTemplate extends ColumnTemplate {
8787
*/
8888
void delete(DeleteQuery query, ConsistencyLevel level);
8989

90+
/**
91+
* Returns the count of records that match the given {@link SelectQuery} using the specified
92+
* {@link ConsistencyLevel}.
93+
*
94+
* @param query the select query defining the criteria for counting; must not be {@code null}
95+
* @param level the Cassandra consistency level to use for the operation; must not be {@code null}
96+
* @return the number of records matching the query
97+
* @throws NullPointerException if {@code query} or {@code level} is {@code null}
98+
*/
99+
long count(SelectQuery query, ConsistencyLevel level);
100+
90101
/**
91102
* Executes a {@link SelectQuery} using a specified {@link ConsistencyLevel} and retrieves the matching records.
92103
*

jnosql-cassandra/src/main/java/org/eclipse/jnosql/databases/cassandra/mapping/DefaultCassandraTemplate.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,13 @@ public void delete(DeleteQuery query, ConsistencyLevel level) {
147147
manager.get().delete(query, level);
148148
}
149149

150+
@Override
151+
public long count(SelectQuery query, ConsistencyLevel level) {
152+
Objects.requireNonNull(query, "query is required");
153+
Objects.requireNonNull(level, "level is required");
154+
return manager.get().count(query, level);
155+
}
156+
150157
@Override
151158
public <T> Stream<T> find(SelectQuery query, ConsistencyLevel level) {
152159
Objects.requireNonNull(query, "query is required");

0 commit comments

Comments
 (0)