GH-1949: Align CassandraVectorStore API naming with other vector stores

Fixes: #1949

- Rename 'disallowSchemaChanges(boolean)' to 'initializeSchema(boolean)' for consistency
  with other vector store implementations
- Maintain semantic meaning by inverting the default value (from disallowSchemaChanges=false
  to initializeSchema=true) in the CassandraVectorStore implementation
- Keep default behavior in auto-configuration consistent with other vector stores
- Remove unused 'returnEmbeddings' functionality and related code
- Update test cases to use the new initialization parameter
- Ref docs and javadocs updates

This change improves API consistency across Spring AI vector stores while
preserving the same behavior in the Cassandra implementation.

Signed-off-by: Soby Chacko <soby.chacko@broadcom.com>
This commit is contained in:
Soby Chacko
2025-05-12 17:30:27 -04:00
committed by Mark Pollack
parent 848a3fd31f
commit 868e288e01
7 changed files with 33 additions and 94 deletions

View File

@@ -74,8 +74,7 @@ public class CassandraVectorStoreAutoConfiguration {
.embeddingColumnName(properties.getEmbeddingColumnName())
.indexName(properties.getIndexName())
.fixedThreadPoolExecutorSize(properties.getFixedThreadPoolExecutorSize())
.disallowSchemaChanges(!properties.isInitializeSchema())
.returnEmbeddings(properties.getReturnEmbeddings())
.initializeSchema(properties.isInitializeSchema())
.observationRegistry(observationRegistry.getIfUnique(() -> ObservationRegistry.NOOP))
.customObservationConvention(customObservationConvention.getIfAvailable(() -> null))
.batchingStrategy(batchingStrategy)

View File

@@ -16,9 +16,6 @@
package org.springframework.ai.vectorstore.cassandra.autoconfigure;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.vectorstore.cassandra.CassandraVectorStore;
import org.springframework.ai.vectorstore.properties.CommonVectorStoreProperties;
import org.springframework.boot.context.properties.ConfigurationProperties;
@@ -35,8 +32,6 @@ public class CassandraVectorStoreProperties extends CommonVectorStoreProperties
public static final String CONFIG_PREFIX = "spring.ai.vectorstore.cassandra";
private static final Logger logger = LoggerFactory.getLogger(CassandraVectorStoreProperties.class);
private String keyspace = CassandraVectorStore.DEFAULT_KEYSPACE_NAME;
private String table = CassandraVectorStore.DEFAULT_TABLE_NAME;
@@ -47,8 +42,6 @@ public class CassandraVectorStoreProperties extends CommonVectorStoreProperties
private String embeddingColumnName = CassandraVectorStore.DEFAULT_EMBEDDING_COLUMN_NAME;
private boolean returnEmbeddings = false;
private int fixedThreadPoolExecutorSize = CassandraVectorStore.DEFAULT_ADD_CONCURRENCY;
public String getKeyspace() {
@@ -91,14 +84,6 @@ public class CassandraVectorStoreProperties extends CommonVectorStoreProperties
this.embeddingColumnName = embeddingColumnName;
}
public boolean getReturnEmbeddings() {
return this.returnEmbeddings;
}
public void setReturnEmbeddings(boolean returnEmbeddings) {
this.returnEmbeddings = returnEmbeddings;
}
public int getFixedThreadPoolExecutorSize() {
return this.fixedThreadPoolExecutorSize;
}

View File

@@ -20,9 +20,9 @@ This Spring AI Vector Store is designed to work for both brand-new RAG applicati
The store can also be used for non-RAG use-cases in an existing database, e.g. semantic searches, geo-proximity searches, etc.
The store will automatically create, or enhance, the schema as needed according to its configuration. If you don't want the schema modifications, configure the store with `disallowSchemaChanges`.
The store will automatically create, or enhance, the schema as needed according to its configuration. If you don't want the schema modifications, configure the store with `initializeSchema`.
When using spring-boot-autoconfigure `disallowSchemaChanges` defaults to true, per Spring Boot standards, and you must opt-in to schema creation/modifications by setting `...initialize-schema=true` in the `application.properties` file.
When using spring-boot-autoconfigure `initializeSchema` defaults to `false`, per Spring Boot standards, and you must opt-in to schema creation/modifications by setting `...initialize-schema=true` in the `application.properties` file.
== What is JVector?
@@ -167,7 +167,7 @@ public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel
// Performance tuning
.fixedThreadPoolExecutorSize(32)
// Schema management
.disallowSchemaChanges(false)
.initializeSchema(true)
// Custom batching strategy
.batchingStrategy(new TokenCountBatchingStrategy())
.build();
@@ -282,7 +282,7 @@ public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel
.contentColumnName("body")
.embeddingColumnName("all_minilm_l6_v2_embedding")
.indexName("all_minilm_l6_v2_ann")
.disallowSchemaChanges(true)
.initializeSchema(false)
.addMetadataColumns(extraColumns)
.primaryKeyTranslator((List<Object> primaryKeys) -> {
if (primaryKeys.isEmpty()) {

View File

@@ -95,7 +95,7 @@ import org.springframework.util.Assert;
*
* A schema matching the configuration is automatically created if it doesn't exist.
* Missing columns and indexes in existing tables will also be automatically created.
* Disable this with the CassandraBuilder#disallowSchemaChanges().
* Disable this with the CassandraBuilder#initializeSchema(boolean) method().
*
* <p>
* Basic usage example:
@@ -139,7 +139,7 @@ import org.springframework.util.Assert;
* .contentColumnName("text")
* .embeddingColumnName("vector")
* .fixedThreadPoolExecutorSize(32)
* .disallowSchemaChanges(false)
* .initializeSchema(true)
* .batchingStrategy(new TokenCountBatchingStrategy())
* .build();
* }</pre>
@@ -202,7 +202,7 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
private final Schema schema;
private final boolean disallowSchemaChanges;
private final boolean initializeSchema;
private final FilterExpressionConverter filterExpressionConverter;
@@ -229,7 +229,7 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
this.session = builder.session;
this.schema = builder.buildSchema();
this.disallowSchemaChanges = builder.disallowSchemaChanges;
this.initializeSchema = builder.initializeSchema;
this.documentIdTranslator = builder.documentIdTranslator;
this.primaryKeyTranslator = builder.primaryKeyTranslator;
this.executor = Executors.newFixedThreadPool(builder.fixedThreadPoolExecutorSize);
@@ -525,7 +525,7 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
}
void ensureSchemaExists(int vectorDimension) {
if (!this.disallowSchemaChanges) {
if (this.initializeSchema) {
SchemaUtil.ensureKeyspaceExists(this.session, this.schema.keyspace);
ensureTableExists(vectorDimension);
ensureTableColumnsExist(vectorDimension);
@@ -805,7 +805,7 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
private Set<SchemaColumn> metadataColumns = new HashSet<>();
private boolean disallowSchemaChanges = false;
private boolean initializeSchema = true;
private int fixedThreadPoolExecutorSize = DEFAULT_ADD_CONCURRENCY;
@@ -821,8 +821,6 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
return (String) primaryKeyColumns.get(0);
};
private boolean returnEmbeddings = false;
private Builder(EmbeddingModel embeddingModel) {
super(embeddingModel);
}
@@ -938,12 +936,12 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
}
/**
* Sets whether to disallow schema changes.
* @param disallowSchemaChanges true to disallow schema changes
* Sets whether to initialize the schema.
* @param initializeSchema true to initialize schema, false otherwise
* @return the builder instance
*/
public Builder disallowSchemaChanges(boolean disallowSchemaChanges) {
this.disallowSchemaChanges = disallowSchemaChanges;
public Builder initializeSchema(boolean initializeSchema) {
this.initializeSchema = initializeSchema;
return this;
}
@@ -1016,11 +1014,6 @@ public class CassandraVectorStore extends AbstractObservationVectorStore impleme
return this;
}
public Builder returnEmbeddings(boolean returnEmbeddings) {
this.returnEmbeddings = true;
return this;
}
Schema buildSchema() {
if (this.indexName == null) {
this.indexName = String.format("%s_%s_%s", this.table, this.embeddingColumnName, DEFAULT_INDEX_SUFFIX);

View File

@@ -150,7 +150,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void ensureSchemaCreation() {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
Assertions.assertNotNull(store);
store.checkSchemaValid();
store.similaritySearch(SearchRequest.builder().query("1843").topK(1).build());
@@ -162,7 +162,7 @@ class CassandraRichSchemaVectorStoreIT {
void ensureSchemaNoCreation() {
this.contextRunner.run(context -> {
executeCqlFile(context, "test_wiki_full_schema.cql");
var builder = createBuilder(context, List.of(), true, false);
var builder = createBuilder(context, List.of(), false, false);
Assertions.assertNotNull(builder);
var store = new CassandraVectorStore(builder);
try {
@@ -176,7 +176,7 @@ class CassandraRichSchemaVectorStoreIT {
// IllegalStateException: column all_minilm_l6_v2_embedding does not exist
IllegalStateException ise = Assertions.assertThrows(IllegalStateException.class,
() -> createStore(context, List.of(), true, false));
() -> createStore(context, List.of(), false, false));
Assertions.assertEquals("column all_minilm_l6_v2_embedding does not exist", ise.getMessage());
}
@@ -193,7 +193,7 @@ class CassandraRichSchemaVectorStoreIT {
int PARTIAL_FILES = 5;
for (int i = 0; i < PARTIAL_FILES; ++i) {
executeCqlFile(context, java.lang.String.format("test_wiki_partial_%d_schema.cql", i));
var builder = createBuilder(context, List.of(), false, false);
var builder = createBuilder(context, List.of(), true, false);
Assertions.assertNotNull(builder);
CassandraVectorStore.dropKeyspace(builder);
var store = builder.build();
@@ -216,7 +216,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void addAndSearch() {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> results = store.similaritySearch(
@@ -290,7 +290,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void searchWithPartitionFilter() throws InterruptedException {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> results = store
@@ -346,7 +346,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void unsearchableFilters() throws InterruptedException {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> results = store
@@ -367,7 +367,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void searchWithFilters() throws InterruptedException {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> results = store
@@ -447,7 +447,7 @@ class CassandraRichSchemaVectorStoreIT {
new SchemaColumn("title", DataTypes.TEXT, CassandraVectorStore.SchemaColumnTags.INDEXED),
new SchemaColumn("chunk_no", DataTypes.INT, CassandraVectorStore.SchemaColumnTags.INDEXED));
try (CassandraVectorStore store = createStore(context, overrides, false, true)) {
try (CassandraVectorStore store = createStore(context, overrides, true, true)) {
store.add(documents);
@@ -481,7 +481,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void documentUpdate() {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> results = store
@@ -532,7 +532,7 @@ class CassandraRichSchemaVectorStoreIT {
@Test
void searchWithThreshold() {
this.contextRunner.run(context -> {
try (CassandraVectorStore store = createStore(context, false)) {
try (CassandraVectorStore store = createStore(context, true)) {
store.add(documents);
List<Document> fullResult = store.similaritySearch(
@@ -562,19 +562,16 @@ class CassandraRichSchemaVectorStoreIT {
});
}
private CassandraVectorStore createStore(ApplicationContext context, boolean disallowSchemaCreation)
throws IOException {
private CassandraVectorStore createStore(ApplicationContext context, boolean initializeSchema) throws IOException {
return createStore(context, List.of(), disallowSchemaCreation, true);
return createStore(context, List.of(), initializeSchema, true);
}
private CassandraVectorStore createStore(ApplicationContext context, List<SchemaColumn> columnOverrides,
boolean disallowSchemaCreation, boolean dropKeyspaceFirst) throws IOException {
boolean initializeSchema, boolean dropKeyspaceFirst) throws IOException {
CassandraVectorStore.Builder builder = storeBuilder(context, columnOverrides);
if (disallowSchemaCreation) {
builder = builder.disallowSchemaChanges(true);
}
builder.initializeSchema(initializeSchema);
if (dropKeyspaceFirst) {
CassandraVectorStore.dropKeyspace(builder);
@@ -584,12 +581,10 @@ class CassandraRichSchemaVectorStoreIT {
}
private CassandraVectorStore.Builder createBuilder(ApplicationContext context, List<SchemaColumn> columnOverrides,
boolean disallowSchemaCreation, boolean dropKeyspaceFirst) throws IOException {
boolean initailzeSchema, boolean dropKeyspaceFirst) throws IOException {
CassandraVectorStore.Builder builder = storeBuilder(context, columnOverrides);
if (disallowSchemaCreation) {
builder = builder.disallowSchemaChanges(true);
}
builder.initializeSchema(initailzeSchema);
if (dropKeyspaceFirst) {
CassandraVectorStore.dropKeyspace(builder);

View File

@@ -172,39 +172,6 @@ class CassandraVectorStoreIT extends BaseVectorStoreTests {
});
}
@Test
void addAndSearchReturnEmbeddings() {
this.contextRunner.run(context -> {
CassandraVectorStore.Builder builder = storeBuilder(context.getBean(CqlSession.class),
context.getBean(EmbeddingModel.class))
.returnEmbeddings(true);
try (CassandraVectorStore store = createTestStore(context, builder)) {
List<Document> documents = documents();
store.add(documents);
List<Document> results = store
.similaritySearch(SearchRequest.builder().query("Spring").topK(1).build());
assertThat(results).hasSize(1);
Document resultDoc = results.get(0);
assertThat(resultDoc.getId()).isEqualTo(documents().get(0).getId());
assertThat(resultDoc.getText()).contains(
"Spring AI provides abstractions that serve as the foundation for developing AI applications.");
assertThat(resultDoc.getMetadata()).hasSize(1);
assertThat(resultDoc.getMetadata()).containsKey(DocumentMetadata.DISTANCE.value());
// Remove all documents from the store
store.delete(documents().stream().map(doc -> doc.getId()).toList());
results = store.similaritySearch(SearchRequest.builder().query("Spring").topK(1).build());
assertThat(results).isEmpty();
}
});
}
@Test
void searchWithPartitionFilter() throws InterruptedException {
this.contextRunner.run(context -> {

View File

@@ -102,7 +102,7 @@ class WikiVectorStoreExample {
.contentColumnName("body")
.embeddingColumnName("all_minilm_l6_v2_embedding")
.indexName("all_minilm_l6_v2_ann")
.disallowSchemaChanges(true)
.initializeSchema(false)
.addMetadataColumns(extraColumns)
.primaryKeyTranslator((List<Object> primaryKeys) -> {
// the deliminator used to join fields together into the document's id