Refactoring to functional interfaces

Fixes #47

reformatting
This commit is contained in:
Christian Tzolov
2023-10-03 21:10:56 +02:00
committed by Mark Pollack
parent 46e372d95c
commit 3eff6a75c9
25 changed files with 153 additions and 184 deletions

View File

@@ -0,0 +1,8 @@
package org.springframework.ai.document;
import java.util.List;
import java.util.function.Supplier;
public interface DocumentReader extends Supplier<List<Document>> {
}

View File

@@ -0,0 +1,19 @@
package org.springframework.ai.document;
import java.util.List;
import java.util.function.Function;
public interface DocumentRetriever extends Function<String, List<Document>> {
/**
* Retrieves relevant documents however the implementation sees fit.
* @param query query string
* @return relevant documents
*/
List<Document> retrieve(String query);
default List<Document> apply(String query) {
return retrieve(query);
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.document;
import java.util.List;
import java.util.function.Consumer;
/**
* @author Christian Tzolov
*/
public interface DocumentWriter extends Consumer<List<Document>> {
}

View File

@@ -1,14 +0,0 @@
package org.springframework.ai.loader;
import org.springframework.ai.document.Document;
import org.springframework.ai.splitter.TextSplitter;
import java.util.List;
public interface Loader {
List<Document> load();
List<Document> load(TextSplitter textSplitter);
}

View File

@@ -1,49 +0,0 @@
/*
* Copyright 2023-2023 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.ai.loader.extractor;
import java.util.List;
import java.util.Map;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentTransformer;
/**
* @author Christian Tzolov
*/
public abstract class AbstractMetadataFeatureExtractor implements DocumentTransformer {
@Override
public List<Document> apply(List<Document> documents) {
List<Map<String, Object>> metadataList = this.extract(documents);
for (int idx = 0; idx < documents.size(); idx++) {
documents.get(idx).getMetadata().putAll(metadataList.get(idx));
}
return documents;
}
/**
* Extracts metadata for a list of documents, returning a list of metadata
* dictionaries corresponding to each document.
* @param documents Documents to extract metadata from.
* @return List of metadata dictionaries corresponding to each document
*/
abstract public List<Map<String, Object>> extract(List<Document> documents);
}

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.loader.impl;
package org.springframework.ai.reader;
import java.util.Collections;
import java.util.Map;

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.loader.impl;
package org.springframework.ai.reader;
import java.util.Map;

View File

@@ -1,17 +1,19 @@
package org.springframework.ai.loader.impl;
package org.springframework.ai.reader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.Loader;
import org.springframework.ai.splitter.TextSplitter;
import org.springframework.ai.splitter.TokenTextSplitter;
import org.springframework.ai.document.DocumentReader;
import org.springframework.core.io.Resource;
import java.io.IOException;
import java.util.*;
public class JsonLoader implements Loader {
public class JsonReader implements DocumentReader {
private Resource resource;
@@ -22,15 +24,15 @@ public class JsonLoader implements Loader {
*/
private List<String> jsonKeysToUse;
public JsonLoader(Resource resource) {
public JsonReader(Resource resource) {
this(resource, new ArrayList<>().toArray(new String[0]));
}
public JsonLoader(Resource resource, String... jsonKeysToUse) {
public JsonReader(Resource resource, String... jsonKeysToUse) {
this(resource, new EmptyJsonMetadataGenerator(), jsonKeysToUse);
}
public JsonLoader(Resource resource, JsonMetadataGenerator jsonMetadataGenerator, String... jsonKeysToUse) {
public JsonReader(Resource resource, JsonMetadataGenerator jsonMetadataGenerator, String... jsonKeysToUse) {
Objects.requireNonNull(jsonKeysToUse, "keys must not be null");
Objects.requireNonNull(jsonMetadataGenerator, "jsonMetadataGenerator must not be null");
Objects.requireNonNull(resource, "The Spring Resource must not be null");
@@ -40,13 +42,7 @@ public class JsonLoader implements Loader {
}
@Override
public List<Document> load() {
return load(new TokenTextSplitter());
}
@Override
public List<Document> load(TextSplitter textSplitter) {
public List<Document> get() {
ObjectMapper objectMapper = new ObjectMapper();
List<Document> documents = new ArrayList<>();
try {
@@ -74,15 +70,8 @@ public class JsonLoader implements Loader {
else {
document = new Document(item.toString(), metadata);
}
// Splitting at the item level is good when the size of the json per
// element is large
// as is the case with a catalog of product, as the metadata applies
// across all split documents
// This may not be good when the size of the json element is small as it
// can create too many individual
// documents.
List<Document> splitDocuments = textSplitter.apply(List.of(document));
documents.addAll(splitDocuments);
documents.add(document);
}
}
catch (IOException e) {

View File

@@ -1,18 +1,15 @@
package org.springframework.ai.loader.impl;
package org.springframework.ai.reader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.Loader;
import org.springframework.ai.splitter.TextSplitter;
import org.springframework.ai.splitter.TokenTextSplitter;
import org.springframework.ai.document.DocumentReader;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.util.StreamUtils;
@@ -21,7 +18,7 @@ import org.springframework.util.StreamUtils;
* @author Craig Walls
* @author Christian Tzolov
*/
public class TextLoader implements Loader {
public class TextReader implements DocumentReader {
public static final String CHARSET_METADATA = "charset";
@@ -39,11 +36,11 @@ public class TextLoader implements Loader {
private Map<String, Object> customMetadata = new HashMap<>();
public TextLoader(String resourceUrl) {
public TextReader(String resourceUrl) {
this(new DefaultResourceLoader().getResource(resourceUrl));
}
public TextLoader(Resource resource) {
public TextReader(Resource resource) {
Objects.requireNonNull(resource, "The Spring Resource must not be null");
this.resource = resource;
}
@@ -66,12 +63,7 @@ public class TextLoader implements Loader {
}
@Override
public List<Document> load() {
return load(new TokenTextSplitter());
}
@Override
public List<Document> load(TextSplitter textSplitter) {
public List<Document> get() {
try {
String document = StreamUtils.copyToString(this.resource.getInputStream(), this.charset);
@@ -80,7 +72,9 @@ public class TextLoader implements Loader {
this.customMetadata.put(CHARSET_METADATA, this.charset.name());
this.customMetadata.put(SOURCE_METADATA, this.resource.getFilename());
return textSplitter.apply(Collections.singletonList(new Document(document, this.customMetadata)));
return List.of(new Document(document, this.customMetadata));
// return textSplitter.apply(Collections.singletonList(new Document(document,
// this.customMetadata)));
}
catch (IOException e) {
throw new RuntimeException(e);

View File

@@ -1,16 +0,0 @@
package org.springframework.ai.retriever;
import org.springframework.ai.document.Document;
import java.util.List;
public interface Retriever {
/**
* Retrieves relevant documents however the implementation sees fit.
* @param query query string
* @return relevant documents
*/
List<Document> retrieve(String query);
}

View File

@@ -1,14 +1,14 @@
package org.springframework.ai.retriever.impl;
package org.springframework.ai.retriever;
import org.springframework.ai.document.Document;
import org.springframework.ai.retriever.Retriever;
import org.springframework.ai.document.DocumentRetriever;
import org.springframework.ai.vectorstore.VectorStore;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
public class VectorStoreRetriever implements Retriever {
public class VectorStoreRetriever implements DocumentRetriever {
private VectorStore vectorStore;

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.loader.extractor;
package org.springframework.ai.transformer;
import java.util.ArrayList;
import java.util.List;
@@ -27,7 +27,7 @@ import org.springframework.ai.document.DocumentTransformer;
/**
* @author Christian Tzolov
*/
public class ContentFormatEnricher implements DocumentTransformer {
public class ContentFormatTransformer implements DocumentTransformer {
/**
* Disable the content-formatter template rewrite.
@@ -36,11 +36,11 @@ public class ContentFormatEnricher implements DocumentTransformer {
private ContentFormatter contentFormatter;
public ContentFormatEnricher(ContentFormatter contentFormatter) {
public ContentFormatTransformer(ContentFormatter contentFormatter) {
this(contentFormatter, false);
}
public ContentFormatEnricher(ContentFormatter contentFormatter, boolean disableTemplateRewrite) {
public ContentFormatTransformer(ContentFormatter contentFormatter, boolean disableTemplateRewrite) {
this.contentFormatter = contentFormatter;
this.disableTemplateRewrite = disableTemplateRewrite;
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.loader.extractor;
package org.springframework.ai.transformer;
import java.util.List;
import java.util.Map;
@@ -31,7 +31,7 @@ import org.springframework.util.Assert;
*
* @author Christian Tzolov
*/
public class KeywordExtractor implements DocumentTransformer {
public class KeywordMetadataEnricher implements DocumentTransformer {
private static final String EXCERPT_KEYWORDS_METADATA_KEY = "excerpt_keywords";
@@ -51,7 +51,7 @@ public class KeywordExtractor implements DocumentTransformer {
*/
private final int keywordCount;
public KeywordExtractor(AiClient aiClient, int keywordCount) {
public KeywordMetadataEnricher(AiClient aiClient, int keywordCount) {
Assert.notNull(aiClient, "AiClient must not be null");
Assert.isTrue(keywordCount >= 1, "Document count must be >= 1");

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.loader.extractor;
package org.springframework.ai.transformer;
import java.util.ArrayList;
import java.util.HashMap;
@@ -36,7 +36,7 @@ import org.springframework.util.CollectionUtils;
*
* @author Christian Tzolov
*/
public class SummaryExtractor implements DocumentTransformer {
public class SummaryMetadataEnricher implements DocumentTransformer {
private static final String SECTION_SUMMARY_METADATA_KEY = "section_summary";
@@ -77,11 +77,11 @@ public class SummaryExtractor implements DocumentTransformer {
*/
private final String summaryTemplate;
public SummaryExtractor(AiClient aiClient, List<SummaryType> summaryTypes) {
public SummaryMetadataEnricher(AiClient aiClient, List<SummaryType> summaryTypes) {
this(aiClient, summaryTypes, DEFAULT_SUMMARY_EXTRACT_TEMPLATE, MetadataMode.ALL);
}
public SummaryExtractor(AiClient aiClient, List<SummaryType> summaryTypes, String summaryTemplate,
public SummaryMetadataEnricher(AiClient aiClient, List<SummaryType> summaryTypes, String summaryTemplate,
MetadataMode metadataMode) {
Assert.notNull(aiClient, "AiClient must not be null");
Assert.hasText(summaryTemplate, "Summary template must not be empty");

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.splitter;
package org.springframework.ai.transformer.splitter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.splitter;
package org.springframework.ai.transformer.splitter;
import com.knuddels.jtokkit.Encodings;
import com.knuddels.jtokkit.api.Encoding;

View File

@@ -1,10 +1,9 @@
package org.springframework.ai.vectorstore.impl;
package org.springframework.ai.vectorstore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.vectorstore.VectorStore;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

View File

@@ -1,4 +1,4 @@
package org.springframework.ai.vectorstore.impl;
package org.springframework.ai.vectorstore;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;

View File

@@ -4,8 +4,9 @@ import java.util.List;
import java.util.Optional;
import org.springframework.ai.document.Document;
import org.springframework.ai.document.DocumentWriter;
public interface VectorStore {
public interface VectorStore extends DocumentWriter {
/**
* Adds Documents to the vector store.
@@ -14,6 +15,10 @@ public interface VectorStore {
*/
void add(List<Document> documents);
default void accept(List<Document> documents) {
add(documents);
}
Optional<Boolean> delete(List<String> idList);
List<Document> similaritySearch(String query);

View File

@@ -14,11 +14,11 @@
* limitations under the License.
*/
package org.springframework.ai.loader;
package org.springframework.ai.reader;
import org.junit.jupiter.api.Test;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.impl.JsonLoader;
import org.springframework.ai.reader.JsonReader;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.core.io.Resource;
@@ -36,8 +36,8 @@ public class JsonLoaderTests {
@Test
void loadJson() {
assertThat(resource).isNotNull();
JsonLoader jsonLoader = new JsonLoader(resource, "description");
List<Document> documents = jsonLoader.load();
JsonReader jsonLoader = new JsonReader(resource, "description");
List<Document> documents = jsonLoader.get();
assertThat(documents).isNotEmpty();
for (Document document : documents) {
assertThat(document.getContent()).isNotEmpty();

View File

@@ -14,14 +14,15 @@
* limitations under the License.
*/
package org.springframework.ai.loader;
package org.springframework.ai.reader;
import java.util.List;
import org.junit.jupiter.api.Test;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.impl.TextLoader;
import org.springframework.ai.reader.TextReader;
import org.springframework.ai.transformer.splitter.TokenTextSplitter;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
@@ -37,17 +38,19 @@ public class TextLoaderTests {
@Test
void loadText() {
assertThat(resource).isNotNull();
TextLoader textLoader = new TextLoader(resource);
TextReader textLoader = new TextReader(resource);
textLoader.getCustomMetadata().put("customKey", "Value");
List<Document> documents = textLoader.load();
List<Document> documents0 = textLoader.get();
List<Document> documents = new TokenTextSplitter().apply(documents0);
assertThat(documents.size()).isEqualTo(54);
for (Document document : documents) {
assertThat(document.getMetadata().get("customKey")).isEqualTo("Value");
assertThat(document.getMetadata().get(TextLoader.SOURCE_METADATA)).isEqualTo("text_source.txt");
assertThat(document.getMetadata().get(TextLoader.CHARSET_METADATA)).isEqualTo("UTF-8");
assertThat(document.getMetadata().get(TextReader.SOURCE_METADATA)).isEqualTo("text_source.txt");
assertThat(document.getMetadata().get(TextReader.CHARSET_METADATA)).isEqualTo("UTF-8");
assertThat(document.getContent()).isNotEmpty();
}
}

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.splitter;
package org.springframework.ai.transformer.splitter;
import java.util.ArrayList;
import java.util.List;
@@ -25,6 +25,7 @@ import org.junit.jupiter.api.Test;
import org.springframework.ai.document.DefaultContentFormatter;
import org.springframework.ai.document.Document;
import org.springframework.ai.transformer.splitter.TextSplitter;
/**
* @author Christian Tzolov

View File

@@ -6,16 +6,17 @@ import org.slf4j.LoggerFactory;
import org.springframework.ai.client.AiClient;
import org.springframework.ai.client.AiResponse;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.impl.JsonLoader;
import org.springframework.ai.openai.embedding.OpenAiEmbeddingClient;
import org.springframework.ai.openai.testutils.AbstractIT;
import org.springframework.ai.prompt.Prompt;
import org.springframework.ai.prompt.SystemPromptTemplate;
import org.springframework.ai.prompt.messages.Message;
import org.springframework.ai.prompt.messages.UserMessage;
import org.springframework.ai.retriever.impl.VectorStoreRetriever;
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.VectorStore;
import org.springframework.ai.vectorstore.impl.InMemoryVectorStore;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -51,19 +52,20 @@ public class AcmeIT extends AbstractIT {
assertThat(aiClient).isNotNull();
}
// @Test
@Test
void acmeChain() {
// Step 1 - load documents
JsonLoader jsonLoader = new JsonLoader(bikesResource, "name", "price", "shortDescription", "description");
List<Document> documents = jsonLoader.load();
JsonReader jsonLoader = new JsonReader(bikesResource, "name", "price", "shortDescription", "description");
var textSplitter = new TokenTextSplitter();
// Step 2 - Create embeddings and save to vector store
logger.info("Creating Embeddings...");
VectorStore vectorStore = new InMemoryVectorStore(embeddingClient);
vectorStore.add(documents);
vectorStore.accept(textSplitter.apply(jsonLoader.get()));
// Now user query

View File

@@ -14,7 +14,7 @@
* limitations under the License.
*/
package org.springframework.ai.openai.extractor;
package org.springframework.ai.openai.transformer;
import java.io.IOException;
import java.time.Duration;
@@ -27,11 +27,11 @@ import org.junit.jupiter.api.Test;
import org.springframework.ai.document.DefaultContentFormatter;
import org.springframework.ai.document.Document;
import org.springframework.ai.loader.extractor.ContentFormatEnricher;
import org.springframework.ai.loader.extractor.KeywordExtractor;
import org.springframework.ai.loader.extractor.SummaryExtractor;
import org.springframework.ai.loader.extractor.SummaryExtractor.SummaryType;
import org.springframework.ai.openai.client.OpenAiClient;
import org.springframework.ai.transformer.ContentFormatTransformer;
import org.springframework.ai.transformer.KeywordMetadataEnricher;
import org.springframework.ai.transformer.SummaryMetadataEnricher;
import org.springframework.ai.transformer.SummaryMetadataEnricher.SummaryType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.test.context.SpringBootTest;
@@ -44,16 +44,16 @@ import static org.assertj.core.api.Assertions.assertThat;
* @author Christian Tzolov
*/
@SpringBootTest
public class MetadataExtractorIT {
public class MetadataTransformerIT {
@Autowired
KeywordExtractor keywordExtractor;
KeywordMetadataEnricher keywordMetadataEnricher;
@Autowired
SummaryExtractor summaryExtractor;
SummaryMetadataEnricher summaryMetadataEnricher;
@Autowired
ContentFormatEnricher metadataExtractor;
ContentFormatTransformer contentFormatTransformer;
@Autowired
DefaultContentFormatter defaultContentFormatter;
@@ -75,7 +75,7 @@ public class MetadataExtractorIT {
@Test
public void testKeywordExtractor() {
var updatedDocuments = keywordExtractor.apply(List.of(document1, document2));
var updatedDocuments = keywordMetadataEnricher.apply(List.of(document1, document2));
List<Map<String, Object>> keywords = updatedDocuments.stream().map(d -> d.getMetadata()).toList();
@@ -92,7 +92,7 @@ public class MetadataExtractorIT {
@Test
public void testSummaryExtractor() {
var updatedDocuments = summaryExtractor.apply(List.of(document1, document2));
var updatedDocuments = summaryMetadataEnricher.apply(List.of(document1, document2));
List<Map<String, Object>> summaries = updatedDocuments.stream().map(d -> d.getMetadata()).toList();
@@ -126,7 +126,7 @@ public class MetadataExtractorIT {
assertThat(((DefaultContentFormatter) document2.getContentFormatter()).getExcludedInferenceMetadataKeys())
.doesNotContain("NewInferenceKey");
List<Document> enrichedDocuments = metadataExtractor.apply(List.of(document1, document2));
List<Document> enrichedDocuments = contentFormatTransformer.apply(List.of(document1, document2));
assertThat(enrichedDocuments.size()).isEqualTo(2);
var doc1 = enrichedDocuments.get(0);
@@ -173,13 +173,14 @@ public class MetadataExtractorIT {
}
@Bean
public KeywordExtractor keywordExtractor(OpenAiClient aiClient) {
return new KeywordExtractor(aiClient, 5);
public KeywordMetadataEnricher keywordMetadata(OpenAiClient aiClient) {
return new KeywordMetadataEnricher(aiClient, 5);
}
@Bean
public SummaryExtractor summaryExtractor(OpenAiClient aiClient) {
return new SummaryExtractor(aiClient, List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));
public SummaryMetadataEnricher summaryMetadata(OpenAiClient aiClient) {
return new SummaryMetadataEnricher(aiClient,
List.of(SummaryType.PREVIOUS, SummaryType.CURRENT, SummaryType.NEXT));
}
@Bean
@@ -191,8 +192,8 @@ public class MetadataExtractorIT {
}
@Bean
public ContentFormatEnricher metadataExtractor(DefaultContentFormatter defaultContentFormatter) {
return new ContentFormatEnricher(defaultContentFormatter, false);
public ContentFormatTransformer contentFormatTransformer(DefaultContentFormatter defaultContentFormatter) {
return new ContentFormatTransformer(defaultContentFormatter, false);
}
}

View File

@@ -5,9 +5,9 @@ import org.junit.jupiter.api.io.CleanupMode;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.loader.impl.JsonLoader;
import org.springframework.ai.loader.impl.JsonMetadataGenerator;
import org.springframework.ai.vectorstore.impl.SimplePersistentVectorStore;
import org.springframework.ai.reader.JsonReader;
import org.springframework.ai.vectorstore.SimplePersistentVectorStore;
import org.springframework.ai.reader.JsonMetadataGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.test.context.SpringBootTest;
@@ -31,9 +31,9 @@ public class SimplePersistentVectorStoreIT {
@Test
void persist(@TempDir(cleanup = CleanupMode.ON_SUCCESS) Path workingDir) {
JsonLoader jsonLoader = new JsonLoader(bikesJsonResource, new ProductMetadataGenerator(), "price", "name",
JsonReader jsonLoader = new JsonReader(bikesJsonResource, new ProductMetadataGenerator(), "price", "name",
"shortDescription", "description", "tags");
List<Document> documents = jsonLoader.load();
List<Document> documents = jsonLoader.get();
SimplePersistentVectorStore vectorStore = new SimplePersistentVectorStore(this.embeddingClient);
vectorStore.add(documents);