Skip to content

Commit 07cc79d

Browse files
committed
feature: implement Query By Example API - QBE (resolves gh-40)
1 parent f6c6d22 commit 07cc79d

27 files changed

+2393
-279
lines changed

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

Lines changed: 44 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,10 @@ public boolean indexExistsFor(Class<?> entityClass) {
185185
return indexedEntityClasses.contains(entityClass);
186186
}
187187

188+
public Schema getSchemaFor(Class<?> entityClass) {
189+
return entityClassToSchema.get(entityClass);
190+
}
191+
188192
private List<Field> findIndexFields(java.lang.reflect.Field field, String prefix, boolean isDocument) {
189193
List<Field> fields = new ArrayList<>();
190194

@@ -200,13 +204,7 @@ private List<Field> findIndexFields(java.lang.reflect.Field field, String prefix
200204
// @Reference @Indexed fields: Create schema field for the reference entity @Id field
201205
//
202206
logger.debug("🪲Found @Reference field " + field.getName() + " in " + field.getDeclaringClass().getSimpleName());
203-
var maybeReferenceIdField = getIdFieldForEntityClass(fieldType);
204-
if (maybeReferenceIdField.isPresent()) {
205-
var idFieldToIndex = maybeReferenceIdField.get();
206-
createIndexedFieldForReferenceIdField(field, idFieldToIndex, isDocument).ifPresent(fields::add);
207-
} else {
208-
logger.warn("Couldn't find ID field for reference" + field.getName() + " in " + field.getDeclaringClass().getSimpleName());
209-
}
207+
createIndexedFieldForReferenceIdField(field, isDocument).ifPresent(fields::add);
210208
} else if (indexed.schemaFieldType() == SchemaFieldType.AUTODETECT) {
211209
//
212210
// Any Character class, Enums or Boolean -> Tag Search Field
@@ -337,13 +335,11 @@ private Field indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocume
337335
String fieldPostfix = (isDocument && typeInfo.isCollectionLike() && !field.isAnnotationPresent(JsonAdapter.class))
338336
? "[*]"
339337
: "";
340-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName() + fieldPostfix);
338+
String name = fieldPrefix + field.getName() + fieldPostfix;
339+
String alias = ObjectUtils.isEmpty(ti.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ti.alias();
341340

342-
if (!ObjectUtils.isEmpty(ti.alias())) {
343-
fieldName = fieldName.as(ti.alias());
344-
} else {
345-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
346-
}
341+
FieldName fieldName = FieldName.of(name);
342+
fieldName = fieldName.as(alias);
347343

348344
return new TagField(fieldName, ti.separator(), false);
349345
}
@@ -384,13 +380,10 @@ private Field indexAsVectorFieldFor(java.lang.reflect.Field field, boolean isDoc
384380
}
385381
}
386382

387-
VectorField vectorField = new VectorField(fieldName, indexed.algorithm(), attributes);
383+
String alias = ObjectUtils.isEmpty(indexed.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : indexed.alias();
388384

389-
if (!ObjectUtils.isEmpty(indexed.alias())) {
390-
vectorField.as(indexed.alias());
391-
} else {
392-
vectorField.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
393-
}
385+
VectorField vectorField = new VectorField(fieldName, indexed.algorithm(), attributes);
386+
vectorField.as(alias);
394387

395388
return vectorField;
396389
}
@@ -431,13 +424,10 @@ private Field indexAsVectorFieldFor(java.lang.reflect.Field field, boolean isDoc
431424
}
432425
}
433426

434-
VectorField vectorField = new VectorField(fieldName, vi.algorithm(), attributes);
427+
String alias = ObjectUtils.isEmpty(vi.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : vi.alias();
435428

436-
if (!ObjectUtils.isEmpty(vi.alias())) {
437-
vectorField.as(vi.alias());
438-
} else {
439-
vectorField.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
440-
}
429+
VectorField vectorField = new VectorField(fieldName, vi.algorithm(), attributes);
430+
vectorField.as(alias);
441431

442432
return vectorField;
443433
}
@@ -450,22 +440,20 @@ private Field indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocume
450440
String fieldPostfix = (isDocument && typeInfo.isCollectionLike() && !field.isAnnotationPresent(JsonAdapter.class))
451441
? index
452442
: "";
453-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName() + fieldPostfix);
454-
455-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
443+
String name = fieldPrefix + field.getName() + fieldPostfix;
444+
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
445+
FieldName fieldName = FieldName.of(name);
446+
fieldName = fieldName.as(alias);
456447

457448
return new TagField(fieldName, separator.isBlank() ? null : separator, sortable);
458449
}
459450

460451
private Field indexAsTextFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, TextIndexed ti) {
461452
String fieldPrefix = getFieldPrefix(prefix, isDocument);
462-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
463-
464-
if (!ObjectUtils.isEmpty(ti.alias())) {
465-
fieldName = fieldName.as(ti.alias());
466-
} else {
467-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
468-
}
453+
String name = fieldPrefix + field.getName();
454+
String alias = ObjectUtils.isEmpty(ti.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ti.alias();
455+
FieldName fieldName = FieldName.of(name);
456+
fieldName = fieldName.as(alias);
469457

470458
String phonetic = ObjectUtils.isEmpty(ti.phonetic()) ? null : ti.phonetic();
471459

@@ -474,60 +462,54 @@ private Field indexAsTextFieldFor(java.lang.reflect.Field field, boolean isDocum
474462

475463
private Field indexAsTextFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, Searchable ti) {
476464
String fieldPrefix = getFieldPrefix(prefix, isDocument);
477-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
465+
String name = fieldPrefix + field.getName();
466+
String alias = ObjectUtils.isEmpty(ti.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ti.alias();
467+
FieldName fieldName = FieldName.of(name);
468+
fieldName = fieldName.as(alias);
478469

479-
if (!ObjectUtils.isEmpty(ti.alias())) {
480-
fieldName = fieldName.as(ti.alias());
481-
} else {
482-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
483-
}
484470
String phonetic = ObjectUtils.isEmpty(ti.phonetic()) ? null : ti.phonetic();
485471

486472
return new TextField(fieldName, ti.weight(), ti.sortable(), ti.nostem(), ti.noindex(), phonetic);
487473
}
488474

489475
private Field indexAsGeoFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, GeoIndexed gi) {
490476
String fieldPrefix = getFieldPrefix(prefix, isDocument);
491-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
492-
493-
if (!ObjectUtils.isEmpty(gi.alias())) {
494-
fieldName = fieldName.as(gi.alias());
495-
} else {
496-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
497-
}
477+
String name = fieldPrefix + field.getName();
478+
String alias = ObjectUtils.isEmpty(gi.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : gi.alias();
479+
FieldName fieldName = FieldName.of(name);
480+
fieldName = fieldName.as(alias);
498481

499482
return new Field(fieldName, FieldType.GEO);
500483
}
501484

502485
private Field indexAsNumericFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix,
503486
NumericIndexed ni) {
504487
String fieldPrefix = getFieldPrefix(prefix, isDocument);
505-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
506-
507-
if (!ObjectUtils.isEmpty(ni.alias())) {
508-
fieldName = fieldName.as(ni.alias());
509-
} else {
510-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
511-
}
488+
String name = fieldPrefix + field.getName();
489+
String alias = ObjectUtils.isEmpty(ni.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ni.alias();
490+
FieldName fieldName = FieldName.of(name);
491+
fieldName = fieldName.as(alias);
512492

513493
return new Field(fieldName, FieldType.NUMERIC);
514494
}
515495

516496
private Field indexAsNumericFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix,
517497
boolean sortable, boolean noIndex) {
518498
String fieldPrefix = getFieldPrefix(prefix, isDocument);
519-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
520-
521-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
499+
String name = fieldPrefix + field.getName();
500+
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
501+
FieldName fieldName = FieldName.of(name);
502+
fieldName = fieldName.as(alias);
522503

523504
return new Field(fieldName, FieldType.NUMERIC, sortable, noIndex);
524505
}
525506

526507
private Field indexAsGeoFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix) {
527508
String fieldPrefix = getFieldPrefix(prefix, isDocument);
528-
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());
529-
530-
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
509+
String name = fieldPrefix + field.getName();
510+
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
511+
FieldName fieldName = FieldName.of(name);
512+
fieldName = fieldName.as(alias);
531513

532514
return new Field(fieldName, FieldType.GEO);
533515
}
@@ -716,7 +698,7 @@ private Optional<Field> createIndexedFieldForIdField(Class<?> cl, List<Field> fi
716698

717699
private Optional<Field> createIndexedFieldForReferenceIdField( //
718700
java.lang.reflect.Field referenceIdField, //
719-
java.lang.reflect.Field idFieldToIndex, boolean isDocument) {
701+
boolean isDocument) {
720702
Optional<Field> result;
721703

722704
String fieldPrefix = getFieldPrefix("", isDocument);

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -320,9 +320,10 @@ public CustomRedisKeyValueTemplate getKeyValueTemplate( //
320320
@Bean(name = "streamingQueryBuilder")
321321
EntityStream streamingQueryBuilder(
322322
RedisModulesOperations<?> redisModulesOperations,
323-
@Qualifier("omGsonBuilder") GsonBuilder gsonBuilder
323+
@Qualifier("omGsonBuilder") GsonBuilder gsonBuilder,
324+
RediSearchIndexer indexer
324325
) {
325-
return new EntityStreamImpl(redisModulesOperations, gsonBuilder);
326+
return new EntityStreamImpl(redisModulesOperations, gsonBuilder, indexer);
326327
}
327328

328329
@EventListener(ContextRefreshedEvent.class)
@@ -348,7 +349,6 @@ public void processBloom(ContextRefreshedEvent cre) {
348349
try {
349350
Class<?> cl = Class.forName(beanDef.getBeanClassName());
350351
for (java.lang.reflect.Field field : getDeclaredFieldsTransitively(cl)) {
351-
// Text
352352
if (field.isAnnotationPresent(Bloom.class)) {
353353
Bloom bloom = field.getAnnotation(Bloom.class);
354354
BloomOperations<String> ops = rmo.opsForBloom();

0 commit comments

Comments
 (0)