diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/annotations/IndexingOptions.java b/redis-om-spring/src/main/java/com/redis/om/spring/annotations/IndexingOptions.java index 9f299b99..893cb4ad 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/annotations/IndexingOptions.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/annotations/IndexingOptions.java @@ -6,5 +6,6 @@ @Retention(RetentionPolicy.RUNTIME) @Target({ ElementType.TYPE }) public @interface IndexingOptions { + String indexName() default ""; IndexCreationMode creationMode() default IndexCreationMode.SKIP_IF_EXIST; } diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/indexing/RediSearchIndexer.java b/redis-om-spring/src/main/java/com/redis/om/spring/indexing/RediSearchIndexer.java index f8ec0eaa..dbb92f5f 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/indexing/RediSearchIndexer.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/indexing/RediSearchIndexer.java @@ -99,10 +99,20 @@ public void createIndexFor(Class cl) { Optional maybeScoreField; try { if (isDocument) { - indexName = document.get().indexName(); + // IndexingOptions overrides Document# + if (maybeIndexingOptions.isPresent()) { + indexName = maybeIndexingOptions.get().indexName(); + } else { + indexName = document.get().indexName(); + } indexName = indexName.isBlank() ? cl.getName() + "Idx" : indexName; } else { - indexName = cl.getName() + "Idx"; + if (maybeIndexingOptions.isPresent()) { + indexName = maybeIndexingOptions.get().indexName(); + indexName = indexName.isBlank() ? cl.getName() + "Idx" : indexName; + } else { + indexName = cl.getName() + "Idx"; + } } logger.info(String.format("Found @%s annotated class: %s", idxType, cl.getName())); diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/BasicRedisHashMappingTest.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/BasicRedisHashMappingTest.java index 6a76ef76..cc58decb 100644 --- a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/BasicRedisHashMappingTest.java +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/BasicRedisHashMappingTest.java @@ -6,6 +6,7 @@ import com.redis.om.spring.fixtures.hash.model.*; import com.redis.om.spring.fixtures.hash.repository.*; import com.redis.om.spring.repository.query.QueryUtils; +import com.redis.om.spring.search.stream.EntityStream; import org.assertj.core.util.Arrays; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; @@ -15,7 +16,10 @@ import org.springframework.data.geo.Distance; import org.springframework.data.geo.Point; import org.springframework.data.redis.connection.RedisGeoCommands.DistanceUnit; +import org.springframework.data.redis.connection.jedis.JedisConnectionFactory; import org.springframework.data.repository.query.FluentQuery.FetchableFluentQuery; +import redis.clients.jedis.JedisPooled; +import redis.clients.jedis.UnifiedJedis; import redis.clients.jedis.search.aggr.AggregationResult; import java.time.Duration; @@ -56,8 +60,22 @@ class BasicRedisHashMappingTest extends AbstractBaseEnhancedRedisTest { @Autowired StudentRepository studentRepository; + @Autowired + CustomIndexHashRepository customIndexHashRepository; + + @Autowired + EntityStream es; + + @Autowired + JedisConnectionFactory jedisConnectionFactory; + + private UnifiedJedis jedis; + @BeforeEach void createTestDataIfNeeded() { + jedis = new JedisPooled(Objects.requireNonNull(jedisConnectionFactory.getPoolConfig()), + jedisConnectionFactory.getHostName(), jedisConnectionFactory.getPort()); + flushSearchIndexFor(Company.class); flushSearchIndexFor(Person.class); john = Person.of("John Cleese", "john.cleese@mp.com", "john"); @@ -90,8 +108,7 @@ void createTestDataIfNeeded() { studentRepository.deleteAll(); List students = new ArrayList<>(); for (int i = 0; i < 10; i++) { - var student = Student.of("Student" + i, i != 2 ? LocalDateTime.now() : LocalDateTime.of(2023, 6, 1, 1, 1, - 1)); + var student = Student.of("Student" + i, i != 2 ? LocalDateTime.now() : LocalDateTime.of(2023, 6, 1, 1, 1, 1)); student.setId((long) i); students.add(student); } @@ -672,12 +689,10 @@ void testFindByPropertyWithAliasWithHyphens() { @Test void testFindByPropertyWithAliasWithHyphensAndOrderBy() { - LocalDateTime beginLocalDateTime = LocalDateTime.of(2023, 1, 1, 1, 1, - 1); - LocalDateTime endLocalDateTime = LocalDateTime.of(2023, 12, 1, 1, 1, - 1); - List result = studentRepository.findByUserNameAndEventTimestampBetweenOrderByEventTimestampAsc("Student2", beginLocalDateTime, - endLocalDateTime); + LocalDateTime beginLocalDateTime = LocalDateTime.of(2023, 1, 1, 1, 1, 1); + LocalDateTime endLocalDateTime = LocalDateTime.of(2023, 12, 1, 1, 1, 1); + List result = studentRepository.findByUserNameAndEventTimestampBetweenOrderByEventTimestampAsc("Student2", + beginLocalDateTime, endLocalDateTime); assertAll( // () -> assertThat(result).hasSize(1), // @@ -687,8 +702,8 @@ void testFindByPropertyWithAliasWithHyphensAndOrderBy() { @Test void testQBEWithAliasWithHyphensAndOrderBy() { - Function, Student> sortFunction = - query -> query.sortBy(Sort.by("Event-Timestamp").descending()).firstValue(); + Function, Student> sortFunction = query -> query.sortBy( + Sort.by("Event-Timestamp").descending()).firstValue(); var matcher = ExampleMatcher.matching().withMatcher("userName", ExampleMatcher.GenericPropertyMatcher::exact); @@ -698,4 +713,32 @@ void testQBEWithAliasWithHyphensAndOrderBy() { Student result = studentRepository.findFirstByPropertyOrderByEventTimestamp(student, matcher, sortFunction); assertThat(result.getUserName()).isEqualTo("Student2"); } + + //customIndexHashRepository + + @Test + void testCustomIndexName() { + // CustomIndexHash is a Hash that has a custom index name defined in the @IndexingOptions annotation + var indices = jedis.ftList(); + assertThat(indices).contains("MyCustomHashIndex"); + } + + @Test + void testFreeFormTextSearchOrderIssue() { + customIndexHashRepository.deleteAll(); + CustomIndexHash redis1 = customIndexHashRepository.save(CustomIndexHash.of("Redis", "wwwabccom")); + CustomIndexHash redis2 = customIndexHashRepository.save(CustomIndexHash.of("Redis", "wwwxyznet")); + CustomIndexHash microsoft1 = customIndexHashRepository.save(CustomIndexHash.of("Microsoft", "wwwabcnet")); + CustomIndexHash microsoft2 = customIndexHashRepository.save(CustomIndexHash.of("Microsoft", "wwwxyzcom")); + + var withFreeTextFirst = es.of(CustomIndexHash.class).filter("*co*").filter(CustomIndexHash$.FIRST.eq("Microsoft")) + .collect(Collectors.toList()); + + var withFreeTextLast = es.of(CustomIndexHash.class).filter(CustomIndexHash$.FIRST.eq("Microsoft")).filter("*co*") + .collect(Collectors.toList()); + + assertAll( // + () -> assertThat(withFreeTextLast).containsExactly(microsoft2), + () -> assertThat(withFreeTextFirst).containsExactly(microsoft2)); + } } diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/model/CustomIndexHash.java b/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/model/CustomIndexHash.java new file mode 100644 index 00000000..01ece8b5 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/model/CustomIndexHash.java @@ -0,0 +1,27 @@ +package com.redis.om.spring.fixtures.hash.model; + +import com.redis.om.spring.annotations.IndexingOptions; +import com.redis.om.spring.annotations.Searchable; +import lombok.*; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +@Data +@RequiredArgsConstructor(staticName = "of") +@AllArgsConstructor(access = AccessLevel.PROTECTED) +@NoArgsConstructor(force = true) +@IndexingOptions(indexName = "MyCustomHashIndex") +@RedisHash("custom_prefix") +public class CustomIndexHash { + @Id + private String id; + + @NonNull + @Searchable(sortable = true) + private String first; + + @NonNull + @Searchable(sortable = true) + private String second; +} + diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/repository/CustomIndexHashRepository.java b/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/repository/CustomIndexHashRepository.java new file mode 100644 index 00000000..4c0a4452 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/fixtures/hash/repository/CustomIndexHashRepository.java @@ -0,0 +1,8 @@ +package com.redis.om.spring.fixtures.hash.repository; + +import com.redis.om.spring.fixtures.hash.model.CustomIndexHash; +import com.redis.om.spring.repository.RedisDocumentRepository; + +@SuppressWarnings("unused") +public interface CustomIndexHashRepository extends RedisDocumentRepository { +}