Skip to content

Commit

Permalink
feature: implement Query By Example API - QBE (resolves gh-40)
Browse files Browse the repository at this point in the history
  • Loading branch information
bsbodden committed Jul 14, 2023
1 parent 3928c20 commit 8dca359
Show file tree
Hide file tree
Showing 27 changed files with 2,393 additions and 279 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,10 @@ public boolean indexExistsFor(Class<?> entityClass) {
return indexedEntityClasses.contains(entityClass);
}

public Schema getSchemaFor(Class<?> entityClass) {
return entityClassToSchema.get(entityClass);
}

private List<Field> findIndexFields(java.lang.reflect.Field field, String prefix, boolean isDocument) {
List<Field> fields = new ArrayList<>();

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

if (!ObjectUtils.isEmpty(ti.alias())) {
fieldName = fieldName.as(ti.alias());
} else {
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

return new TagField(fieldName, ti.separator(), false);
}
Expand Down Expand Up @@ -384,13 +380,10 @@ private Field indexAsVectorFieldFor(java.lang.reflect.Field field, boolean isDoc
}
}

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

if (!ObjectUtils.isEmpty(indexed.alias())) {
vectorField.as(indexed.alias());
} else {
vectorField.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
VectorField vectorField = new VectorField(fieldName, indexed.algorithm(), attributes);
vectorField.as(alias);

return vectorField;
}
Expand Down Expand Up @@ -431,13 +424,10 @@ private Field indexAsVectorFieldFor(java.lang.reflect.Field field, boolean isDoc
}
}

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

if (!ObjectUtils.isEmpty(vi.alias())) {
vectorField.as(vi.alias());
} else {
vectorField.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
VectorField vectorField = new VectorField(fieldName, vi.algorithm(), attributes);
vectorField.as(alias);

return vectorField;
}
Expand All @@ -450,22 +440,20 @@ private Field indexAsTagFieldFor(java.lang.reflect.Field field, boolean isDocume
String fieldPostfix = (isDocument && typeInfo.isCollectionLike() && !field.isAnnotationPresent(JsonAdapter.class))
? index
: "";
FieldName fieldName = FieldName.of(fieldPrefix + field.getName() + fieldPostfix);

fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
String name = fieldPrefix + field.getName() + fieldPostfix;
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

private Field indexAsTextFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, TextIndexed ti) {
String fieldPrefix = getFieldPrefix(prefix, isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());

if (!ObjectUtils.isEmpty(ti.alias())) {
fieldName = fieldName.as(ti.alias());
} else {
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
String name = fieldPrefix + field.getName();
String alias = ObjectUtils.isEmpty(ti.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ti.alias();
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

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

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

if (!ObjectUtils.isEmpty(ti.alias())) {
fieldName = fieldName.as(ti.alias());
} else {
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
String phonetic = ObjectUtils.isEmpty(ti.phonetic()) ? null : ti.phonetic();

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

private Field indexAsGeoFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix, GeoIndexed gi) {
String fieldPrefix = getFieldPrefix(prefix, isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());

if (!ObjectUtils.isEmpty(gi.alias())) {
fieldName = fieldName.as(gi.alias());
} else {
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
String name = fieldPrefix + field.getName();
String alias = ObjectUtils.isEmpty(gi.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : gi.alias();
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

private Field indexAsNumericFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix,
NumericIndexed ni) {
String fieldPrefix = getFieldPrefix(prefix, isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());

if (!ObjectUtils.isEmpty(ni.alias())) {
fieldName = fieldName.as(ni.alias());
} else {
fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
}
String name = fieldPrefix + field.getName();
String alias = ObjectUtils.isEmpty(ni.alias()) ? QueryUtils.searchIndexFieldAliasFor(field, prefix) : ni.alias();
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

private Field indexAsNumericFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix,
boolean sortable, boolean noIndex) {
String fieldPrefix = getFieldPrefix(prefix, isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());

fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
String name = fieldPrefix + field.getName();
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

private Field indexAsGeoFieldFor(java.lang.reflect.Field field, boolean isDocument, String prefix) {
String fieldPrefix = getFieldPrefix(prefix, isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + field.getName());

fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(field, prefix));
String name = fieldPrefix + field.getName();
String alias = QueryUtils.searchIndexFieldAliasFor(field, prefix);
FieldName fieldName = FieldName.of(name);
fieldName = fieldName.as(alias);

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

private Optional<Field> createIndexedFieldForReferenceIdField( //
java.lang.reflect.Field referenceIdField, //
java.lang.reflect.Field idFieldToIndex, boolean isDocument) {
boolean isDocument) {
Optional<Field> result;

String fieldPrefix = getFieldPrefix("", isDocument);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,9 +320,10 @@ public CustomRedisKeyValueTemplate getKeyValueTemplate( //
@Bean(name = "streamingQueryBuilder")
EntityStream streamingQueryBuilder(
RedisModulesOperations<?> redisModulesOperations,
@Qualifier("omGsonBuilder") GsonBuilder gsonBuilder
@Qualifier("omGsonBuilder") GsonBuilder gsonBuilder,
RediSearchIndexer indexer
) {
return new EntityStreamImpl(redisModulesOperations, gsonBuilder);
return new EntityStreamImpl(redisModulesOperations, gsonBuilder, indexer);
}

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

0 comments on commit 8dca359

Please sign in to comment.