Skip to content

Commit

Permalink
feature: introduce @IndexingOptions annotation to control index lifec…
Browse files Browse the repository at this point in the history
…ycle
  • Loading branch information
bsbodden committed May 21, 2024
1 parent 40d72ca commit 7205e1c
Show file tree
Hide file tree
Showing 12 changed files with 190 additions and 4 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.redis.om.spring.annotations;

public enum IndexCreationMode {
SKIP_IF_EXIST,
SKIP_ALWAYS,
DROP_AND_RECREATE
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.redis.om.spring.annotations;

import java.lang.annotation.*;

@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface IndexingOptions {
IndexCreationMode creationMode() default IndexCreationMode.SKIP_IF_EXIST;
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import org.springframework.data.util.TypeInformation;
import org.springframework.stereotype.Component;
import org.springframework.util.ClassUtils;
import redis.clients.jedis.exceptions.JedisDataException;
import redis.clients.jedis.search.FTCreateParams;
import redis.clients.jedis.search.FieldName;
import redis.clients.jedis.search.IndexDataType;
Expand Down Expand Up @@ -92,6 +93,7 @@ public void createIndexFor(Class<?> cl) {
boolean isDocument = idxType == IndexDataType.JSON;
Optional<Document> document = isDocument ? Optional.of(cl.getAnnotation(Document.class)) : Optional.empty();
Optional<RedisHash> hash = !isDocument ? Optional.of(cl.getAnnotation(RedisHash.class)) : Optional.empty();
Optional<IndexingOptions> maybeIndexingOptions = Optional.ofNullable(cl.getAnnotation(IndexingOptions.class));

String indexName = "";
Optional<String> maybeScoreField;
Expand Down Expand Up @@ -137,7 +139,30 @@ public void createIndexFor(Class<?> cl) {
List<SchemaField> fields = searchFields.stream().map(SearchField::getSchemaField).toList();
entityClassToSchema.put(cl, searchFields);
entityClassToIndexName.put(cl, indexName);
opsForSearch.createIndex(params, fields);
if (maybeIndexingOptions.isPresent()) {
IndexingOptions options = maybeIndexingOptions.get();
switch (options.creationMode()) {
case SKIP_IF_EXIST:
opsForSearch.createIndex(params, fields);
logger.info(String.format("Created index %s...", indexName));
break;
case DROP_AND_RECREATE:
if (indexExistsFor(cl)) {
opsForSearch.dropIndex();
logger.info(String.format("Dropped index %s", indexName));
}
opsForSearch.createIndex(params, fields);
logger.info(String.format("Created index %s", indexName));
break;
case SKIP_ALWAYS:
// do nothing and like it!
logger.info(String.format("Skipped index creation for %s", cl.getSimpleName()));
break;
}
} else {
opsForSearch.createIndex(params, fields);
logger.info(String.format("Created index %s", indexName));
}
} catch (Exception e) {
logger.warn(String.format(SKIPPING_INDEX_CREATION, indexName, e.getMessage()));
}
Expand Down Expand Up @@ -197,10 +222,28 @@ public String getKeyspaceForEntityClass(Class<?> entityClass) {
return keyspace;
}

public boolean indexExistsFor(Class<?> entityClass) {
public boolean indexDefinitionExistsFor(Class<?> entityClass) {
return indexedEntityClasses.contains(entityClass);
}

public boolean indexExistsFor(Class<?> entityClass) {
try {
return getIndexInfo(entityClass) != null;
} catch (JedisDataException jde) {
if (jde.getMessage().contains("Unknown index name")) {
return false;
} else {
throw jde;
}
}
}

Map<String,Object> getIndexInfo(Class<?> entityClass) {
String indexName = entityClassToIndexName.get(entityClass);
SearchOperations<String> opsForSearch = rmo.opsForSearch(indexName);
return opsForSearch.getInfo();
}

public List<SearchField> getSchemaFor(Class<?> entityClass) {
return entityClassToSchema.get(entityClass);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -386,7 +386,7 @@ public Page<T> findAll(Pageable pageable) {
return new PageImpl<>(result, Pageable.unpaged(), result.size());
}

if (indexer.indexExistsFor(metadata.getJavaType())) {
if (indexer.indexDefinitionExistsFor(metadata.getJavaType())) {
Optional<String> maybeSearchIndex = indexer.getIndexName(metadata.getJavaType());
if (maybeSearchIndex.isPresent()) {
String searchIndex = maybeSearchIndex.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ public Page<T> findAll(Pageable pageable) {
return new PageImpl<>(result, Pageable.unpaged(), result.size());
}

if (indexer.indexExistsFor(metadata.getJavaType())) {
if (indexer.indexDefinitionExistsFor(metadata.getJavaType())) {
Optional<String> maybeSearchIndex = indexer.getIndexName(metadata.getJavaType());
if (maybeSearchIndex.isPresent()) {
String searchIndex = maybeSearchIndex.get();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.redis.om.spring.fixtures.document.model;

import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.IndexingOptions;
import lombok.Data;
import lombok.NonNull;
import org.springframework.data.annotation.Id;

import static com.redis.om.spring.annotations.IndexCreationMode.DROP_AND_RECREATE;

@Data
@Document
@IndexingOptions(creationMode = DROP_AND_RECREATE)
public class ModelDropAndRecreate {
@Id
private String id;
@NonNull
@Indexed
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.redis.om.spring.fixtures.document.model;

import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.IndexingOptions;
import lombok.Data;
import lombok.NonNull;
import org.springframework.data.annotation.Id;

import static com.redis.om.spring.annotations.IndexCreationMode.SKIP_ALWAYS;

@Data
@Document
@IndexingOptions(creationMode = SKIP_ALWAYS)
public class ModelSkipAlways {
@Id
private String id;
@NonNull
@Indexed
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.redis.om.spring.fixtures.document.model;

import com.redis.om.spring.annotations.Document;
import com.redis.om.spring.annotations.Indexed;
import com.redis.om.spring.annotations.IndexingOptions;
import lombok.Data;
import lombok.NonNull;
import org.springframework.data.annotation.Id;

import static com.redis.om.spring.annotations.IndexCreationMode.SKIP_IF_EXIST;

@Data
@Document
@IndexingOptions(creationMode = SKIP_IF_EXIST)
public class ModelSkipIfExist {
@Id
private String id;
@NonNull
@Indexed
private String name;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.redis.om.spring.fixtures.document.repository;

import com.redis.om.spring.fixtures.document.model.ModelDropAndRecreate;
import com.redis.om.spring.repository.RedisDocumentRepository;

public interface ModelDropAndRecreateRepository extends RedisDocumentRepository<ModelDropAndRecreate, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.redis.om.spring.fixtures.document.repository;

import com.redis.om.spring.fixtures.document.model.ModelSkipAlways;
import com.redis.om.spring.repository.RedisDocumentRepository;

public interface ModelSkipAlwaysRepository extends RedisDocumentRepository<ModelSkipAlways, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.redis.om.spring.fixtures.document.repository;

import com.redis.om.spring.fixtures.document.model.ModelSkipIfExist;
import com.redis.om.spring.repository.RedisDocumentRepository;

public interface ModelSkipIfExistsRepository extends RedisDocumentRepository<ModelSkipIfExist, String> {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.redis.om.spring.indexing;

import com.redis.om.spring.AbstractBaseDocumentTest;
import com.redis.om.spring.fixtures.document.model.ModelDropAndRecreate;
import com.redis.om.spring.fixtures.document.model.ModelSkipAlways;
import com.redis.om.spring.fixtures.document.model.ModelSkipIfExist;
import com.redis.om.spring.fixtures.document.repository.ModelDropAndRecreateRepository;
import com.redis.om.spring.fixtures.document.repository.ModelSkipAlwaysRepository;
import com.redis.om.spring.fixtures.document.repository.ModelSkipIfExistsRepository;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.annotation.DirtiesContext;

import static org.assertj.core.api.Assertions.assertThat;

public class IndexingOptionsTest extends AbstractBaseDocumentTest {
@Autowired
ModelSkipIfExistsRepository modelSkipIfExistRepository;
@Autowired
ModelSkipAlwaysRepository modelSkipAlwaysRepository;
@Autowired
ModelDropAndRecreateRepository modelDropAndRecreateRepository;

@Test
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
void testSkipIfExists() {
assertThat(indexer.indexExistsFor(ModelSkipIfExist.class)).isTrue();
}

@Test
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
void testSkipAlways() {
assertThat(indexer.indexExistsFor(ModelSkipAlways.class)).isFalse();
}

@Test
@DirtiesContext(methodMode = DirtiesContext.MethodMode.BEFORE_METHOD)
void testDropAndRecreate() {
assertThat(indexer.indexExistsFor(ModelDropAndRecreate.class)).isTrue();
}

}

0 comments on commit 7205e1c

Please sign in to comment.