Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.retriever.VectorStoreRetriever;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.ai.vectorstore.InMemoryVectorStore;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -67,7 +67,7 @@ void acmeChain() {
// Step 2 - Create embeddings and save to vector store

logger.info("Creating Embeddings...");
VectorStore vectorStore = new InMemoryVectorStore(embeddingClient);
VectorStore vectorStore = new SimpleVectorStore(embeddingClient);

vectorStore.accept(textSplitter.apply(jsonReader.get()));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.vectorstore.SimplePersistentVectorStore;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.reader.JsonMetadataGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
Expand Down Expand Up @@ -34,14 +34,14 @@ void persist(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path workingDir) {
JsonReader jsonReader = new JsonReader(bikesJsonResource, new ProductMetadataGenerator(), "price", "name",
"shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
SimplePersistentVectorStore vectorStore = new SimplePersistentVectorStore(this.embeddingClient);
SimpleVectorStore vectorStore = new SimpleVectorStore(this.embeddingClient);
vectorStore.add(documents);

File tempFile = new File(workingDir.toFile(), "temp.txt");
vectorStore.save(tempFile);
assertThat(tempFile).isNotEmpty();
assertThat(tempFile).content().contains("Velo 99 XR1 AXS");
SimplePersistentVectorStore vectorStore2 = new SimplePersistentVectorStore(this.embeddingClient);
SimpleVectorStore vectorStore2 = new SimpleVectorStore(this.embeddingClient);

vectorStore2.load(tempFile);
List<Document> similaritySearch = vectorStore2.similaritySearch("Velo 99 XR1 AXS");
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,21 @@
package org.springframework.ai.vectorstore;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;

import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

Expand All @@ -14,15 +25,15 @@
* @author Mark Pollack
* @author Christian Tzolov
*/
public class InMemoryVectorStore implements VectorStore {
public class SimpleVectorStore implements VectorStore {

private static final Logger logger = LoggerFactory.getLogger(InMemoryVectorStore.class);
private static final Logger logger = LoggerFactory.getLogger(SimpleVectorStore.class);

protected Map<String, Document> store = new ConcurrentHashMap<>();

protected EmbeddingClient embeddingClient;

public InMemoryVectorStore(EmbeddingClient embeddingClient) {
public SimpleVectorStore(EmbeddingClient embeddingClient) {
Objects.requireNonNull(embeddingClient, "EmbeddingClient must not be null");
this.embeddingClient = embeddingClient;
}
Expand Down Expand Up @@ -66,6 +77,81 @@ public List<Document> similaritySearch(SearchRequest request) {
return similarities;
}

/**
* Serialize the vector store content into a file in JSON format.
* @param file the file to save the vector store content
*/
public void save(File file) {
String json = getVectorDbAsJson();
try {
if (!file.exists()) {
logger.info("Creating new vector store file: " + file);
file.createNewFile();
}
else {
logger.info("Replacing existing vector store file: " + file);
file.delete();
file.createNewFile();
}
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
try (OutputStream stream = new FileOutputStream(file)) {
StreamUtils.copy(json, Charset.forName("UTF-8"), stream);
}
catch (IOException e) {
throw new RuntimeException(e);
}
}

/**
* Deserialize the vector store content from a file in JSON format into memory.
* @param file the file to load the vector store content
*/
public void load(File file) {
TypeReference<HashMap<String, Document>> typeRef = new TypeReference<>() {
};
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, Document> deserializedMap = objectMapper.readValue(file, typeRef);
this.store = deserializedMap;
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}

/**
* Deserialize the vector store content from a resource in JSON format into memory.
* @param resource the resource to load the vector store content
*/
public void load(Resource resource) {
TypeReference<HashMap<String, Document>> typeRef = new TypeReference<>() {
};
ObjectMapper objectMapper = new ObjectMapper();
try {
Map<String, Document> deserializedMap = objectMapper.readValue(resource.getInputStream(), typeRef);
this.store = deserializedMap;
}
catch (IOException ex) {
throw new RuntimeException(ex);
}
}

private String getVectorDbAsJson() {
ObjectMapper objectMapper = new ObjectMapper();
ObjectWriter objectWriter = objectMapper.writerWithDefaultPrettyPrinter();
String json;
try {
json = objectWriter.writeValueAsString(this.store);
}
catch (JsonProcessingException e) {
throw new RuntimeException("Error serializing documentMap to JSON.", e);
}
return json;
}

private List<Double> getUserQueryEmbedding(String query) {
List<Double> userQueryEmbedding = this.embeddingClient.embed(query);
return userQueryEmbedding;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public interface DocumentWriter extends Consumer<List<Document>> {
*PgVectorStore*::
+ Provides vector storage capabilities using PostgreSQL.

*SimplePersistentVectorStore*::
*SimpleVectorStore*::
+ A straightforward approach to persistent vector storage.

*InMemoryVectorStore*::
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ country == 'UK' && year >= 2020 && isActive == true.

These are the available implementations of the `VectorStore` interface:

* `InMemoryVectorStore` and `SimplePersistentVectorStore`.
* `SimpleVectorStore`
* Pinecone: https://www.pinecone.io/[PineCone] vector store.
* PgVector [`PgVectorStore`]: The https://github.com/pgvector/pgvector[PostgreSQL/PGVector] vector store.
* Milvus [`MilvusVectorStore`]: The https://milvus.io/[Milvus] vector store
Expand Down