diff --git a/jnosql-communication/jnosql-communication-semistructured/src/main/java/org/eclipse/jnosql/communication/semistructured/CursorExecutor.java b/jnosql-communication/jnosql-communication-semistructured/src/main/java/org/eclipse/jnosql/communication/semistructured/CursorExecutor.java index 9d65af497..44b030f73 100644 --- a/jnosql-communication/jnosql-communication-semistructured/src/main/java/org/eclipse/jnosql/communication/semistructured/CursorExecutor.java +++ b/jnosql-communication/jnosql-communication-semistructured/src/main/java/org/eclipse/jnosql/communication/semistructured/CursorExecutor.java @@ -19,6 +19,7 @@ import jakarta.data.page.PageRequest; import jakarta.data.page.impl.CursoredPageRecord; import org.eclipse.jnosql.communication.CommunicationException; +import org.eclipse.jnosql.communication.Condition; import java.util.ArrayList; import java.util.Collections; @@ -48,20 +49,51 @@ public CursoredPage cursor(SelectQuery query, PageRequest> sorts, CommunicationEntity entity) { - List keys = new ArrayList<>(sorts.size()); - for (Sort sort : sorts) { - String name = sort.property(); - Element element = entity.find(name) - .orElseThrow(() -> new CommunicationException("The sort name does not exist in the entity: " + name)); - keys.add(element.get()); - } - return PageRequest.Cursor.forKey(keys.toArray()); - } + }, CURSOR_NEXT { + @SuppressWarnings("unchecked") @Override public CursoredPage cursor(SelectQuery query, PageRequest pageRequest, DatabaseManager template) { - return null; + + + PageRequest.Cursor cursor = pageRequest.cursor().orElseThrow(); + CriteriaCondition condition = getCriteriaCondition(query, cursor); + + var select = new DefaultSelectQuery(pageRequest.size(), 0, query.name(), query.columns(), query.sorts(), + query.condition().map(c -> c.and(condition)).orElse(condition)); + + var entities = template.select(select).toList(); + var last = entities.isEmpty() ? null : entities.get(entities.size() - 1); + if (last == null) { + return new CursoredPageRecord<>(entities, Collections.emptyList(), -1, (PageRequest) pageRequest, + null, null); + } else { + PageRequest.Cursor nextCursor = getCursor(query.sorts(), last); + PageRequest afterCursor = PageRequest.ofSize(pageRequest.size()) + .afterCursor(nextCursor); + + return new CursoredPageRecord<>(entities, List.of(cursor, nextCursor), -1, (PageRequest) + pageRequest, afterCursor, null); + } + } + + static CriteriaCondition getCriteriaCondition(SelectQuery query, PageRequest.Cursor cursor) { + CriteriaCondition condition = null; + CriteriaCondition previousCondition = null; + List> sorts = query.sorts(); + for (int index = 0; index < sorts.size(); index++) { + Sort sort = sorts.get(index); + Object key = cursor.get(index); + if(condition == null) { + condition = CriteriaCondition.gt(sort.property(), key); + previousCondition = CriteriaCondition.eq(sort.property(), key); + } else { + condition = condition.or(previousCondition.and(CriteriaCondition.gt(sort.property(), key))); + previousCondition = previousCondition.and(CriteriaCondition.eq(sort.property(), key)); + } + + } + return condition; } }, CURSOR_PREVIOUS { @Override @@ -82,4 +114,15 @@ public static CursorExecutor of(PageRequest.Mode value) { }; } + + private static PageRequest.Cursor getCursor(List> sorts, CommunicationEntity entity) { + List keys = new ArrayList<>(sorts.size()); + for (Sort sort : sorts) { + String name = sort.property(); + Element element = entity.find(name) + .orElseThrow(() -> new CommunicationException("The sort name does not exist in the entity: " + name)); + keys.add(element.get()); + } + return PageRequest.Cursor.forKey(keys.toArray()); + } } diff --git a/jnosql-communication/jnosql-communication-semistructured/src/test/java/org/eclipse/jnosql/communication/semistructured/DatabaseManagerTest.java b/jnosql-communication/jnosql-communication-semistructured/src/test/java/org/eclipse/jnosql/communication/semistructured/DatabaseManagerTest.java index 27d8b510c..06d04ecf6 100644 --- a/jnosql-communication/jnosql-communication-semistructured/src/test/java/org/eclipse/jnosql/communication/semistructured/DatabaseManagerTest.java +++ b/jnosql-communication/jnosql-communication-semistructured/src/test/java/org/eclipse/jnosql/communication/semistructured/DatabaseManagerTest.java @@ -14,16 +14,21 @@ import jakarta.data.page.CursoredPage; import jakarta.data.page.PageRequest; import org.assertj.core.api.SoftAssertions; +import org.eclipse.jnosql.communication.Condition; +import org.eclipse.jnosql.communication.TypeReference; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Answers; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.jupiter.MockitoExtension; +import java.util.List; import java.util.UUID; import java.util.stream.Stream; +import static org.assertj.core.api.SoftAssertions.assertSoftly; import static org.junit.jupiter.api.Assertions.*; @ExtendWith(MockitoExtension.class) @@ -44,22 +49,13 @@ void shouldReturnPaginationOffSet() { SelectQuery query = SelectQuery.select().from("person") .orderBy("name").asc().build(); - var entity = CommunicationEntity.of("name"); - entity.add("name", "Ada"); - entity.add("age", 10); - entity.add("id", UUID.randomUUID().toString()); - - var entity2 = CommunicationEntity.of("name"); - entity2.add("name", "Poliana"); - entity2.add("age", 35); - entity2.add("id", UUID.randomUUID().toString()); - Mockito.when(databaseManager.select(Mockito.any(SelectQuery.class))) - .thenReturn(Stream.of(entity, entity2)); + .thenReturn(stream()); + CursoredPage entities = databaseManager.selectCursor(query, PageRequest.ofSize(10)); - SoftAssertions.assertSoftly(soft -> { + assertSoftly(soft -> { PageRequest pageRequest = entities.pageRequest(); PageRequest nextedPageRequest = entities.nextPageRequest(); PageRequest.Cursor cursor = nextedPageRequest.cursor().orElseThrow(); @@ -82,22 +78,13 @@ void shouldReturnPaginationOffSet2() { .orderBy("name").asc() .orderBy("age").desc().build(); - var entity = CommunicationEntity.of("name"); - entity.add("name", "Ada"); - entity.add("age", 10); - entity.add("id", UUID.randomUUID().toString()); - - var entity2 = CommunicationEntity.of("name"); - entity2.add("name", "Poliana"); - entity2.add("age", 35); - entity2.add("id", UUID.randomUUID().toString()); Mockito.when(databaseManager.select(Mockito.any(SelectQuery.class))) - .thenReturn(Stream.of(entity, entity2)); + .thenReturn(stream()); CursoredPage entities = databaseManager.selectCursor(query, PageRequest.ofSize(10)); - SoftAssertions.assertSoftly(soft -> { + assertSoftly(soft -> { PageRequest pageRequest = entities.pageRequest(); PageRequest nextedPageRequest = entities.nextPageRequest(); PageRequest.Cursor cursor = nextedPageRequest.cursor().orElseThrow(); @@ -115,4 +102,71 @@ void shouldReturnPaginationOffSet2() { }); } + @Test + void shouldReturnPaginationAfterKeySingleElementWhenConditionIsNull() { + SelectQuery query = SelectQuery.select().from("person") + .orderBy("name").asc() + .orderBy("age").asc() + .orderBy("id").asc().build(); + + Mockito.when(databaseManager.select(Mockito.any(SelectQuery.class))) + .thenReturn(stream()); + + var id = UUID.randomUUID().toString(); + CursoredPage entities = databaseManager.selectCursor(query, + PageRequest.ofSize(10).afterKey("Ada", 20, id)); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SelectQuery.class); + Mockito.verify(databaseManager).select(captor.capture()); + SelectQuery selectQuery = captor.getValue(); + + CriteriaCondition condition = selectQuery.condition().orElseThrow(); + + assertSoftly(soft -> { + soft.assertThat(condition.condition()).isEqualTo(Condition.OR); + List criteriaConditions = condition.element().get(new TypeReference<>() { + }); + + soft.assertThat(criteriaConditions).hasSize(3); + soft.assertThat(criteriaConditions.get(0)).isEqualTo(CriteriaCondition.gt("name", "Ada")); + soft.assertThat(criteriaConditions.get(1)).isEqualTo( + CriteriaCondition.eq("name", "Ada").and(CriteriaCondition.gt("age", 20))); + soft.assertThat(criteriaConditions.get(2)).isEqualTo( + CriteriaCondition.eq("name", "Ada").and(CriteriaCondition.eq("age", 20)) + .and(CriteriaCondition.gt("id", id))); + }); + + assertSoftly(soft -> { + PageRequest pageRequest = entities.pageRequest(); + PageRequest nextedPageRequest = entities.nextPageRequest(); + PageRequest.Cursor cursor = nextedPageRequest.cursor().orElseThrow(); + + soft.assertThat(entities).hasSize(2); + soft.assertThat(pageRequest.mode()) + .isEqualTo(PageRequest.Mode.CURSOR_NEXT); + soft.assertThat(nextedPageRequest.mode()) + .isEqualTo(PageRequest.Mode.CURSOR_NEXT); + soft.assertThat(cursor.elements()) + .hasSize(3); + soft.assertThat(cursor.get(0)).isEqualTo("Poliana"); + soft.assertThat(cursor.get(1)).isEqualTo(35); + soft.assertThat(cursor.get(2)).isNotNull(); + + }); + } + + private Stream stream() { + var entity = CommunicationEntity.of("name"); + entity.add("name", "Ada"); + entity.add("age", 10); + entity.add("id", UUID.randomUUID().toString()); + + var entity2 = CommunicationEntity.of("name"); + entity2.add("name", "Poliana"); + entity2.add("age", 35); + entity2.add("id", UUID.randomUUID().toString()); + return Stream.of(entity, entity2); + } + + } \ No newline at end of file