Expose data using Spring Boot and Spring AI framework
This article is a part of Vector Search using Spring AI Elastic Search and Apache Nifi 2
In the previous article we have seen how to ingest data and apply embeddings and store it using Apache Nifi 2.
In this article we will see how we will expose this data via rest api as well as display on using spring thymeleaf starter.
Let's first create model for elastic search document as given below:
1package in.silentsudo.springbootvectorsearch;
2
3import com.fasterxml.jackson.annotation.JsonIgnore;
4import lombok.Data;
5import lombok.Getter;
6import lombok.Setter;
7import org.springframework.data.annotation.Id;
8import org.springframework.data.elasticsearch.annotations.Document;
9import org.springframework.data.elasticsearch.annotations.Field;
10import org.springframework.data.elasticsearch.annotations.FieldType;
11
12import java.io.Serializable;
13
14@Document(indexName = "amazon-products")
15@Data
16@Getter
17@Setter
18public class AmazonProduct implements Serializable {
19
20 @Id
21 private String id;
22
23 @Field(name = "final_price")
24 private String finalPrice;
25
26 private String currency;
27
28 @Field(name = "reviews_count")
29 private String reviewCount;
30
31 private String title;
32
33 private String brand;
34
35 private String url;
36
37 private float rating;
38
39 @Field(name = "image_url")
40 private String imageUrl;
41
42 @Field(type = FieldType.Dense_Vector, dims = 3072, index = true)
43 @JsonIgnore
44 private float[] embedding;
45
46
47}
In pom.xml(for maven) project add following dependencies
1 <dependency>
2 <groupId>org.springframework.boot</groupId>
3 <artifactId>spring-boot-starter-web</artifactId>
4 </dependency>
5 <dependency>
6 <groupId>org.springframework.ai</groupId>
7 <artifactId>spring-ai-starter-model-ollama</artifactId>
8 </dependency>
9 <dependency>
10 <groupId>org.springframework.ai</groupId>
11 <artifactId>spring-ai-starter-vector-store-elasticsearch</artifactId>
12 </dependency>
13 <dependency>
14 <groupId>org.projectlombok</groupId>
15 <artifactId>lombok</artifactId>
16 <optional>true</optional>
17 </dependency>
18 <dependency>
19 <groupId>org.springframework.boot</groupId>
20 <artifactId>spring-boot-starter-actuator</artifactId>
21 </dependency>
22 <dependency>
23 <groupId>org.springframework.boot</groupId>
24 <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
25 </dependency>
26 <dependency>
27 <groupId>org.springframework.boot</groupId>
28 <artifactId>spring-boot-starter-thymeleaf</artifactId>
29 </dependency>
spring-ai-starter-model-ollama
dependency to auto-configure ollama which is already installed on the machine.
spring-ai-starter-vector-store-elasticsearch
to auto-configure VectorStore's Elastic Search Implementation
ElasticsearchVectorStore
Make following changes in application.properties to set default values for auto-configuration
1# Name of embeddings model
2# to see list of installed models from the system execute `ollama list` on terminal
3spring.ai.ollama.embedding.model=llama3.2
4spring.elasticsearch.uris=http://localhost:9200
5spring.ai.vectorstore.elasticsearch.index-name=amazon-products
6spring.ai.vectorstore.elasticsearch.similarity=cosine
7spring.threads.virtual.enabled=true
Rest Controller
1package in.silentsudo.springbootvectorsearch;
2
3import co.elastic.clients.elasticsearch.ElasticsearchClient;
4import co.elastic.clients.elasticsearch._types.KnnSearch;
5import lombok.RequiredArgsConstructor;
6import lombok.extern.slf4j.Slf4j;
7import org.springframework.ai.document.Document;
8import org.springframework.ai.embedding.EmbeddingModel;
9import org.springframework.ai.model.EmbeddingUtils;
10import org.springframework.ai.vectorstore.SearchRequest;
11import org.springframework.ai.vectorstore.VectorStore;
12import org.springframework.data.domain.Page;
13import org.springframework.data.domain.Pageable;
14import org.springframework.data.elasticsearch.client.elc.NativeQuery;
15import org.springframework.data.elasticsearch.client.elc.NativeQueryBuilder;
16import org.springframework.data.elasticsearch.core.ElasticsearchOperations;
17import org.springframework.data.elasticsearch.core.SearchHit;
18import org.springframework.data.elasticsearch.core.SearchHits;
19import org.springframework.http.ResponseEntity;
20import org.springframework.web.bind.annotation.*;
21
22import java.util.List;
23import java.util.Map;
24
25@RestController
26@RequestMapping("/api/products")
27@RequiredArgsConstructor
28@Slf4j
29public class Api {
30
31 private final VectorStore vectorStore;
32 private final EmbeddingModel embeddingModel;
33 private final AmazonProductRepository repository;
34 private final ElasticsearchClient client;
35 private final ElasticsearchOperations operations;
36
37 @GetMapping("/")
38 public Page<AmazonProduct> get(Pageable pageable) {
39 return repository.findAll(pageable);
40 }
41
42 @PostMapping("/search")
43 public ResponseEntity<Map<String, Object>> search(@RequestBody Map<String, String> body,
44 @RequestParam(defaultValue = "0") int page,
45 @RequestParam(defaultValue = "100") int size,
46 @RequestParam(defaultValue = "id") String sortBy,
47 @RequestParam(defaultValue = "true") boolean ascending) {
48 final String searchTerm = body.get("term");
49 log.info("Received request to search for {}", searchTerm);
50 log.info("Embeddings Model: {} with dimensions: {}", embeddingModel, embeddingModel.dimensions());
51 //default search
52 List<Document> documents = vectorStore.similaritySearch(
53 SearchRequest.builder()
54 .query(searchTerm)
55 .topK(10)
56 .similarityThreshold(0.5)
57 .build()
58 );
59
60 log.info("Embeddings: {}, {}", embeddingModel.dimensions(), embeddingModel.embed(searchTerm).length);
61 return ResponseEntity.ok(Map.of("query", searchTerm, "documents", documents));
62 }
63
64}
Execute and View result
Create json file for request as below:
1{
2 "term": "perfume"
3}
Execute http command from terminal
1http POST http://localhost:8087/api/products/search < search.json
We can view the result as below with matching score
1ashish@es:~/spring-boot-vector-search$ http POST http://localhost:8087/api/products/search < search.json
2HTTP/1.1 200
3Connection: keep-alive
4Content-Type: application/json
5Date: Sun, 13 Jul 2025 12:34:33 GMT
6Keep-Alive: timeout=60
7Transfer-Encoding: chunked
8
9{
10 "documents": [
11 {
12 "id": "948da22634eaf91a4de3d2fa6d87ce79",
13 "media": null,
14 "metadata": {
15 "distance": 0.3885784000000001
16 },
17 "score": 0.6114215999999999,
18 "text": "Title: Aco Jasmine Premium Scent, Long Lasting, Fresh & Soothing Fragrance Perfume Spray For Womens, 100ml, Brand Name: ACO perfumes"
19 },
20 {
21 "id": "2fbcc56269fd6e2aa59c95640babb64a",
22 "media": null,
23 "metadata": {
24 "distance": 0.48872380000000004
25 },
26 "score": 0.5112762,
27 "text": "Title: Parag Fragrances Blue Kamal 30ml Eau De Perfume For Men & Women (Long Lasting Natural Perfume Spray) With Attractive Imported Perfume Bottle, Brand Name: Parag fragrances"
28 }
29 ],
30 "query": "perfume"
31}
I created a small demo for ui purpose which is as below:
Go back to Vector Search using Spring AI Elastic Search and Apache Nifi 2