How to create secondary index in Spring Data Aerospike
Secondary indexes are on a non-primary key, which allows you to model one-to-many relationships.
Some of the information in this article is likely outdated, for the current documentation and usage examples please refer to Spring Data Aerospike.
Spring-data-aerospike supports creating secondary indexes in Aerospike out of the box.
There are two ways to accomplish this task:
Using AerospikeTemplate createIndex method; or
Using @Indexed annotation placed over the field in your entity.
Let’s dive into more details.
Note: Before continuing it is expected that your project has spring-data-aerospike already setup. Please check this guide to find out how to do it.
First approach — creating index via AerospikeTemplate
In this example we will create an index at startup of the application manually.
package com.example.demo.persistence.index;import com.aerospike.client.query.IndexType;
import com.example.demo.persistence.simplecrud.MovieDocument;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.aerospike.IndexAlreadyExistsException;
import org.springframework.data.aerospike.core.AerospikeTemplate;@Slf4j
@Configuration
public class AerospikeIndexConfiguration { private static final String INDEX_NAME = "movie-rating-index"; @Bean
@ConditionalOnProperty(
value = "aerospike." + INDEX_NAME + ".create-on-startup",
havingValue = "true",
matchIfMissing = true)
public boolean createAerospikeIndex(AerospikeTemplate aerospikeTemplate) {
try {
aerospikeTemplate.createIndex(MovieDocument.class, INDEX_NAME, "rating", IndexType.NUMERIC);
log.info("Index {} was successfully created", INDEX_NAME);
} catch (IndexAlreadyExistsException e) {
log.info("Index {} already exists, skipped creating", INDEX_NAME);
}
return true;
}
}Second approach — creating index via @Indexed annotation
Place @Indexed annotation over the field that you want to index in your entity and specify required types of the index. This will make spring-data-aerospike to auto-create specified secondary index in Aerospike on startup of your application.
Note:@Indexed annotation is not supported for the fields annotated with @Id, @Expiration or @Version annotations.
package com.example.demo.persistence.index;import lombok.Value;
import org.springframework.data.aerospike.annotation.Indexed;
import org.springframework.data.aerospike.mapping.Document;
import org.springframework.data.annotation.Id;import java.util.List;import static com.aerospike.client.query.IndexCollectionType.DEFAULT;
import static com.aerospike.client.query.IndexCollectionType.LIST;
import static com.aerospike.client.query.IndexType.NUMERIC;
import static com.aerospike.client.query.IndexType.STRING;@Value
@Document
public class IndexedDocument { @Id
String key; @Indexed(type = STRING, collectionType = DEFAULT)
String author; @Indexed(type = NUMERIC, collectionType = DEFAULT)
int likes; @Indexed(type = NUMERIC, collectionType = LIST)
List<Integer> options;
}Testing
Verify indexes were created using the following tests:
package com.example.demo;import com.aerospike.client.AerospikeClient;
import com.aerospike.client.Info;
import com.aerospike.client.cluster.Node;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;import static org.assertj.core.api.Assertions.assertThat;public class IndexTests extends DemoApplicationTests { @Value("${embedded.aerospike.namespace}")
String namespace; @Autowired
AerospikeClient client;
@Test
void verifyCustomIndexCreated() {
List<String> existingIndexes = getIndexes(client, namespace); assertThat(existingIndexes).contains("movie-rating-index");
} @Test
void verifyAnnotationBasedIndexesCreated() {
List<String> existingIndexes = getIndexes(client, namespace); assertThat(existingIndexes)
.contains(
"IndexedDocument_author_string_default",
"IndexedDocument_likes_numeric_default",
"IndexedDocument_options_numeric_list");
} // DO NOT USE THIS CODE IN PRODUCTION
private static List<String> getIndexes(AerospikeClient client, String namespace) {
Node node = client.getNodes()[0];
String response = Info.request(node, "sindex/" + namespace);
return Arrays.stream(response.split(";"))
.map(info -> {
Map<String, String> keyValue = Arrays.stream(info.split(":"))
.map(part -> {
String[] kvParts = part.split("=");
return Map.entry(kvParts[0], kvParts[1]);
})
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
return keyValue.get("indexname");
})
.collect(Collectors.toList());
}}Demo project is located on GitHub — https://github.com/aerospike-community/spring-data-aerospike-demo.
Keep reading

Mar 18, 2026
How Myntra built a GenAI shopping assistant that remembers, recommends, and responds in milliseconds

Mar 12, 2026
How Unity replaced Redis to scale its ad platform to 10 million ops

Mar 10, 2026
AKO 4.2: The evolution of production-grade Aerospike on Kubernetes

Mar 3, 2026
How Aerospike and ScyllaDB behave under production stress
