diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/RediSearchIndexer.java b/redis-om-spring/src/main/java/com/redis/om/spring/RediSearchIndexer.java index 8f77f276..257de4c9 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/RediSearchIndexer.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/RediSearchIndexer.java @@ -3,6 +3,7 @@ import com.github.f4b6a3.ulid.Ulid; import com.google.gson.GsonBuilder; import com.google.gson.annotations.JsonAdapter; +import com.google.gson.annotations.SerializedName; import com.redis.om.spring.annotations.*; import com.redis.om.spring.ops.RedisModulesOperations; import com.redis.om.spring.ops.search.SearchOperations; @@ -217,8 +218,8 @@ private List findIndexFields(java.lang.reflect.Field field, String // Also UUID and Ulid (classes whose toString() is a valid text representation // of the value) // - if (CharSequence.class.isAssignableFrom( - fieldType) || (fieldType == Boolean.class) || (fieldType == UUID.class) || (fieldType == Ulid.class)) { + if (CharSequence.class.isAssignableFrom(fieldType) || // + (fieldType == Boolean.class) || (fieldType == UUID.class) || (fieldType == Ulid.class)) { fields.add(indexAsTagFieldFor(field, isDocument, prefix, indexed.sortable(), indexed.separator(), indexed.arrayIndex(), indexed.alias())); } else if (fieldType.isEnum()) { @@ -454,13 +455,15 @@ private VectorField indexAsVectorFieldFor(java.lang.reflect.Field field, boolean private SchemaField indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, boolean sortable, String separator, int arrayIndex, String annotationAlias) { + SerializedName serializedName = field.getAnnotation(SerializedName.class); + String fname = (serializedName != null) ? serializedName.value() : field.getName(); TypeInformation typeInfo = TypeInformation.of(field.getType()); String fieldPrefix = getFieldPrefix(prefix, isDocument); String index = (arrayIndex != Integer.MIN_VALUE) ? ".[" + arrayIndex + "]" : "[*]"; String fieldPostfix = (isDocument && typeInfo.isCollectionLike() && !field.isAnnotationPresent(JsonAdapter.class)) ? index : ""; - String name = fieldPrefix + field.getName() + fieldPostfix; + String name = fieldPrefix + fname + fieldPostfix; String alias = (annotationAlias == null || annotationAlias.isBlank()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : annotationAlias; @@ -536,8 +539,10 @@ private List indexAsNestedFieldFor(java.lang.reflect.Field field, S } private FieldName getFieldName(java.lang.reflect.Field field, boolean isDocument, String prefix, String alias) { + SerializedName serializedName = field.getAnnotation(SerializedName.class); + String fname = (serializedName != null) ? serializedName.value() : field.getName(); String fieldPrefix = getFieldPrefix(prefix, isDocument); - String name = fieldPrefix + field.getName(); + String name = fieldPrefix + fname; FieldName fieldName = FieldName.of(name); fieldName = fieldName.as(alias); return fieldName; @@ -758,9 +763,11 @@ private Optional createIndexedFieldForIdField(Class cl, List createIndexedFieldForReferenceIdField( // java.lang.reflect.Field referenceIdField, // boolean isDocument) { + SerializedName serializedName = referenceIdField.getAnnotation(SerializedName.class); + String fname = (serializedName != null) ? serializedName.value() : referenceIdField.getName(); String fieldPrefix = getFieldPrefix("", isDocument); - FieldName fieldName = FieldName.of(fieldPrefix + referenceIdField.getName()); + FieldName fieldName = FieldName.of(fieldPrefix + fname); fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(referenceIdField, "")); return Optional.of( isDocument ? TagField.of(fieldName).separator('|') : TagField.of(fieldName).separator('|').sortable()); 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 e5f2861a..aab1556d 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 @@ -712,7 +712,7 @@ private String prepareQuery(final Object[] parameters, boolean excludeNullParams if (excludeNullParams && (fieldClauses.getSecond() == QueryClause.IS_NULL || fieldClauses.getSecond() == QueryClause.IS_NOT_NULL)) { return ""; } - String fieldName = fieldClauses.getFirst(); + String fieldName = QueryUtils.escape(fieldClauses.getFirst()); QueryClause queryClause = fieldClauses.getSecond(); int paramsCnt = queryClause.getClauseTemplate().getNumberOfArguments(); diff --git a/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RedisEnhancedQuery.java b/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RedisEnhancedQuery.java index 6f174e84..e643f267 100644 --- a/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RedisEnhancedQuery.java +++ b/redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RedisEnhancedQuery.java @@ -716,7 +716,7 @@ private String prepareQuery(final Object[] parameters, boolean excludeNullParams if (excludeNullParams && (fieldClauses.getSecond() == QueryClause.IS_NULL || fieldClauses.getSecond() == QueryClause.IS_NOT_NULL)) { return ""; } - String fieldName = fieldClauses.getFirst(); + String fieldName = QueryUtils.escape(fieldClauses.getFirst()); QueryClause queryClause = fieldClauses.getSecond(); int paramsCnt = queryClause.getClauseTemplate().getNumberOfArguments(); diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/RepositoryIssuesTest.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/RepositoryIssuesTest.java index 9111719b..71b65b9f 100644 --- a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/RepositoryIssuesTest.java +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/RepositoryIssuesTest.java @@ -1,14 +1,12 @@ package com.redis.om.spring.annotations.document; import com.redis.om.spring.AbstractBaseDocumentTest; -import com.redis.om.spring.annotations.document.fixtures.SKU; -import com.redis.om.spring.annotations.document.fixtures.SKUCacheRepository; -import com.redis.om.spring.annotations.document.fixtures.User2; -import com.redis.om.spring.annotations.document.fixtures.User2Repository; +import com.redis.om.spring.annotations.document.fixtures.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Set; @@ -24,6 +22,9 @@ class RepositoryIssuesTest extends AbstractBaseDocumentTest { @Autowired SKUCacheRepository skuCacheRepository; + @Autowired + StudentRepository studentRepository; + @BeforeEach void cleanUp() { repository.deleteAll(); @@ -37,6 +38,13 @@ void cleanUp() { } skuCacheRepository.saveAll(skuCaches); + studentRepository.deleteAll(); + List students = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + students.add( + Student.of((long) i, "Student" + i, LocalDateTime.now())); + } + studentRepository.saveAll(students); } // RediSearchQuery wrong preparedQuery #187 @@ -76,4 +84,14 @@ void testFindOneBy() { () -> assertThat(result.getSkuNumber()).isEqualTo("A11111") // ); } + + @Test + void testFindByPropertyWithAliasWithHyphens() { + List result = studentRepository.findByUserName("Student2"); + + assertAll( // + () -> assertThat(result).hasSize(1), + () -> assertThat(result).extracting("userName").containsExactly("Student2") // + ); + } } diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/Student.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/Student.java new file mode 100644 index 00000000..fcb515d9 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/Student.java @@ -0,0 +1,33 @@ +package com.redis.om.spring.annotations.document.fixtures; + +import com.google.gson.annotations.SerializedName; +import com.redis.om.spring.annotations.Document; +import com.redis.om.spring.annotations.Indexed; +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.data.annotation.Id; + +import java.time.LocalDateTime; + +@Data +@Builder +@RequiredArgsConstructor(staticName = "of") +@Document +public class Student { + + @Id + @NonNull + private Long id; + + @Indexed(alias = "User-Name")// fieldName = "User-Name", schemaFieldType = SchemaFieldType.TAG + @SerializedName("User-Name") + @NonNull + private String userName; + + @Indexed + @NonNull + private LocalDateTime eventTimestamp; + +} diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/StudentRepository.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/StudentRepository.java new file mode 100644 index 00000000..27c1defa --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/fixtures/StudentRepository.java @@ -0,0 +1,9 @@ +package com.redis.om.spring.annotations.document.fixtures; + +import com.redis.om.spring.repository.RedisDocumentRepository; + +import java.util.List; + +public interface StudentRepository extends RedisDocumentRepository { + List findByUserName(String userName); +} 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 698e4a4f..2d77e0cb 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 @@ -21,6 +21,7 @@ import java.time.Duration; import java.time.LocalDate; +import java.time.LocalDateTime; import java.util.*; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -51,6 +52,9 @@ @Autowired HashWithEnumRepository hashWithEnumRepository; + @Autowired + StudentRepository studentRepository; + @BeforeEach void createTestDataIfNeeded() { flushSearchIndexFor(Company.class); @@ -81,6 +85,14 @@ void createTestDataIfNeeded() { michael.setFavoriteFoods(Set.of("Steak and Kidney Pie", "Sunday Roast", "Bangers and Mash")); personRepo.saveAll(List.of(john, gray, terryg, eric, terryj, michael)); + + studentRepository.deleteAll(); + List students = new ArrayList<>(); + for (int i = 0; i < 10; i++) { + students.add( + Student.of((long) i, "Student" + i, LocalDateTime.now())); + } + studentRepository.saveAll(students); } @Test @@ -634,4 +646,14 @@ void testOrderByInMethodName() { () -> assertThat(onlyVal3).containsExactly(doc3) // ); } + + @Test + void testFindByPropertyWithAliasWithHyphens() { + List result = studentRepository.findByUserName("Student2"); + + assertAll( // + () -> assertThat(result).hasSize(1), + () -> assertThat(result).extracting("userName").containsExactly("Student2") // + ); + } } diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/Student.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/Student.java new file mode 100644 index 00000000..75752624 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/Student.java @@ -0,0 +1,33 @@ +package com.redis.om.spring.annotations.hash.fixtures; + +import com.google.gson.annotations.SerializedName; +import com.redis.om.spring.annotations.Document; +import com.redis.om.spring.annotations.Indexed; +import lombok.Builder; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import org.springframework.data.annotation.Id; +import org.springframework.data.redis.core.RedisHash; + +import java.time.LocalDateTime; + +@Data +@Builder +@RequiredArgsConstructor(staticName = "of") +@RedisHash +public class Student { + + @Id + @NonNull + private Long id; + + @Indexed(alias = "User-Name")// fieldName = "User-Name", schemaFieldType = SchemaFieldType.TAG + @NonNull + private String userName; + + @Indexed + @NonNull + private LocalDateTime eventTimestamp; + +} diff --git a/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/StudentRepository.java b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/StudentRepository.java new file mode 100644 index 00000000..be0dd1f1 --- /dev/null +++ b/redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/fixtures/StudentRepository.java @@ -0,0 +1,10 @@ +package com.redis.om.spring.annotations.hash.fixtures; + +import com.redis.om.spring.repository.RedisDocumentRepository; +import com.redis.om.spring.repository.RedisEnhancedRepository; + +import java.util.List; + +public interface StudentRepository extends RedisEnhancedRepository { + List findByUserName(String userName); +}