Skip to content

Commit

Permalink
fix: re-implement @reference in a non-boned-headed way
Browse files Browse the repository at this point in the history
  • Loading branch information
bsbodden committed Jul 2, 2023
1 parent 1e93c58 commit 907adaa
Show file tree
Hide file tree
Showing 19 changed files with 377 additions and 166 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,7 @@ public void createIndexFor(Class<?> cl) {
indexName = cl.getName() + "Idx";
logger.info(String.format("Found @%s annotated class: %s", idxType, cl.getName()));

final List<java.lang.reflect.Field> allClassFields = com.redis.om.spring.util.ObjectUtils
.getDeclaredFieldsTransitively(cl);
final List<java.lang.reflect.Field> allClassFields = getDeclaredFieldsTransitively(cl);

List<Field> fields = processIndexedFields(allClassFields, isDocument);
maybeScoreField = getDocumentScoreField(allClassFields, isDocument);
Expand Down Expand Up @@ -141,38 +140,33 @@ public void dropIndexFor(Class<?> cl) {
}

public Optional<String> getIndexName(String keyspace) {
Class<?> entityClass = keyspaceToEntityClass.get(getKey(keyspace));
if (entityClass != null) {
return Optional.of(entityClass.getName() + "Idx");
} else {
return Optional.empty();
}
return getIndexName(keyspaceToEntityClass.get(getKeyspace(keyspace)));
}

public Optional<String> getIndexName(Class<?> entityClass) {
if (entityClassToKeySpace.containsKey(entityClass)) {
if (entityClass != null && entityClassToKeySpace.containsKey(entityClass)) {
return Optional.of(entityClass.getName() + "Idx");
} else {
return Optional.empty();
}
}

public void addKeySpaceMapping(String keyspace, Class<?> entityClass) {
String key = getKey(keyspace);
String key = getKeyspace(keyspace);
keyspaceToEntityClass.put(key, entityClass);
entityClassToKeySpace.put(entityClass, key);
indexedEntityClasses.add(entityClass);
}

public void removeKeySpaceMapping(String keyspace, Class<?> entityClass) {
String key = getKey(keyspace);
String key = getKeyspace(keyspace);
keyspaceToEntityClass.remove(key);
entityClassToKeySpace.remove(entityClass);
indexedEntityClasses.remove(entityClass);
}

public Class<?> getEntityClassForKeyspace(String keyspace) {
return keyspaceToEntityClass.get(getKey(keyspace));
return keyspaceToEntityClass.get(getKeyspace(keyspace));
}

public String getKeyspaceForEntityClass(Class<?> entityClass) {
Expand Down Expand Up @@ -207,7 +201,7 @@ private List<Field> findIndexFields(java.lang.reflect.Field field, String prefix
var maybeReferenceIdField = getIdFieldForEntityClass(fieldType);
if (maybeReferenceIdField.isPresent()) {
var idFieldToIndex = maybeReferenceIdField.get();
createIndexedFieldForReferenceIdField(field.getDeclaringClass(), idFieldToIndex, isDocument).ifPresent(fields::add);
createIndexedFieldForReferenceIdField(field, idFieldToIndex, isDocument).ifPresent(fields::add);
} else {
logger.warn("Couldn't find ID field for reference" + field.getName() + " in " + field.getDeclaringClass().getSimpleName());
}
Expand Down Expand Up @@ -713,13 +707,20 @@ private Optional<Field> createIndexedFieldForIdField(Class<?> cl, List<Field> fi
return result;
}

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

String fieldPrefix = getFieldPrefix("", isDocument);
FieldName fieldName = FieldName.of(fieldPrefix + referenceIdField.getName());

fieldName = fieldName.as(QueryUtils.searchIndexFieldAliasFor(referenceIdField, ""));

if (Number.class.isAssignableFrom(referenceIdField.getType())) {
result = Optional.of(indexAsNumericFieldFor(referenceIdField, isDocument, "", true, false));
result = Optional.of(new Field(fieldName, FieldType.NUMERIC, true, false));
} else {
result = Optional.of(indexAsTagFieldFor(referenceIdField, isDocument, "", false, "|", Integer.MIN_VALUE));
result = Optional.of(new TagField(fieldName, "|", true));
}

return result;
Expand Down Expand Up @@ -757,7 +758,7 @@ private void updateTTLSettings(Class<?> cl, String entityPrefix, boolean isDocum
}
}

private String getKey(String keyspace) {
private String getKeyspace(String keyspace) {
return keyspace.endsWith(":") ? keyspace : keyspace + ":";
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.redis.om.spring.util.ObjectUtils.getKey;

public class RedisJSONKeyValueAdapter extends RedisKeyValueAdapter {
private static final Log logger = LogFactory.getLog(RedisJSONKeyValueAdapter.class);
private final JSONOperations<?> redisJSONOperations;
Expand Down Expand Up @@ -301,10 +303,6 @@ private void processReferences(String key, Object item) {
}
}

protected String getKey(String keyspace, Object id) {
return String.format("%s:%s", keyspace, id);
}

private Optional<Long> getTTLForEntity(Object entity) {
Class entityClass = entity.getClass();
Class entityClassKey;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,6 @@ public GsonBuilder gsonBuilder(List<GsonBuilderCustomizer> customizers) {
return builder;
}

@Bean(name = "referenceAwareGsonBuilder")
ReferenceAwareGsonBuilder referenceAwareGsonBuilder(GsonBuilder gsonBuilder, ApplicationContext ac) {
return new ReferenceAwareGsonBuilder(gsonBuilder, ac);
}

@Bean(name = "redisModulesClient")
@Lazy
RedisModulesClient redisModulesClient( //
Expand All @@ -116,7 +111,7 @@ RedisModulesClient redisModulesClient( //
RedisModulesOperations<?> redisModulesOperations( //
RedisModulesClient rmc, //
StringRedisTemplate template, //
ReferenceAwareGsonBuilder gsonBuilder) {
@Qualifier("omGsonBuilder") GsonBuilder gsonBuilder) {
return new RedisModulesOperations<>(rmc, template, gsonBuilder);
}

Expand Down Expand Up @@ -366,4 +361,16 @@ public void processBloom(ContextRefreshedEvent cre) {
}
}
}

@EventListener(ContextRefreshedEvent.class)
public void registerReferenceSerializer(ContextRefreshedEvent cre) {
logger.info("Registering Reference Serializers......");

ApplicationContext ac = cre.getApplicationContext();
GsonBuilder gsonBuilder = (GsonBuilder)ac.getBean("omGsonBuilder");
GsonReferenceSerializerRegistrar registrar = new GsonReferenceSerializerRegistrar(gsonBuilder, ac);

registrar.registerReferencesFor(Document.class);
registrar.registerReferencesFor(RedisHash.class);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ public synchronized void init(ProcessingEnvironment env) {
processingEnvironment.getTypeUtils();

messager = processingEnvironment.getMessager();
messager.printMessage(Diagnostic.Kind.NOTE, "Redis OM Spring Entity Metamodel Generator");
messager.printMessage(Diagnostic.Kind.NOTE, "🍃 Redis OM Spring Entity Metamodel Generator");

this.objectTypeElement = processingEnvironment.getElementUtils().getTypeElement("java.lang.Object");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
package com.redis.om.spring.ops;

import com.google.gson.GsonBuilder;
import com.redis.om.spring.client.RedisModulesClient;
import com.redis.om.spring.ops.json.JSONOperations;
import com.redis.om.spring.ops.json.JSONOperationsImpl;
import com.redis.om.spring.ops.pds.*;
import com.redis.om.spring.ops.search.SearchOperations;
import com.redis.om.spring.ops.search.SearchOperationsImpl;
import com.redis.om.spring.serialization.gson.ReferenceAwareGsonBuilder;
import org.springframework.data.redis.core.StringRedisTemplate;

public class RedisModulesOperations<K> {

private final ReferenceAwareGsonBuilder gsonBuilder;
private final GsonBuilder gsonBuilder;
private final RedisModulesClient client;
private final StringRedisTemplate template;

public RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, ReferenceAwareGsonBuilder gsonBuilder) {
public RedisModulesOperations(RedisModulesClient client, StringRedisTemplate template, GsonBuilder gsonBuilder) {
this.client = client;
this.template = template;
this.gsonBuilder = gsonBuilder;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,24 @@
package com.redis.om.spring.ops.json;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.redis.om.spring.client.RedisModulesClient;
import com.redis.om.spring.serialization.gson.ReferenceAwareGsonBuilder;
import org.springframework.lang.Nullable;
import redis.clients.jedis.json.JsonSetParams;
import redis.clients.jedis.json.Path;
import redis.clients.jedis.json.Path2;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Objects;

public class JSONOperationsImpl<K> implements JSONOperations<K> {

private final ReferenceAwareGsonBuilder builder;
final RedisModulesClient client;
private Gson gson;
private final GsonBuilder builder;
private final RedisModulesClient client;

public JSONOperationsImpl(RedisModulesClient client, ReferenceAwareGsonBuilder builder) {
public JSONOperationsImpl(RedisModulesClient client, GsonBuilder builder) {
this.client = client;
this.builder = builder;
}
Expand All @@ -35,14 +36,12 @@ public String get(K key) {

@Override
public <T> T get(K key, Class<T> clazz) {
builder.processEntity(clazz);
return builder.gson().fromJson(client.clientForJSON().jsonGetAsPlainString(key.toString(), Path.ROOT_PATH), clazz);
return getGson().fromJson(client.clientForJSON().jsonGetAsPlainString(key.toString(), Path.ROOT_PATH), clazz);
}

@Override
public <T> T get(K key, Class<T> clazz, Path path) {
builder.processEntity(clazz);
return builder.gson().fromJson(client.clientForJSON().jsonGetAsPlainString(key.toString(), path), clazz);
return getGson().fromJson(client.clientForJSON().jsonGetAsPlainString(key.toString(), path), clazz);
}

@SafeVarargs
Expand All @@ -58,23 +57,23 @@ public final List<String> mget(K... keys) {

@SafeVarargs @Override
public final <T> List<T> mget(Class<T> clazz, K... keys) {
builder.processEntity(clazz);
Gson g = getGson();
return (keys.length > 0) ? client.clientForJSON().jsonMGet(getKeysAsString(keys))
.stream()
.filter(Objects::nonNull)
.map(jsonArr -> jsonArr.get(0))
.map(Object::toString)
.map(str -> builder.gson().fromJson(str, clazz))
.map(str -> g.fromJson(str, clazz))
.toList() : List.of();
}

@SafeVarargs @Override
public final <T> List<T> mget(Path2 path, Class<T> clazz, K... keys) {
builder.processEntity(clazz);
Gson g = getGson();
return (keys.length > 0) ? client.clientForJSON().jsonMGet(path, getKeysAsString(keys))
.stream()
.map(Object::toString)
.map(str -> builder.gson().fromJson(str, clazz))
.map(str -> g.fromJson(str, clazz))
.toList() : List.of();
}

Expand All @@ -85,12 +84,12 @@ public void set(K key, Object object, JsonSetParams flag) {

@Override
public void set(K key, Object object) {
client.clientForJSON().jsonSetWithPlainString(key.toString(), Path.ROOT_PATH, builder.gson().toJson(object));
client.clientForJSON().jsonSetWithPlainString(key.toString(), Path.ROOT_PATH, getGson().toJson(object));
}

@Override
public void set(K key, Object object, Path path) {
client.clientForJSON().jsonSetWithPlainString(key.toString(), path, builder.gson().toJson(object));
client.clientForJSON().jsonSetWithPlainString(key.toString(), path, getGson().toJson(object));
}

@Override
Expand Down Expand Up @@ -173,4 +172,10 @@ private String[] getKeysAsString(K... keys) {
return Arrays.stream(keys).map(Object::toString).toArray(String[]::new);
}

private Gson getGson() {
if (gson == null) {
gson = builder.create();
}
return gson;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ public class RediSearchQuery implements RepositoryQuery {
private final BloomQueryExecutor bloomQueryExecutor;
private final AutoCompleteQueryExecutor autoCompleteQueryExecutor;
private final GsonBuilder gsonBuilder;
private Gson gson;

@SuppressWarnings("unchecked")
public RediSearchQuery(//
Expand Down Expand Up @@ -437,7 +438,7 @@ private Object executeQuery(Object[] parameters) {
if (queryMethod.getReturnedObjectType() == SearchResult.class) {
result = searchResult;
} else if (queryMethod.isPageQuery()) {
Gson gson = gsonBuilder.create();
Gson gson = getGson();
List<Object> content = searchResult.getDocuments().stream()
.map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), queryMethod.getReturnedObjectType()))
.collect(Collectors.toList());
Expand All @@ -449,13 +450,13 @@ private Object executeQuery(Object[] parameters) {

} else if (queryMethod.isQueryForEntity() && !queryMethod.isCollectionQuery()) {
if (!searchResult.getDocuments().isEmpty()) {
Gson gson = gsonBuilder.create();
Gson gson = getGson();
Document doc = searchResult.getDocuments().get(0);
Object json = doc != null ? SafeEncoder.encode((byte[])doc.get("$")) : "";
result = gson.fromJson(json.toString(), queryMethod.getReturnedObjectType());
}
} else if (queryMethod.isQueryForEntity() && queryMethod.isCollectionQuery()) {
Gson gson = gsonBuilder.create();
Gson gson = getGson();
result = searchResult.getDocuments().stream()
.map(d -> gson.fromJson(SafeEncoder.encode((byte[])d.get("$")), queryMethod.getReturnedObjectType()))
.collect(Collectors.toList());
Expand Down Expand Up @@ -644,4 +645,11 @@ private String prepareQuery(final Object[] parameters) {

return preparedQuery.toString();
}

private Gson getGson() {
if (gson == null) {
gson = gsonBuilder.create();
}
return gson;
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
package com.redis.om.spring.search.stream;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.redis.om.spring.ops.RedisModulesOperations;

public class EntityStreamImpl implements EntityStream {

final RedisModulesOperations<String> modulesOperations;
final GsonBuilder gsonBuilder;
private final RedisModulesOperations<String> modulesOperations;
private final GsonBuilder gsonBuilder;

@SuppressWarnings("unchecked")
public EntityStreamImpl(RedisModulesOperations<?> rmo, GsonBuilder gsonBuilder) {
Expand All @@ -17,7 +16,7 @@ public EntityStreamImpl(RedisModulesOperations<?> rmo, GsonBuilder gsonBuilder)

@Override
public <E> SearchStream<E> of(Class<E> entityClass) {
return new SearchStreamImpl<>(entityClass, modulesOperations, gsonBuilder.create());
return new SearchStreamImpl<>(entityClass, modulesOperations, gsonBuilder);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public ReturnFieldsSearchStreamImpl(SearchStreamImpl<E> entitySearchStream, List
this.entitySearchStream = entitySearchStream;
this.returning = returning;
this.gson = gson;
useNoContent = returning.size() == 1 && returning.get(0).getSearchFieldAccessor().getField().isAnnotationPresent(Id.class);
this.useNoContent = returning.size() == 1 && returning.get(0).getSearchFieldAccessor().getField().isAnnotationPresent(Id.class);
}

@Override
Expand Down
Loading

0 comments on commit 907adaa

Please sign in to comment.