Blog

Integrating Pinecone Vector Database into a Spring Boot Application

By  
Marcus Hellberg
Marcus Hellberg
·
On Jul 28, 2023 5:12:09 PM
·

Dive into the process of integrating the Pinecone Vector database with a Spring Boot application using Java in this second installment of the Building an AI chatbot in Java series.

Prerequisites

The article assumes you have a Spring Boot application. The example project is a Spring Boot application with a React front end created with Hilla. To build a new app, follow the instructions in the Hilla documentation.

To use reactive data types, add this dependency to your pom.xml file:

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

Acquiring a Pinecone API Key

To create a new API key, visit the Pinecone console. Store the key in an environment variable PINECONE_API_KEY. Also, save your Pinecone index URL as PINECONE_API_URL. Depending on your needs, environment variables can be stored in your IDE or system-wide.

Setting Up a Service Class

All Pinecone-related code will be in a Spring service class accessible from other parts of our application. Create a new class, PineconService.java:

@Service
public class PineconeService {

@Value("${pinecone.api.key}")
private String PINECONE_API_KEY;

@Value("${pinecone.api.url}")
private String PINECONE_API_URL;
}

Configuring Spring WebClient for Pinecone API

Spring WebClient will be used for making calls to Pinecone REST services. Initialize the client by adding the following to PineconeService.java:

private WebClient webClient;

@PostConstruct
void init() {
this.webClient = WebClient.builder()
.baseUrl(PINECONE_API_URL)
.defaultHeader("Api-Key", PINECONE_API_KEY)
.defaultHeader("Accept", MediaType.APPLICATION_JSON_VALUE)
.defaultHeader("Content-Type", MediaType.APPLICATION_JSON_VALUE)
.build();
}

Querying Pinecone

Now that the client is configured, you can call the Pinecone API to query records. Add the following method to PineconeService.java:

public Mono<List<String>> findSimilarDocuments(List<Double> embedding, int maxResults, String namespace) {

var body = Map.of(
"vector", embedding,
"topK", maxResults,
"includeMetadata", true,
"namespace", namespace
);

return this.webClient.post()
.uri("/query")
.bodyValue(body)
.retrieve()
.bodyToMono(QueryResponse.class)
.map(QueryResponse::getMatches)
.flatMapIterable(matches -> matches)
.map(match -> {
if (match.getMetadata() != null && match.getMetadata().containsKey("text")) {
return match.getMetadata().get("text");
} else {
return "";
}
})
.filter(doc -> !doc.isBlank())
.collectList();
}

The method takes an embedding vector (obtained by calling the OpenAI embeddings API in the previous article), the number of similar documents you want, and the namespace for the search. The Java classes for the response can be found in the project's GitHub repository.

The method returns the source texts from the metadata of the matching vectors.

Completed Application Source Code

The completed application's source code is available on GitHub.

What's Next?

As part of the Building an AI chatbot in Java series, you now have services for calling both OpenAI and Pinecone. The following article will build a service that uses the Pinecone service to fetch articles to include as context for the ChatGPT prompt.

Marcus Hellberg
Marcus Hellberg
Marcus is the VP of Developer Relations at Vaadin. His daily work includes everything from writing blogs and tech demos to attending events and giving presentations on all things Vaadin and web-related. You can reach out to him on Twitter @marcushellberg.
Other posts by Marcus Hellberg