From efec4c396aa7a6f3751a3e65498c24ba0562b438 Mon Sep 17 00:00:00 2001 From: "maor.ohana" Date: Fri, 3 May 2024 22:47:44 +0300 Subject: [PATCH] Support interface based projections --- .../repository/query/RediSearchQuery.java | 29 +++++---- .../spring/repository/DocumentProjection.java | 11 ++++ .../repository/DocumentProjectionPojo.java | 30 +++++++++ .../DocumentProjectionRepository.java | 9 +++ .../repository/DocumentProjectionTest.java | 64 +++++++++++++++++++ 5 files changed, 131 insertions(+), 12 deletions(-) create mode 100644 redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjection.java create mode 100644 redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionPojo.java create mode 100644 redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionRepository.java create mode 100644 redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionTest.java diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RediSearchQuery.java b/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RediSearchQuery.java index ca4af27b..60874918 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RediSearchQuery.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RediSearchQuery.java @@ -21,10 +21,7 @@ import org.springframework.data.keyvalue.core.KeyValueOperations; import org.springframework.data.mapping.PropertyPath; import org.springframework.data.repository.core.RepositoryMetadata; -import org.springframework.data.repository.query.Parameter; -import org.springframework.data.repository.query.QueryMethod; -import org.springframework.data.repository.query.QueryMethodEvaluationContextProvider; -import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.data.repository.query.*; import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.PartTree; @@ -49,9 +46,6 @@ import java.util.stream.Stream; import java.util.stream.StreamSupport; -import static com.redis.om.spring.util.ObjectUtils.getIdFieldForEntityClass; -import static com.redis.om.spring.util.ObjectUtils.getValueForField; - public class RediSearchQuery implements RepositoryQuery { private static final Log logger = LogFactory.getLog(RediSearchQuery.class); @@ -378,6 +372,12 @@ else if (fieldType == Point.class) { @Override public Object execute(Object[] parameters) { + ParameterAccessor accessor = new ParametersParameterAccessor(this.getQueryMethod().getParameters(), parameters); + ResultProcessor processor = this.queryMethod.getResultProcessor().withDynamicProjection(accessor); + return processor.processResult(this.doExecute(parameters)); + } + + public Object doExecute(Object[] parameters) { Optional maybeBloomFilter = bloomQueryExecutor.getBloomFilter(); Optional maybeCuckooFilter = cuckooQueryExecutor.getCuckooFilter(); @@ -477,7 +477,7 @@ private Object executeQuery(Object[] parameters) { } else if (queryMethod.isPageQuery()) { Gson gson = getGson(); List content = searchResult.getDocuments().stream() - .map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), queryMethod.getReturnedObjectType())) + .map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), domainType)) .collect(Collectors.toList()); if (maybePageable.isPresent()) { @@ -485,24 +485,29 @@ private Object executeQuery(Object[] parameters) { result = new PageImpl<>(content, pageable, searchResult.getTotalResults()); } - } else if (queryMethod.isQueryForEntity() && !queryMethod.isCollectionQuery()) { + } else if (!queryMethod.isCollectionQuery()) { // handle the case where we have a single entity result and we the query results are empty if (!searchResult.getDocuments().isEmpty()) { Gson gson = getGson(); Document doc = searchResult.getDocuments().get(0); Object json = doc != null ? SafeEncoder.encode((byte[])doc.get("$")) : ""; - result = gson.fromJson(json.toString(), queryMethod.getReturnedObjectType()); + result = gson.fromJson(json.toString(), domainType); } - } else if ((queryMethod.isQueryForEntity() && queryMethod.isCollectionQuery()) || this.type == RediSearchQueryType.DELETE) { + } else if ((queryMethod.isCollectionQuery()) || this.type == RediSearchQueryType.DELETE) { Gson gson = getGson(); result = searchResult.getDocuments().stream() - .map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), queryMethod.getReturnedObjectType())) + .map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), domainType)) .collect(Collectors.toList()); } return result; } + static class Company { + public String url; + public String name; + } + private Object executeDeleteQuery(Object[] parameters) { SearchOperations ops = modulesOperations.opsForSearch(searchIndex); String baseQuery = prepareQuery(parameters, true); diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjection.java b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjection.java new file mode 100644 index 00000000..663529a1 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjection.java @@ -0,0 +1,11 @@ +package com.redis.om.spring.repository; + +public interface DocumentProjection { + String getName(); + RecursiveSummary getRecursiveProjection(); + + interface RecursiveSummary { + String getRecursiveProp1(); + } + +} diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionPojo.java b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionPojo.java new file mode 100644 index 00000000..8afd8dca --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionPojo.java @@ -0,0 +1,30 @@ +package com.redis.om.spring.repository; + +import com.redis.om.spring.annotations.Document; +import lombok.Data; +import org.springframework.data.annotation.Id; + +import java.util.UUID; + +@Document +@Data +public class DocumentProjectionPojo { + + @Id + private String id; + + private String name; + + private RecursiveProjection recursiveProjection; + + public DocumentProjectionPojo() { + this.id = UUID.randomUUID().toString(); + } + + + @Data + static class RecursiveProjection { + private String recursiveProp1; + private String recursiveProp2; + } +} diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionRepository.java b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionRepository.java new file mode 100644 index 00000000..d5c08002 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionRepository.java @@ -0,0 +1,9 @@ +package com.redis.om.spring.repository; + +import java.util.Collection; +import java.util.Optional; + +public interface DocumentProjectionRepository extends RedisDocumentRepository { + Optional findByName(String name); + Collection findAllByName(String name); +} diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionTest.java b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionTest.java new file mode 100644 index 00000000..136c2516 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/repository/DocumentProjectionTest.java @@ -0,0 +1,64 @@ +package com.redis.om.spring.repository; + +import com.redis.om.spring.AbstractBaseDocumentTest; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import java.util.Collection; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; + +class DocumentProjectionTest extends AbstractBaseDocumentTest { + + public static final String TEST_NAME = "testName"; + public static final String TEST_REC_1 = "test1"; + public static final String TEST_REC_2 = "test2"; + private final DocumentProjectionRepository documentProjectionRepository; + + @Autowired + DocumentProjectionTest(DocumentProjectionRepository documentProjectionRepository) { + this.documentProjectionRepository = documentProjectionRepository; + } + + @BeforeEach + void setUp() { + for (int i = 0; i < 2; i++) { + DocumentProjectionPojo entity = new DocumentProjectionPojo(); + entity.setName(TEST_NAME); + DocumentProjectionPojo.RecursiveProjection recursiveProjection = new DocumentProjectionPojo.RecursiveProjection(); + recursiveProjection.setRecursiveProp1(TEST_REC_1); + recursiveProjection.setRecursiveProp2(TEST_REC_2); + entity.setRecursiveProjection(recursiveProjection); + documentProjectionRepository.save(entity); + } + } + + @Test + void testEntityProjection() { + Optional byNameProjection = documentProjectionRepository.findByName(TEST_NAME); + assertTrue(byNameProjection.isPresent()); + assertEquals(TEST_NAME, byNameProjection.get().getName()); + assertNotNull(byNameProjection.get().getRecursiveProjection()); + assertEquals(TEST_REC_1, byNameProjection.get().getRecursiveProjection().getRecursiveProp1()); + } + + @Test + void testCollectionProjection() { + Collection byNameProjection = documentProjectionRepository.findAllByName(TEST_NAME); + assertNotNull(byNameProjection); + assertEquals(2, byNameProjection.size()); + byNameProjection.forEach(documentProjection -> { + assertNotNull(documentProjection.getName()); + assertNotNull(documentProjection.getRecursiveProjection()); + }); + } + + @AfterEach + void tearDown() { + documentProjectionRepository.deleteAll(); + } + +}