diff --git a/spring-ai-2/docker-compose.yml b/spring-ai-2/docker-compose.yml new file mode 100644 index 000000000000..2b4b28761035 --- /dev/null +++ b/spring-ai-2/docker-compose.yml @@ -0,0 +1,29 @@ +services: + postgres: + image: pgvector/pgvector:pg17 + environment: + POSTGRES_DB: vectordb + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + ports: + - "5434:5432" + healthcheck: + test: [ "CMD-SHELL", "pg_isready -U postgres" ] + interval: 10s + timeout: 5s + retries: 5 + + ollama: + image: ollama/ollama:latest + ports: + - "11435:11434" + volumes: + - ollama_data:/root/.ollama + healthcheck: + test: [ "CMD", "curl", "-f", "http://localhost:11435/api/health" ] + interval: 10s + timeout: 5s + retries: 10 + +volumes: + ollama_data: \ No newline at end of file diff --git a/spring-ai-2/pom.xml b/spring-ai-2/pom.xml index a690d50d6c3b..ecdc410ed53c 100644 --- a/spring-ai-2/pom.xml +++ b/spring-ai-2/pom.xml @@ -72,6 +72,10 @@ hsqldb runtime + + org.springframework.ai + spring-ai-pgvector-store-spring-boot-starter + @@ -94,6 +98,11 @@ ollama test + + org.springframework.boot + spring-boot-docker-compose + ${spring-boot-docker-compose.version} + @@ -119,6 +128,7 @@ 3.4.1 1.0.0-M5 5.9.0 + 3.1.1 \ No newline at end of file diff --git a/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Application.java b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Application.java new file mode 100644 index 000000000000..4cb8a5021e0d --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Application.java @@ -0,0 +1,26 @@ +package com.baeldung.springai.semanticsearch; + +import org.springframework.ai.autoconfigure.openai.OpenAiAutoConfiguration; +import org.springframework.ai.autoconfigure.vectorstore.chroma.ChromaVectorStoreAutoConfiguration; +import org.springframework.ai.chat.model.ChatModel; +import org.springframework.ai.ollama.OllamaChatModel; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Primary; +import org.springframework.context.annotation.PropertySource; + +@SpringBootApplication(exclude = { OpenAiAutoConfiguration.class, ChromaVectorStoreAutoConfiguration.class }) +@PropertySource("classpath:application-semantic-search.properties") +public class Application { + + public static void main(String[] args) { + SpringApplication.run(Application.class, args); + } + + @Bean + @Primary + public ChatModel chatModel(OllamaChatModel ollamaChatModel) { + return ollamaChatModel; + } +} diff --git a/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Book.java b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Book.java new file mode 100644 index 000000000000..abd7a2a69091 --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/Book.java @@ -0,0 +1,5 @@ +package com.baeldung.springai.semanticsearch; + +public record Book(String title, String author, String description) { + +} diff --git a/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BookSearchController.java b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BookSearchController.java new file mode 100644 index 000000000000..922c6058011a --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BookSearchController.java @@ -0,0 +1,54 @@ +package com.baeldung.springai.semanticsearch; + +import org.springframework.ai.chat.client.ChatClient; +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.SearchRequest; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +@RestController +@RequestMapping("/books") +public class BookSearchController { + + private final VectorStore vectorStore; + private final ChatClient chatClient; + + public BookSearchController(VectorStore vectorStore, ChatClient.Builder chatClientBuilder) { + this.vectorStore = vectorStore; + this.chatClient = chatClientBuilder.build(); + } + + @PostMapping("/search") + List semanticSearch(@RequestBody String query) { + return vectorStore.similaritySearch(SearchRequest.builder() + .query(query) + .topK(3) + .build()) + .stream() + .map(Document::getText) + .toList(); + } + + @PostMapping("/enhanced-search") + String enhancedSearch(@RequestBody String query) { + String context = vectorStore.similaritySearch(SearchRequest.builder() + .query(query) + .topK(3) + .build()) + .stream() + .map(Document::getText) + .reduce("", (a, b) -> a + b + "\n"); + + return chatClient.prompt() + .system(context) + .user(query) + .call() + .content(); + } + +} diff --git a/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BooksIngestionPipeline.java b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BooksIngestionPipeline.java new file mode 100644 index 000000000000..471d9387d92e --- /dev/null +++ b/spring-ai-2/src/main/java/com/baeldung/springai/semanticsearch/BooksIngestionPipeline.java @@ -0,0 +1,39 @@ +package com.baeldung.springai.semanticsearch; + +import jakarta.annotation.PostConstruct; + +import org.springframework.ai.document.Document; +import org.springframework.ai.vectorstore.VectorStore; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class BooksIngestionPipeline { + + private final VectorStore vectorStore; + + public BooksIngestionPipeline(VectorStore vectorStore) { + this.vectorStore = vectorStore; + } + + @PostConstruct + void run() { + var books = List.of(new Book("The Great Gatsby", "F. Scott Fitzgerald", + "The Great Gatsby is a 1925 novel by American writer F. Scott Fitzgerald. Set in the Jazz Age on Long Island, near New York City, the novel depicts first-person narrator Nick Carraway's interactions with mysterious millionaire Jay Gatsby and Gatsby's obsession to reunite with his former lover, Daisy Buchanan."), + new Book("To Kill a Mockingbird", "Harper Lee", + "To Kill a Mockingbird is a novel by the American author Harper Lee. It was published in 1960 and was instantly successful. In the United States, it is widely read in high schools and middle schools."), + new Book("1984", "George Orwell", + "Nineteen Eighty-Four: A Novel, often referred to as 1984, is a dystopian social science fiction novel by the English novelist George Orwell. It was published on 8 June 1949 by Secker & Warburg as Orwell's ninth and final book completed in his lifetime."), + new Book("The Catcher in the Rye", "J. D. Salinger", + "The Catcher in the Rye is a novel by J. D. Salinger, partially published in serial form in 1945–1946 and as a novel in 1951. It was originally intended for adults but is often read by adolescents for its themes of angst, alienation, and as a critique on superficiality in society."), + new Book("Lord of the Flies", "William Golding", + "Lord of the Flies is a 1954 novel by Nobel Prize-winning British author William Golding. The book focuses on a group of British")); + + List documents = books.stream() + .map(book -> new Document(book.toString())) + .toList(); + + vectorStore.add(documents); + } +} diff --git a/spring-ai-2/src/main/resources/application-semantic-search.properties b/spring-ai-2/src/main/resources/application-semantic-search.properties new file mode 100644 index 000000000000..2e02182f9dcf --- /dev/null +++ b/spring-ai-2/src/main/resources/application-semantic-search.properties @@ -0,0 +1,13 @@ +spring.ai.ollama.init.pull-model-strategy=when_missing +spring.ai.ollama.init.chat.include=true +spring.ai.ollama.embedding.options.model=nomic-embed-text +spring.ai.vectorstore.pgvector.initialize-schema=true +spring.ai.vectorstore.pgvector.dimensions=768 +spring.ai.vectorstore.pgvector.index-type=hnsw +spring.docker.compose.file=docker-compose.yml +spring.docker.compose.enabled=true +spring.datasource.url=jdbc:postgresql://localhost:5434/vectordb +spring.datasource.username=postgres +spring.datasource.password=postgres +spring.datasource.driver-class-name=org.postgresql.Driver +spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect \ No newline at end of file