@@ -47,7 +47,7 @@ public class RediSearchIndexer {
47
47
private final Map <String , Class <?>> keyspaceToEntityClass = new ConcurrentHashMap <>();
48
48
private final Map <Class <?>, String > entityClassToKeySpace = new ConcurrentHashMap <>();
49
49
private final List <Class <?>> indexedEntityClasses = new ArrayList <>();
50
- private final Map <Class <?>,Schema > entityClassToSchema = new ConcurrentHashMap <>();
50
+ private final Map <Class <?>, Schema > entityClassToSchema = new ConcurrentHashMap <>();
51
51
52
52
private static final Log logger = LogFactory .getLog (RediSearchIndexer .class );
53
53
@@ -97,7 +97,8 @@ public void createIndexFor(Class<?> cl) {
97
97
indexName = cl .getName () + "Idx" ;
98
98
logger .info (String .format ("Found @%s annotated class: %s" , idxType , cl .getName ()));
99
99
100
- final List <java .lang .reflect .Field > allClassFields = com .redis .om .spring .util .ObjectUtils .getDeclaredFieldsTransitively (cl );
100
+ final List <java .lang .reflect .Field > allClassFields = com .redis .om .spring .util .ObjectUtils
101
+ .getDeclaredFieldsTransitively (cl );
101
102
102
103
List <Field > fields = new ArrayList <>();
103
104
@@ -115,7 +116,8 @@ public void createIndexFor(Class<?> cl) {
115
116
if (maybeIdField .isPresent ()) {
116
117
java .lang .reflect .Field idField = maybeIdField .get ();
117
118
// Only auto-index the @Id if not already indexed by the user (gh-135)
118
- if (!idField .isAnnotationPresent (Indexed .class ) && !idField .isAnnotationPresent (Searchable .class ) && (fields .stream ().noneMatch (f -> f .name .equals (idField .getName ())))) {
119
+ if (!idField .isAnnotationPresent (Indexed .class ) && !idField .isAnnotationPresent (Searchable .class )
120
+ && (fields .stream ().noneMatch (f -> f .name .equals (idField .getName ())))) {
119
121
if (Number .class .isAssignableFrom (idField .getType ())) {
120
122
fields
121
123
.add (indexAsNumericFieldFor (maybeIdField .get (), idxType == IndexDefinition .Type .JSON , "" , true ,
@@ -239,12 +241,15 @@ private List<Field> findIndexFields(java.lang.reflect.Field field, String prefix
239
241
// Any Character class, Enums or Boolean -> Tag Search Field
240
242
//
241
243
if (CharSequence .class .isAssignableFrom (fieldType ) || (fieldType == Boolean .class ) || (fieldType .isEnum ())) {
242
- fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (), indexed .arrayIndex ()));
244
+ fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (),
245
+ indexed .arrayIndex ()));
243
246
}
244
247
//
245
248
// Any Numeric class -> Numeric Search Field
246
249
//
247
- else if (Number .class .isAssignableFrom (fieldType ) || (fieldType == LocalDateTime .class ) || (field .getType () == LocalDate .class ) || (field .getType () == Date .class ) || (field .getType () == Instant .class )) {
250
+ else if (Number .class .isAssignableFrom (fieldType ) || (fieldType == LocalDateTime .class )
251
+ || (field .getType () == LocalDate .class ) || (field .getType () == Date .class )
252
+ || (field .getType () == Instant .class )) {
248
253
fields .add (indexAsNumericFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .noindex ()));
249
254
}
250
255
//
@@ -263,7 +268,8 @@ else if (Set.class.isAssignableFrom(fieldType) || List.class.isAssignableFrom(fi
263
268
Class <?> collectionType = maybeCollectionType .get ();
264
269
265
270
if (CharSequence .class .isAssignableFrom (collectionType ) || (collectionType == Boolean .class )) {
266
- fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (), indexed .arrayIndex ()));
271
+ fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (),
272
+ indexed .arrayIndex ()));
267
273
// Index nested fields
268
274
} else if (isDocument ) {
269
275
if (Number .class .isAssignableFrom (collectionType )) {
@@ -291,25 +297,27 @@ else if (fieldType == Point.class) {
291
297
// Recursively explore the fields for Index annotated fields
292
298
//
293
299
else {
294
- for (java .lang .reflect .Field subfield : com .redis .om .spring .util .ObjectUtils . getDeclaredFieldsTransitively ( field . getType ())) {
295
- String subfieldPrefix = ( prefix == null || prefix . isBlank ()) ?
296
- field .getName () :
297
- String .join ("." , prefix , field .getName ());
300
+ for (java .lang .reflect .Field subfield : com .redis .om .spring .util .ObjectUtils
301
+ . getDeclaredFieldsTransitively ( field . getType ())) {
302
+ String subfieldPrefix = ( prefix == null || prefix . isBlank ()) ? field .getName ()
303
+ : String .join ("." , prefix , field .getName ());
298
304
fields .addAll (findIndexFields (subfield , subfieldPrefix , isDocument ));
299
305
}
300
306
}
301
307
} else { // Schema field type hardcoded/set in @Indexed
302
308
switch (indexed .schemaFieldType ()) {
303
309
case TAG ->
304
- fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (), indexed .arrayIndex ()));
305
- case NUMERIC -> fields .add (indexAsNumericFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .noindex ()));
310
+ fields .add (indexAsTagFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .separator (),
311
+ indexed .arrayIndex ()));
312
+ case NUMERIC ->
313
+ fields .add (indexAsNumericFieldFor (field , isDocument , prefix , indexed .sortable (), indexed .noindex ()));
306
314
case GEO -> fields .add (indexAsGeoFieldFor (field , true , prefix , indexed .sortable (), indexed .noindex ()));
307
315
case VECTOR -> fields .add (indexAsVectorFieldFor (field , isDocument , prefix , indexed ));
308
316
case NESTED -> {
309
- for (java .lang .reflect .Field subfield : com .redis .om .spring .util .ObjectUtils . getDeclaredFieldsTransitively ( field . getType ())) {
310
- String subfieldPrefix = ( prefix == null || prefix . isBlank ()) ?
311
- field .getName () :
312
- String .join ("." , prefix , field .getName ());
317
+ for (java .lang .reflect .Field subfield : com .redis .om .spring .util .ObjectUtils
318
+ . getDeclaredFieldsTransitively ( field . getType ())) {
319
+ String subfieldPrefix = ( prefix == null || prefix . isBlank ()) ? field .getName ()
320
+ : String .join ("." , prefix , field .getName ());
313
321
fields .addAll (findIndexFields (subfield , subfieldPrefix , isDocument ));
314
322
}
315
323
}
@@ -370,14 +378,15 @@ private Field indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocume
370
378
return new TagField (fieldName , ti .separator (), false );
371
379
}
372
380
373
- private Field indexAsVectorFieldFor (java .lang .reflect .Field field , boolean isDocument , String prefix , Indexed indexed ) {
381
+ private Field indexAsVectorFieldFor (java .lang .reflect .Field field , boolean isDocument , String prefix ,
382
+ Indexed indexed ) {
374
383
TypeInformation <?> typeInfo = TypeInformation .of (field .getType ());
375
384
String fieldPrefix = getFieldPrefix (prefix , isDocument );
376
385
377
386
String fieldPostfix = (isDocument && typeInfo .isCollectionLike () && !field .isAnnotationPresent (JsonAdapter .class ))
378
387
? "[*]"
379
388
: "" ;
380
- String fieldName =fieldPrefix + field .getName () + fieldPostfix ;
389
+ String fieldName = fieldPrefix + field .getName () + fieldPostfix ;
381
390
382
391
Map <String , Object > attributes = new HashMap <>();
383
392
attributes .put ("TYPE" , indexed .type ().toString ());
@@ -416,14 +425,15 @@ private Field indexAsVectorFieldFor(java.lang.reflect.Field field, boolean isDoc
416
425
return vectorField ;
417
426
}
418
427
419
- private Field indexAsVectorFieldFor (java .lang .reflect .Field field , boolean isDocument , String prefix , VectorIndexed vi ) {
428
+ private Field indexAsVectorFieldFor (java .lang .reflect .Field field , boolean isDocument , String prefix ,
429
+ VectorIndexed vi ) {
420
430
TypeInformation <?> typeInfo = TypeInformation .of (field .getType ());
421
431
String fieldPrefix = getFieldPrefix (prefix , isDocument );
422
432
423
433
String fieldPostfix = (isDocument && typeInfo .isCollectionLike () && !field .isAnnotationPresent (JsonAdapter .class ))
424
434
? "[*]"
425
435
: "" ;
426
- String fieldName =fieldPrefix + field .getName () + fieldPostfix ;
436
+ String fieldName = fieldPrefix + field .getName () + fieldPostfix ;
427
437
428
438
Map <String , Object > attributes = new HashMap <>();
429
439
attributes .put ("TYPE" , vi .type ().toString ());
@@ -566,7 +576,8 @@ private List<Field> getNestedField(String fieldPrefix, java.lang.reflect.Field f
566
576
Type genericType = field .getGenericType ();
567
577
if (genericType instanceof ParameterizedType pt ) {
568
578
Class <?> actualTypeArgument = (Class <?>) pt .getActualTypeArguments ()[0 ];
569
- List <java .lang .reflect .Field > subDeclaredFields = com .redis .om .spring .util .ObjectUtils .getDeclaredFieldsTransitively (actualTypeArgument );
579
+ List <java .lang .reflect .Field > subDeclaredFields = com .redis .om .spring .util .ObjectUtils
580
+ .getDeclaredFieldsTransitively (actualTypeArgument );
570
581
String tempPrefix = "" ;
571
582
if (prefix == null ) {
572
583
prefix = field .getName ();
@@ -613,6 +624,22 @@ else if (Number.class.isAssignableFrom(subField.getType()) || (subField.getType(
613
624
logger .info (String .format ("Creating nested relationships: %s -> %s" , field .getName (), subField .getName ()));
614
625
fieldList .add (new Field (fieldName , FieldType .NUMERIC ));
615
626
}
627
+ } else if (subField .isAnnotationPresent (Searchable .class )) {
628
+ Searchable searchable = subField .getAnnotation (Searchable .class );
629
+ tempPrefix = field .getName () + "[0:]." ;
630
+
631
+ FieldName fieldName = FieldName .of (fieldPrefix + tempPrefix + subField .getName ());
632
+ fieldName = fieldName .as (QueryUtils .searchIndexFieldAliasFor (subField , prefix ));
633
+
634
+ logger
635
+ .info (String .format ("Creating TEXT nested relationships: %s -> %s" , field .getName (), subField .getName ()));
636
+
637
+ String phonetic = ObjectUtils .isEmpty (searchable .phonetic ()) ? null : searchable .phonetic ();
638
+
639
+ fieldList .add (new TextField (fieldName , searchable .weight (), searchable .sortable (), searchable .nostem (),
640
+ searchable .noindex (), phonetic ));
641
+
642
+ continue ;
616
643
}
617
644
getNestedField (fieldPrefix + tempPrefix , subField , prefix , fieldList );
618
645
}
0 commit comments