Index Creation and Management
Introduction to Index Creation
Redis OM Spring automates the creation and management of Redis Query Engine indices based on your entity annotations. As of Redis 8, the Query Engine is a standard component of Redis. The process involves:
-
Entity annotations (@Document, @Indexed, etc.) define the structure
-
Index creation process generates appropriate Redis Query Engine indices
-
Different creation modes control how indices are managed
Benefits include fast queries, full-text search, numeric range queries, geospatial search, and vector similarity search.
Index Creation Process
Automatic Index Creation
When your application starts, Redis OM Spring scans for annotated entity classes and creates the necessary indices:
@Document(indexName = "product-idx")
public class Product {
@Id
private String id;
@TextIndexed
private String name;
@TextIndexed(weight = 0.5)
private String description;
@NumericIndexed
private double price;
@TagIndexed
private List<String> categories;
// Other fields, getters, setters
}
Based on these annotations, Redis OM Spring will create a Redis Query Engine index named product-idx with appropriate field definitions.
Index Creation Modes
Control how indices are created using the indexCreationMode attribute:
@Document(indexName = "product-idx", indexCreationMode = IndexCreationMode.RECREATE_INDEXES)
public class Product {
// Fields
}
Available modes:
-
CREATE_IF_NOT_EXIST(default) - Only create the index if it doesn’t exist -
RECREATE_INDEXES- Drop and recreate the index on application startup -
NO_CREATE_NO_DROP- Don’t create or drop indices (manual management)
The index creation process varies based on the selected mode:
-
With
CREATE_IF_NOT_EXIST(default):-
Redis OM Spring checks if the index already exists
-
If it doesn’t exist, creates the index
-
If it exists, skips creation
-
-
With
RECREATE_INDEXES:-
Drops the index if it exists
-
Creates a new index
-
-
With
NO_CREATE_NO_DROP:-
Skips all index operations
-
Advanced Index Configuration
@IndexingOptions
The @IndexingOptions annotation provides advanced configuration for your indices:
@Document
@IndexingOptions(
stopWords = {"a", "an", "the", "in", "on", "at"},
language = "English",
prefixes = {"product:"}
)
public class Product {
// Fields
}
Configuration options:
-
stopWords- Words to ignore in text searches -
language- Default language for stemming (e.g., "English", "French", "German") -
prefixes- Key prefixes to include in the index -
filter- Custom filtering expression for the index -
maxTextFields- Whether to index all text fields -
temporaryIndex- Create a temporary index that will be deleted when the connection closes
Dynamic Index Naming and Multi-Tenant Support
Redis OM Spring provides powerful dynamic indexing capabilities using Spring Expression Language (SpEL) for runtime index name resolution. This enables multi-tenant applications, environment-specific indices, and flexible index naming strategies.
SpEL Expressions in Index Names
Use SpEL expressions in the indexName attribute to create dynamic index names:
@Document
@IndexingOptions(indexName = "#{@environment.getProperty('app.tenant')}_products_idx")
public class Product {
@Id
private String id;
@Indexed
private String name;
}
The SpEL expression #{@environment.getProperty('app.tenant')}_products_idx will be evaluated at runtime, allowing different tenants to have separate indices.
Environment-Based Index Names
Create environment-specific indices using application properties:
# application.properties
app.environment=production
app.tenant=acme-corp
app.version=1.2.3
@Document
@IndexingOptions(indexName = "#{@environment.getProperty('app.environment')}_#{@environment.getProperty('app.tenant')}_orders")
public class Order {
@Id
private String id;
@Indexed
private String status;
}
This creates indices like production_acme-corp_orders based on your configuration.
Bean References in SpEL
Reference Spring beans in your SpEL expressions for complex logic:
@Service
public class TenantResolver {
public String getCurrentTenant() {
// Custom logic to determine current tenant
return SecurityContextHolder.getContext().getAuthentication().getName();
}
}
@Document
@IndexingOptions(indexName = "tenant_#{@tenantResolver.getCurrentTenant()}_users_v#{@versionService.getMajorVersion()}")
public class User {
@Id
private String id;
@Searchable
private String username;
}
Multi-Tenant Index Context
Use RedisIndexContext for thread-local tenant isolation:
@Service
public class MultiTenantService {
@Autowired
private RediSearchIndexer indexer;
public void setupTenantContext(String tenantId, String environment) {
RedisIndexContext context = RedisIndexContext.builder()
.tenantId(tenantId)
.environment(environment)
.setAttribute("region", "us-west")
.build();
RedisIndexContext.setContext(context);
// All subsequent operations use this context
indexer.createIndexFor(Product.class);
}
public void clearContext() {
RedisIndexContext.clearContext();
}
}
Custom Index Resolvers
Implement custom index resolution logic:
@Component
public class CustomIndexResolver implements IndexResolver {
@Override
public String resolveIndexName(Class<?> entityClass, RedisIndexContext context) {
if (context != null && context.getTenantId() != null) {
return String.format("%s_%s_%s_idx",
context.getTenantId(),
context.getEnvironment(),
entityClass.getSimpleName().toLowerCase());
}
return entityClass.getSimpleName().toLowerCase() + "_idx";
}
@Override
public String resolveKeyPrefix(Class<?> entityClass, RedisIndexContext context) {
if (context != null && context.getTenantId() != null) {
return context.getTenantId() + ":" + entityClass.getSimpleName().toLowerCase() + ":";
}
return entityClass.getSimpleName().toLowerCase() + ":";
}
}
SpEL Expression Features
Supported SpEL features include:
-
Environment Properties:
#{@environment.getProperty('key')} -
Bean Method Calls:
#{@myService.getValue()} -
System Properties:
#{T(System).getProperty('key')} -
String Operations:
#{someValue.toUpperCase()} -
Conditional Logic:
#{condition ? 'value1' : 'value2'} -
Mathematical Operations:
#{value1 + value2}
Error Handling and Fallbacks
Redis OM Spring provides robust error handling for SpEL expressions:
@Document
@IndexingOptions(indexName = "#{@environment.getProperty('missing.property', 'fallback')}_idx")
public class SafeEntity {
// If 'missing.property' doesn't exist, uses 'fallback'
}
For malformed expressions or evaluation errors, the system falls back to the default index naming pattern based on the entity class name.
Index Migration Service
Manage index migrations in multi-tenant environments:
@Service
public class TenantMigrationService {
@Autowired
private IndexMigrationService migrationService;
public void migrateTenantIndices(String tenantId) {
RedisIndexContext context = RedisIndexContext.builder()
.tenantId(tenantId)
.build();
RedisIndexContext.setContext(context);
// Migrate all entity indices for this tenant
MigrationResult result = migrationService.migrateIndex(
Product.class,
MigrationStrategy.BLUE_GREEN
);
if (result.isSuccessful()) {
log.info("Migration completed for tenant: {}", tenantId);
}
}
}
Best Practices for Dynamic Indexing
-
Use consistent naming patterns across your application
-
Validate SpEL expressions during development
-
Implement proper tenant isolation in multi-tenant applications
-
Monitor index creation and memory usage
-
Use environment-specific configurations for different deployment stages
-
Implement graceful fallbacks for expression evaluation failures
-
Consider index naming length limits in Redis
-
Test with realistic tenant scenarios during development
Field-Level Configuration
Configure individual fields with specific indexing options:
@Document
public class Article {
@Id
private String id;
@TextIndexed(sortable = true, weight = 1.5, nostem = true)
private String title;
@TextIndexed(weight = 1.0)
private String content;
@TextIndexed(phonetic = "dm:en")
private String authorName;
// Other fields
}
Manual Index Management
Programmatic Index Creation
For advanced cases, you can manage indices programmatically using the RediSearchIndexer class:
@Service
public class IndexService {
@Autowired
private RediSearchIndexer indexer;
public void recreateIndex(Class<?> entityClass) {
// Drop the existing index and documents for the entity
indexer.dropIndexAndDocumentsFor(entityClass);
// Create a new index for the entity
indexer.createIndexFor(entityClass);
}
}
Best Practices
-
Choose the appropriate index creation mode for your environment
-
Use
CREATE_IF_NOT_EXISTfor production to avoid disruption -
Use
RECREATE_INDEXESduring development for schema changes -
Configure language settings for proper text searching
-
Define appropriate stop words for your domain
-
Consider index size and memory usage
-
Be careful with sortable fields as they increase memory usage