Update VectorStore docs to use inner builder class
Updated the manual configuration examples in the following docs to show the correct usage of the inner builder class: - azure.adoc: Show builder(searchIndexClient, embeddingModel) with all available options - chroma.adoc: Show builder(chromaApi, embeddingModel) with collection config - oracle.adoc: Show builder(jdbcTemplate, embeddingModel) with database options The examples now reflect the current implementation where the builder takes both the client and embedding model as constructor arguments.
This commit is contained in:
@@ -97,90 +97,109 @@ You can use the following properties in your Spring Boot configuration to custom
|
||||
|
||||
=== Basic Usage
|
||||
|
||||
Create a CassandraVectorStore instance using the builder pattern:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
|
||||
return CassandraVectorStore.builder()
|
||||
.session(session)
|
||||
.embeddingModel(embeddingModel)
|
||||
.keyspace("my_keyspace")
|
||||
.table("my_vectors")
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The default configuration connects to Cassandra at `localhost:9042` and will automatically create a default schema in keyspace `springframework`, table `ai_vector_store`.
|
||||
====
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
The Cassandra Java Driver is easiest configured via an `application.conf` file on the classpath. More info https://github.com/apache/cassandra-java-driver/tree/4.x/manual/core/configuration[here].
|
||||
====
|
||||
|
||||
Then in your main code, create and add some documents:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<Document> documents = List.of(
|
||||
new Document("Spring AI rocks!! Spring AI rocks!! Spring AI rocks!!",
|
||||
Map.of("country", "UK", "year", 2020)),
|
||||
new Document("The World is Big and Salvation Lurks Around the Corner",
|
||||
Map.of()),
|
||||
new Document("You walk forward facing the past and you turn back toward the future.",
|
||||
Map.of("country", "NL", "year", 2023)));
|
||||
|
||||
vectorStore.add(documents);
|
||||
----
|
||||
|
||||
And retrieve documents similar to a query:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<Document> results = vectorStore.similaritySearch(
|
||||
SearchRequest.builder().query("Spring").topK(5).build());
|
||||
----
|
||||
|
||||
You can also limit results based on a similarity threshold:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
List<Document> results = vectorStore.similaritySearch(
|
||||
SearchRequest.builder().query("Spring")
|
||||
.topK(5)
|
||||
.similarityThreshold(0.5d).build());
|
||||
----
|
||||
|
||||
=== Advanced Configuration
|
||||
|
||||
For more complex scenarios, the builder pattern offers extensive configuration options:
|
||||
Create a CassandraVectorStore instance as a Spring Bean:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
|
||||
return CassandraVectorStore.builder(embeddingModel)
|
||||
.session(session)
|
||||
.keyspace("my_keyspace")
|
||||
.table("my_vectors")
|
||||
.partitionKeys(List.of(new SchemaColumn("id", DataTypes.TEXT)))
|
||||
.clusteringKeys(List.of(new SchemaColumn("timestamp", DataTypes.TIMESTAMP)))
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
Once you have the vector store instance, you can add documents and perform searches:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
// Add documents
|
||||
vectorStore.add(List.of(
|
||||
new Document("1", "content1", Map.of("key1", "value1")),
|
||||
new Document("2", "content2", Map.of("key2", "value2"))
|
||||
));
|
||||
|
||||
// Search with filters
|
||||
List<Document> results = vectorStore.similaritySearch(
|
||||
SearchRequest.query("search text")
|
||||
.withTopK(5)
|
||||
.withSimilarityThreshold(0.7f)
|
||||
.withFilterExpression("metadata.key1 == 'value1'")
|
||||
);
|
||||
----
|
||||
|
||||
=== Advanced Configuration
|
||||
|
||||
For more complex use cases, you can configure additional settings in your Spring Bean:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
|
||||
return CassandraVectorStore.builder(embeddingModel)
|
||||
.session(session)
|
||||
.keyspace("my_keyspace")
|
||||
.table("my_vectors")
|
||||
// Configure primary keys
|
||||
.partitionKeys(List.of(
|
||||
new SchemaColumn("id", DataTypes.TEXT),
|
||||
new SchemaColumn("category", DataTypes.TEXT)
|
||||
))
|
||||
.clusteringKeys(List.of(
|
||||
new SchemaColumn("timestamp", DataTypes.TIMESTAMP)
|
||||
))
|
||||
// Add metadata columns with optional indexing
|
||||
.addMetadataColumns(
|
||||
new SchemaColumn("category", DataTypes.TEXT, SchemaColumnTags.INDEXED),
|
||||
new SchemaColumn("score", DataTypes.DOUBLE)
|
||||
)
|
||||
// Customize column names
|
||||
.contentColumnName("text")
|
||||
.embeddingColumnName("vector")
|
||||
// Performance tuning
|
||||
.fixedThreadPoolExecutorSize(32)
|
||||
// Schema management
|
||||
.disallowSchemaChanges(false)
|
||||
// Custom batching strategy
|
||||
.batchingStrategy(new TokenCountBatchingStrategy())
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
=== Connection Configuration
|
||||
|
||||
There are two ways to configure the connection to Cassandra:
|
||||
|
||||
* Using an injected CqlSession (recommended):
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
|
||||
return CassandraVectorStore.builder(embeddingModel)
|
||||
.session(session)
|
||||
.keyspace("my_keyspace")
|
||||
.table("my_vectors")
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
* Using connection details directly in the builder:
|
||||
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
|
||||
return CassandraVectorStore.builder(embeddingModel)
|
||||
.contactPoint(new InetSocketAddress("localhost", 9042))
|
||||
.localDatacenter("datacenter1")
|
||||
.keyspace("my_keyspace")
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
=== Metadata Filtering
|
||||
|
||||
You can leverage the generic, portable metadata filters with the CassandraVectorStore. For metadata columns to be searchable they must be either primary keys or SAI indexed. To make non-primary-key columns indexed, configure the metadata column with the `SchemaColumnTags.INDEXED`.
|
||||
|
||||
@@ -148,84 +148,51 @@ List<Document> results = vectorStore.similaritySearch(SearchRequest.builder().qu
|
||||
|
||||
The following code demonstrates how to set up the `CosmosDBVectorStore` without relying on auto-configuration:
|
||||
|
||||
```java
|
||||
package com.example.demo;
|
||||
[source,java]
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(ObservationRegistry observationRegistry) {
|
||||
// Create the Cosmos DB client
|
||||
CosmosAsyncClient cosmosClient = new CosmosClientBuilder()
|
||||
.endpoint(System.getenv("COSMOSDB_AI_ENDPOINT"))
|
||||
.key(System.getenv("COSMOSDB_AI_KEY"))
|
||||
.userAgentSuffix("SpringAI-CDBNoSQL-VectorStore")
|
||||
.gatewayMode()
|
||||
.buildAsyncClient();
|
||||
|
||||
import com.azure.cosmos.CosmosAsyncClient;
|
||||
import com.azure.cosmos.CosmosClientBuilder;
|
||||
import io.micrometer.observation.ObservationRegistry;
|
||||
import org.springframework.ai.document.Document;
|
||||
import org.springframework.ai.embedding.EmbeddingModel;
|
||||
import org.springframework.ai.transformers.TransformersEmbeddingModel;
|
||||
import org.springframework.ai.vectorstore.cosmosdb.CosmosDBVectorStore;
|
||||
import org.springframework.ai.vectorstore.CosmosDBVectorStoreConfig;
|
||||
import org.springframework.ai.vectorstore.VectorStore;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Lazy;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@SpringBootApplication
|
||||
public class DemoApplication implements CommandLineRunner {
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private VectorStore vectorStore;
|
||||
|
||||
@Lazy
|
||||
@Autowired
|
||||
private EmbeddingModel embeddingModel;
|
||||
|
||||
public static void main(String[] args) {
|
||||
SpringApplication.run(DemoApplication.class, args);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(String... args) throws Exception {
|
||||
Document document1 = new Document(UUID.randomUUID().toString(), "Sample content1", Map.of("key1", "value1"));
|
||||
Document document2 = new Document(UUID.randomUUID().toString(), "Sample content2", Map.of("key2", "value2"));
|
||||
this.vectorStore.add(List.of(document1, document2));
|
||||
|
||||
List<Document> results = this.vectorStore.similaritySearch(SearchRequest.builder().query("Sample content").topK(1).build());
|
||||
log.info("Search results: {}", results);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ObservationRegistry observationRegistry() {
|
||||
return ObservationRegistry.create();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public VectorStore vectorStore(ObservationRegistry observationRegistry) {
|
||||
|
||||
CosmosAsyncClient cosmosClient = new CosmosClientBuilder()
|
||||
.endpoint(System.getenv("COSMOSDB_AI_ENDPOINT"))
|
||||
.userAgentSuffix("SpringAI-CDBNoSQL-VectorStore")
|
||||
.key(System.getenv("COSMOSDB_AI_KEY"))
|
||||
.gatewayMode()
|
||||
.buildAsyncClient();
|
||||
|
||||
return CosmosDBVectorStore.builder(cosmosClient, this.embeddingModel)
|
||||
.databaseName("test-database")
|
||||
.containerName("test-container")
|
||||
.metadataFields(List.of("country", "year", "city"))
|
||||
.vectorStoreThroughput(1000)
|
||||
.observationRegistry(observationRegistry)
|
||||
.build();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new TransformersEmbeddingModel();
|
||||
}
|
||||
// Create and configure the vector store
|
||||
return CosmosDBVectorStore.builder(cosmosClient, embeddingModel)
|
||||
.databaseName("test-database")
|
||||
.containerName("test-container")
|
||||
// Configure metadata fields for filtering
|
||||
.metadataFields(List.of("country", "year", "city"))
|
||||
// Set the partition key path (optional)
|
||||
.partitionKeyPath("/id")
|
||||
// Configure performance settings
|
||||
.vectorStoreThroughput(1000)
|
||||
.vectorDimensions(1536) // Match your embedding model's dimensions
|
||||
// Add custom batching strategy (optional)
|
||||
.batchingStrategy(new TokenCountBatchingStrategy())
|
||||
// Add observation registry for metrics
|
||||
.observationRegistry(observationRegistry)
|
||||
.build();
|
||||
}
|
||||
```
|
||||
|
||||
@Bean
|
||||
public EmbeddingModel embeddingModel() {
|
||||
return new TransformersEmbeddingModel();
|
||||
}
|
||||
----
|
||||
|
||||
This configuration shows all the available builder options:
|
||||
|
||||
* `databaseName`: The name of your Cosmos DB database
|
||||
* `containerName`: The name of your container within the database
|
||||
* `partitionKeyPath`: The path for the partition key (e.g., "/id")
|
||||
* `metadataFields`: List of metadata fields that will be used for filtering
|
||||
* `vectorStoreThroughput`: The throughput (RU/s) for the vector store container
|
||||
* `vectorDimensions`: The number of dimensions for your vectors (should match your embedding model)
|
||||
* `batchingStrategy`: Strategy for batching document operations (optional)
|
||||
|
||||
== Manual Dependency Setup
|
||||
|
||||
|
||||
@@ -138,6 +138,9 @@ public VectorStore vectorStore(SearchIndexClient searchIndexClient, EmbeddingMod
|
||||
// in the similarity search filters.
|
||||
.filterMetadataFields(List.of(MetadataField.text("country"), MetadataField.int64("year"),
|
||||
MetadataField.date("activationDate")))
|
||||
.defaultTopK(5)
|
||||
.defaultSimilarityThreshold(0.7)
|
||||
.indexName("spring-ai-document-index")
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -232,9 +232,9 @@ Integrate with OpenAI's embeddings by adding the Spring Boot OpenAI starter to y
|
||||
@Bean
|
||||
public VectorStore chromaVectorStore(EmbeddingModel embeddingModel, ChromaApi chromaApi) {
|
||||
return ChromaVectorStore.builder(chromaApi, embeddingModel)
|
||||
.collectionName("TestCollection")
|
||||
.initializeSchema(true)
|
||||
.build();
|
||||
.collectionName("TestCollection")
|
||||
.initializeSchema(true)
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -272,4 +272,3 @@ docker run -it --rm --name chroma -p 8000:8000 ghcr.io/chroma-core/chroma:0.5.20
|
||||
```
|
||||
|
||||
Starts a chroma store at <http://localhost:8000/api/v1>
|
||||
|
||||
|
||||
@@ -184,7 +184,14 @@ To configure the `OracleVectorStore` in your application, you can use the follow
|
||||
----
|
||||
@Bean
|
||||
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel) {
|
||||
return new OracleVectorStore(jdbcTemplate, embeddingModel, true);
|
||||
return OracleVectorStore.builder(jdbcTemplate, embeddingModel)
|
||||
.tableName("my_vectors")
|
||||
.indexType(OracleVectorStoreIndexType.IVF)
|
||||
.distanceType(OracleVectorStoreDistanceType.COSINE)
|
||||
.dimensions(1536)
|
||||
.searchAccuracy(95)
|
||||
.initializeSchema(true)
|
||||
.build();
|
||||
}
|
||||
----
|
||||
|
||||
@@ -199,5 +206,3 @@ You can then connect to the database using:
|
||||
----
|
||||
sql mlops/mlops@localhost/freepdb1
|
||||
----
|
||||
|
||||
|
||||
|
||||
@@ -56,81 +56,25 @@ import org.springframework.lang.Nullable;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A ObservationVectorStore implementation that uses Typesense as the underlying storage.
|
||||
* A vector store implementation that uses Typesense as the backend. This implementation
|
||||
* supports storing and searching document embeddings using Typesense's vector search
|
||||
* capabilities.
|
||||
*
|
||||
* <p>
|
||||
* The store uses Typesense's vector search functionality to persist and query vector
|
||||
* embeddings along with their associated document content and metadata. The
|
||||
* implementation leverages Typesense's efficient similarity search capabilities for k-NN
|
||||
* operations.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Features:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Automatic schema initialization with configurable collection creation</li>
|
||||
* <li>Support for cosine similarity search</li>
|
||||
* <li>Metadata filtering using Typesense's filter expressions</li>
|
||||
* <li>Configurable similarity thresholds for search results</li>
|
||||
* <li>Batch processing support with configurable strategies</li>
|
||||
* <li>Observation and metrics support through Micrometer</li>
|
||||
* </ul>
|
||||
*
|
||||
* <p>
|
||||
* Basic usage example:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* TypesenseVectorStore vectorStore = TypesenseVectorStore.builder()
|
||||
* .client(client)
|
||||
* .embeddingModel(embeddingModel)
|
||||
* .initializeSchema(true)
|
||||
* .build();
|
||||
*
|
||||
* // Add documents
|
||||
* vectorStore.add(List.of(
|
||||
* new Document("content1", Map.of("key1", "value1")),
|
||||
* new Document("content2", Map.of("key2", "value2"))
|
||||
* ));
|
||||
*
|
||||
* // Search with filters
|
||||
* List<Document> results = vectorStore.similaritySearch(
|
||||
* SearchRequest.query("search text")
|
||||
* .withTopK(5)
|
||||
* .withSimilarityThreshold(0.7)
|
||||
* .withFilterExpression("metadata.key1 == 'value1'")
|
||||
* );
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Advanced configuration example:
|
||||
* </p>
|
||||
* <pre>{@code
|
||||
* TypesenseVectorStore vectorStore = TypesenseVectorStore.builder()
|
||||
* .client(client)
|
||||
* .embeddingModel(embeddingModel)
|
||||
* .collectionName("custom_vectors")
|
||||
* Example usage: <pre>{@code
|
||||
* TypesenseVectorStore vectorStore = TypesenseVectorStore.builder(client, embeddingModel)
|
||||
* .collectionName("my_collection")
|
||||
* .embeddingDimension(1536)
|
||||
* .initializeSchema(true)
|
||||
* .batchingStrategy(new TokenCountBatchingStrategy())
|
||||
* .observationRegistry(observationRegistry)
|
||||
* .build();
|
||||
* }</pre>
|
||||
*
|
||||
* <p>
|
||||
* Requirements:
|
||||
* </p>
|
||||
* <ul>
|
||||
* <li>Typesense server running and accessible</li>
|
||||
* <li>Collection schema with id (string), content (string), metadata (object), and
|
||||
* embedding (float array) fields</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Pablo Sanchidrian Herrera
|
||||
* @author Soby Chacko
|
||||
* @author Dhanush Anumula
|
||||
* @author Christian Tzolov
|
||||
* @author Thomas Vitale
|
||||
* @since 1.0.0
|
||||
* @author Eddú Meléndez
|
||||
* @author Mark Pollack
|
||||
* @see org.springframework.ai.vectorstore.VectorStore
|
||||
* @see org.springframework.ai.embedding.EmbeddingModel
|
||||
*/
|
||||
public class TypesenseVectorStore extends AbstractObservationVectorStore implements InitializingBean {
|
||||
|
||||
|
||||
Reference in New Issue
Block a user