Version current

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:

  1. 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

  2. With RECREATE_INDEXES:

    • Drops the index if it exists

    • Creates a new index

  3. 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);
    }
}

Redis CLI Commands

You can also manage indices directly using the Redis CLI:

# List all indices
FT._LIST

# Get index info
FT.INFO product-idx

# Drop an index
FT.DROPINDEX product-idx

Best Practices

  • Choose the appropriate index creation mode for your environment

  • Use CREATE_IF_NOT_EXIST for production to avoid disruption

  • Use RECREATE_INDEXES during 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