Skip to content

Commit 0ee4f4e

Browse files
committed
feature: handle search aliased fields with special characters (resolves #433)
1 parent 3e10e0c commit 0ee4f4e

File tree

9 files changed

+143
-11
lines changed

9 files changed

+143
-11
lines changed

redis-om-spring/src/main/java/com/redis/om/spring/RediSearchIndexer.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.github.f4b6a3.ulid.Ulid;
44
import com.google.gson.GsonBuilder;
55
import com.google.gson.annotations.JsonAdapter;
6+
import com.google.gson.annotations.SerializedName;
67
import com.redis.om.spring.annotations.*;
78
import com.redis.om.spring.ops.RedisModulesOperations;
89
import com.redis.om.spring.ops.search.SearchOperations;
@@ -217,8 +218,8 @@ private List<SchemaField> findIndexFields(java.lang.reflect.Field field, String
217218
// Also UUID and Ulid (classes whose toString() is a valid text representation
218219
// of the value)
219220
//
220-
if (CharSequence.class.isAssignableFrom(
221-
fieldType) || (fieldType == Boolean.class) || (fieldType == UUID.class) || (fieldType == Ulid.class)) {
221+
if (CharSequence.class.isAssignableFrom(fieldType) || //
222+
(fieldType == Boolean.class) || (fieldType == UUID.class) || (fieldType == Ulid.class)) {
222223
fields.add(indexAsTagFieldFor(field, isDocument, prefix, indexed.sortable(), indexed.separator(),
223224
indexed.arrayIndex(), indexed.alias()));
224225
} else if (fieldType.isEnum()) {
@@ -454,13 +455,15 @@ private VectorField indexAsVectorFieldFor(java.lang.reflect.Field field, boolean
454455

455456
private SchemaField indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix,
456457
boolean sortable, String separator, int arrayIndex, String annotationAlias) {
458+
SerializedName serializedName = field.getAnnotation(SerializedName.class);
459+
String fname = (serializedName != null) ? serializedName.value() : field.getName();
457460
TypeInformation<?> typeInfo = TypeInformation.of(field.getType());
458461
String fieldPrefix = getFieldPrefix(prefix, isDocument);
459462
String index = (arrayIndex != Integer.MIN_VALUE) ? ".[" + arrayIndex + "]" : "[*]";
460463
String fieldPostfix = (isDocument && typeInfo.isCollectionLike() && !field.isAnnotationPresent(JsonAdapter.class)) ?
461464
index :
462465
"";
463-
String name = fieldPrefix + field.getName() + fieldPostfix;
466+
String name = fieldPrefix + fname + fieldPostfix;
464467
String alias = (annotationAlias == null || annotationAlias.isBlank()) ?
465468
QueryUtils.searchIndexFieldAliasFor(field, prefix) :
466469
annotationAlias;
@@ -536,8 +539,10 @@ private List<SchemaField> indexAsNestedFieldFor(java.lang.reflect.Field field, S
536539
}
537540

538541
private FieldName getFieldName(java.lang.reflect.Field field, boolean isDocument, String prefix, String alias) {
542+
SerializedName serializedName = field.getAnnotation(SerializedName.class);
543+
String fname = (serializedName != null) ? serializedName.value() : field.getName();
539544
String fieldPrefix = getFieldPrefix(prefix, isDocument);
540-
String name = fieldPrefix + field.getName();
545+
String name = fieldPrefix + fname;
541546
FieldName fieldName = FieldName.of(name);
542547
fieldName = fieldName.as(alias);
543548
return fieldName;
@@ -758,9 +763,11 @@ private Optional<SchemaField> createIndexedFieldForIdField(Class<?> cl, List<Sch
758763
private Optional<SchemaField> createIndexedFieldForReferenceIdField( //
759764
java.lang.reflect.Field referenceIdField, //
760765
boolean isDocument) {
766+
SerializedName serializedName = referenceIdField.getAnnotation(SerializedName.class);
767+
String fname = (serializedName != null) ? serializedName.value() : referenceIdField.getName();
761768

762769
String fieldPrefix = getFieldPrefix("", isDocument);
763-
FieldName fieldName = FieldName.of(fieldPrefix + referenceIdField.getName());
770+
FieldName fieldName = FieldName.of(fieldPrefix + fname);
764771
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(referenceIdField, ""));
765772
return Optional.of(
766773
isDocument ? TagField.of(fieldName).separator('|') : TagField.of(fieldName).separator('|').sortable());

redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RediSearchQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -712,7 +712,7 @@ private String prepareQuery(final Object[] parameters, boolean excludeNullParams
712712
if (excludeNullParams && (fieldClauses.getSecond() == QueryClause.IS_NULL || fieldClauses.getSecond() == QueryClause.IS_NOT_NULL)) {
713713
return "";
714714
}
715-
String fieldName = fieldClauses.getFirst();
715+
String fieldName = QueryUtils.escape(fieldClauses.getFirst());
716716
QueryClause queryClause = fieldClauses.getSecond();
717717
int paramsCnt = queryClause.getClauseTemplate().getNumberOfArguments();
718718

redis-om-spring/src/main/java/com/redis/om/spring/repository/query/RedisEnhancedQuery.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -716,7 +716,7 @@ private String prepareQuery(final Object[] parameters, boolean excludeNullParams
716716
if (excludeNullParams && (fieldClauses.getSecond() == QueryClause.IS_NULL || fieldClauses.getSecond() == QueryClause.IS_NOT_NULL)) {
717717
return "";
718718
}
719-
String fieldName = fieldClauses.getFirst();
719+
String fieldName = QueryUtils.escape(fieldClauses.getFirst());
720720
QueryClause queryClause = fieldClauses.getSecond();
721721
int paramsCnt = queryClause.getClauseTemplate().getNumberOfArguments();
722722

redis-om-spring/src/test/java/com/redis/om/spring/annotations/document/RepositoryIssuesTest.java

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,12 @@
11
package com.redis.om.spring.annotations.document;
22

33
import com.redis.om.spring.AbstractBaseDocumentTest;
4-
import com.redis.om.spring.annotations.document.fixtures.SKU;
5-
import com.redis.om.spring.annotations.document.fixtures.SKUCacheRepository;
6-
import com.redis.om.spring.annotations.document.fixtures.User2;
7-
import com.redis.om.spring.annotations.document.fixtures.User2Repository;
4+
import com.redis.om.spring.annotations.document.fixtures.*;
85
import org.junit.jupiter.api.BeforeEach;
96
import org.junit.jupiter.api.Test;
107
import org.springframework.beans.factory.annotation.Autowired;
118

9+
import java.time.LocalDateTime;
1210
import java.util.ArrayList;
1311
import java.util.List;
1412
import java.util.Set;
@@ -24,6 +22,9 @@ class RepositoryIssuesTest extends AbstractBaseDocumentTest {
2422
@Autowired
2523
SKUCacheRepository skuCacheRepository;
2624

25+
@Autowired
26+
StudentRepository studentRepository;
27+
2728
@BeforeEach
2829
void cleanUp() {
2930
repository.deleteAll();
@@ -37,6 +38,13 @@ void cleanUp() {
3738
}
3839
skuCacheRepository.saveAll(skuCaches);
3940

41+
studentRepository.deleteAll();
42+
List<Student> students = new ArrayList<>();
43+
for (int i = 0; i < 10; i++) {
44+
students.add(
45+
Student.of((long) i, "Student" + i, LocalDateTime.now()));
46+
}
47+
studentRepository.saveAll(students);
4048
}
4149

4250
// RediSearchQuery wrong preparedQuery #187
@@ -76,4 +84,14 @@ void testFindOneBy() {
7684
() -> assertThat(result.getSkuNumber()).isEqualTo("A11111") //
7785
);
7886
}
87+
88+
@Test
89+
void testFindByPropertyWithAliasWithHyphens() {
90+
List<Student> result = studentRepository.findByUserName("Student2");
91+
92+
assertAll( //
93+
() -> assertThat(result).hasSize(1),
94+
() -> assertThat(result).extracting("userName").containsExactly("Student2") //
95+
);
96+
}
7997
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.redis.om.spring.annotations.document.fixtures;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import com.redis.om.spring.annotations.Document;
5+
import com.redis.om.spring.annotations.Indexed;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NonNull;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.data.annotation.Id;
11+
12+
import java.time.LocalDateTime;
13+
14+
@Data
15+
@Builder
16+
@RequiredArgsConstructor(staticName = "of")
17+
@Document
18+
public class Student {
19+
20+
@Id
21+
@NonNull
22+
private Long id;
23+
24+
@Indexed(alias = "User-Name")// fieldName = "User-Name", schemaFieldType = SchemaFieldType.TAG
25+
@SerializedName("User-Name")
26+
@NonNull
27+
private String userName;
28+
29+
@Indexed
30+
@NonNull
31+
private LocalDateTime eventTimestamp;
32+
33+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.redis.om.spring.annotations.document.fixtures;
2+
3+
import com.redis.om.spring.repository.RedisDocumentRepository;
4+
5+
import java.util.List;
6+
7+
public interface StudentRepository extends RedisDocumentRepository<Student, Long> {
8+
List<Student> findByUserName(String userName);
9+
}

redis-om-spring/src/test/java/com/redis/om/spring/annotations/hash/BasicRedisHashMappingTest.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
import java.time.Duration;
2323
import java.time.LocalDate;
24+
import java.time.LocalDateTime;
2425
import java.util.*;
2526
import java.util.stream.Collectors;
2627
import java.util.stream.IntStream;
@@ -51,6 +52,9 @@
5152
@Autowired
5253
HashWithEnumRepository hashWithEnumRepository;
5354

55+
@Autowired
56+
StudentRepository studentRepository;
57+
5458
@BeforeEach
5559
void createTestDataIfNeeded() {
5660
flushSearchIndexFor(Company.class);
@@ -81,6 +85,14 @@ void createTestDataIfNeeded() {
8185
michael.setFavoriteFoods(Set.of("Steak and Kidney Pie", "Sunday Roast", "Bangers and Mash"));
8286

8387
personRepo.saveAll(List.of(john, gray, terryg, eric, terryj, michael));
88+
89+
studentRepository.deleteAll();
90+
List<Student> students = new ArrayList<>();
91+
for (int i = 0; i < 10; i++) {
92+
students.add(
93+
Student.of((long) i, "Student" + i, LocalDateTime.now()));
94+
}
95+
studentRepository.saveAll(students);
8496
}
8597

8698
@Test
@@ -634,4 +646,14 @@ void testOrderByInMethodName() {
634646
() -> assertThat(onlyVal3).containsExactly(doc3) //
635647
);
636648
}
649+
650+
@Test
651+
void testFindByPropertyWithAliasWithHyphens() {
652+
List<Student> result = studentRepository.findByUserName("Student2");
653+
654+
assertAll( //
655+
() -> assertThat(result).hasSize(1),
656+
() -> assertThat(result).extracting("userName").containsExactly("Student2") //
657+
);
658+
}
637659
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.redis.om.spring.annotations.hash.fixtures;
2+
3+
import com.google.gson.annotations.SerializedName;
4+
import com.redis.om.spring.annotations.Document;
5+
import com.redis.om.spring.annotations.Indexed;
6+
import lombok.Builder;
7+
import lombok.Data;
8+
import lombok.NonNull;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.data.annotation.Id;
11+
import org.springframework.data.redis.core.RedisHash;
12+
13+
import java.time.LocalDateTime;
14+
15+
@Data
16+
@Builder
17+
@RequiredArgsConstructor(staticName = "of")
18+
@RedisHash
19+
public class Student {
20+
21+
@Id
22+
@NonNull
23+
private Long id;
24+
25+
@Indexed(alias = "User-Name")// fieldName = "User-Name", schemaFieldType = SchemaFieldType.TAG
26+
@NonNull
27+
private String userName;
28+
29+
@Indexed
30+
@NonNull
31+
private LocalDateTime eventTimestamp;
32+
33+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package com.redis.om.spring.annotations.hash.fixtures;
2+
3+
import com.redis.om.spring.repository.RedisDocumentRepository;
4+
import com.redis.om.spring.repository.RedisEnhancedRepository;
5+
6+
import java.util.List;
7+
8+
public interface StudentRepository extends RedisEnhancedRepository<Student, Long> {
9+
List<Student> findByUserName(String userName);
10+
}

0 commit comments

Comments
 (0)