本文目录导读:

我来为您提供一个完整的Java向量数据库案例,使用Qdrant作为向量数据库,并集成OpenAI的Embedding API。
项目结构
vector-demo/
├── pom.xml
├── src/main/java/com/example/vectordemo/
│ ├── VectorDemoApplication.java
│ ├── config/
│ │ └── VectorConfig.java
│ ├── service/
│ │ ├── EmbeddingService.java
│ │ ├── VectorService.java
│ │ └── DocumentService.java
│ ├── model/
│ │ ├── Document.java
│ │ └── SearchResult.java
│ └── controller/
│ └── VectorController.java
Maven依赖 (pom.xml)
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.2.0</version>
</parent>
<groupId>com.example</groupId>
<artifactId>vector-demo</artifactId>
<version>1.0.0</version>
<properties>
<java.version>17</java.version>
</properties>
<dependencies>
<!-- Spring Boot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Qdrant Client -->
<dependency>
<groupId>io.qdrant</groupId>
<artifactId>client</artifactId>
<version>1.9.0</version>
</dependency>
<!-- OpenAI API -->
<dependency>
<groupId>com.theokanning.openai-gpt3-java</groupId>
<artifactId>service</artifactId>
<version>0.18.2</version>
</dependency>
<!-- Lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- Jackson for JSON -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
</dependency>
<!-- Test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
配置类 (VectorConfig.java)
package com.example.vectordemo.config;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class VectorConfig {
@Value("${qdrant.host:localhost}")
private String qdrantHost;
@Value("${qdrant.port:6334}")
private int qdrantPort;
@Bean
public QdrantClient qdrantClient() {
QdrantGrpcClient grpcClient = QdrantGrpcClient.newBuilder(
qdrantHost, qdrantPort, true
).build();
return new QdrantClient(grpcClient);
}
@Bean
public EmbeddingService embeddingService() {
return new EmbeddingService();
}
}
Embedding服务 (EmbeddingService.java)
package com.example.vectordemo.config;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.util.ArrayList;
import java.util.List;
@Service
public class EmbeddingService {
private final HttpClient httpClient;
private final ObjectMapper objectMapper;
@Value("${openai.api.key}")
private String apiKey;
@Value("${openai.embedding.model:text-embedding-ada-002}")
private String model;
public EmbeddingService() {
this.httpClient = HttpClient.newHttpClient();
this.objectMapper = new ObjectMapper();
}
/**
* 将文本转换为向量
*/
public List<Float> createEmbedding(String text) {
try {
String requestBody = objectMapper.writeValueAsString(
Map.of(
"model", model,
"input", text
)
);
HttpRequest request = HttpRequest.newBuilder()
.uri(URI.create("https://api.openai.com/v1/embeddings"))
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + apiKey)
.POST(HttpRequest.BodyPublishers.ofString(requestBody))
.build();
HttpResponse<String> response = httpClient.send(
request,
HttpResponse.BodyHandlers.ofString()
);
if (response.statusCode() == 200) {
JsonNode root = objectMapper.readTree(response.body());
return objectMapper.convertValue(
root.path("data").get(0).path("embedding"),
new ArrayList<Float>().getClass()
);
} else {
throw new RuntimeException("Embedding API error: " + response.body());
}
} catch (Exception e) {
throw new RuntimeException("Failed to create embedding", e);
}
}
private record Map(String s, String s2) {
}
}
文档模型 (Document.java)
package com.example.vectordemo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.ArrayList;
import java.util.List;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Document {
private String id;
private String content;
private String metadata;
private List<Float> vector = new ArrayList<>();
public Document(String id, String content, String metadata) {
this.id = id;
this.content = content;
this.metadata = metadata;
}
}
向量服务 (VectorService.java)
package com.example.vectordemo.service;
import com.example.vectordemo.model.Document;
import com.example.vectordemo.model.SearchResult;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.grpc.JsonWithInt;
import io.qdrant.client.grpc.Points;
import io.qdrant.client.grpc.Collections;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.concurrent.ExecutionException;
@Service
public class VectorService {
private final QdrantClient qdrantClient;
private final EmbeddingService embeddingService;
private static final String COLLECTION_NAME = "documents";
private static final int VECTOR_SIZE = 1536; // text-embedding-ada-002的向量维度
public VectorService(QdrantClient qdrantClient, EmbeddingService embeddingService) {
this.qdrantClient = qdrantClient;
this.embeddingService = embeddingService;
initializeCollection();
}
/**
* 初始化向量集合
*/
private void initializeCollection() {
try {
boolean exists = qdrantClient.collectionExistsAsync(COLLECTION_NAME).get();
if (!exists) {
Collections.VectorParams vectorParams = Collections.VectorParams.newBuilder()
.setSize(VECTOR_SIZE)
.setDistance(Collections.Distance.Cosine)
.build();
Collections.CreateCollection createCollection = Collections.CreateCollection.newBuilder()
.setCollectionName(COLLECTION_NAME)
.setVectorsConfig(
Collections.VectorsConfig.newBuilder()
.setParams(vectorParams)
)
.build();
qdrantClient.createCollectionAsync(createCollection).get();
System.out.println("Collection created: " + COLLECTION_NAME);
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 添加文档
*/
public void addDocument(Document document) {
try {
// 生成向量
List<Float> embedding = embeddingService.createEmbedding(document.getContent());
document.setVector(embedding);
// 构建payload
Map<String, JsonWithInt> payload = new HashMap<>();
payload.put("content", JsonWithInt.from(document.getContent()));
payload.put("metadata", JsonWithInt.from(document.getMetadata()));
Points.PointStruct point = Points.PointStruct.newBuilder()
.setId(Points.PointId.newBuilder()
.setUuid(document.getId()))
.addAllVectors(
Collections.Vectors.newBuilder()
.putData(COLLECTION_NAME,
Points.Vectors.newBuilder()
.setVector(Points.Vector.newBuilder()
.addAllData(document.getVector())
.build())
.build())
.build())
.putAllPayload(payload)
.build();
qdrantClient.upsertAsync(
Points.UpsertPoints.newBuilder()
.setCollectionName(COLLECTION_NAME)
.addPoints(point)
.build()
).get();
System.out.println("Document added: " + document.getId());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 批量添加文档
*/
public void addDocuments(List<Document> documents) {
documents.forEach(this::addDocument);
}
/**
* 搜索相似文档
*/
public List<SearchResult> searchSimilar(String query, int limit) {
try {
// 生成查询向量
List<Float> queryVector = embeddingService.createEmbedding(query);
// 构建搜索请求
Points.SearchPoints searchPoints = Points.SearchPoints.newBuilder()
.setCollectionName(COLLECTION_NAME)
.addAllVector(queryVector)
.setLimit(limit)
.setWithPayload(true)
.build();
Points.SearchResponse response = qdrantClient.searchAsync(searchPoints).get();
List<SearchResult> results = new ArrayList<>();
for (Points.ScoredPoint scoredPoint : response.getResultList()) {
SearchResult result = new SearchResult();
result.setScore(scoredPoint.getScore());
result.setContent(scoredPoint.getPayloadOrThrow("content").getStringValue());
result.setMetadata(scoredPoint.getPayloadOrThrow("metadata").getStringValue());
results.add(result);
}
return results;
} catch (Exception e) {
e.printStackTrace();
return Collections.emptyList();
}
}
/**
* 删除文档
*/
public void deleteDocument(String id) {
try {
Points.DeletePoints deletePoints = Points.DeletePoints.newBuilder()
.setCollectionName(COLLECTION_NAME)
.addPoints(
Points.PointId.newBuilder()
.setUuid(id)
)
.build();
qdrantClient.deleteAsync(deletePoints).get();
System.out.println("Document deleted: " + id);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取集合统计信息
*/
public Map<String, Object> getCollectionInfo() {
Map<String, Object> info = new HashMap<>();
try {
Collections.CollectionInfo collectionInfo =
qdrantClient.getCollectionInfoAsync(COLLECTION_NAME).get()
.getResult();
info.put("name", collectionInfo.getConfig().getParams().getVectorsConfig());
info.put("points_count", collectionInfo.getPointsCount());
info.put("status", collectionInfo.getStatus().name());
} catch (Exception e) {
e.printStackTrace();
}
return info;
}
}
搜索结果模型 (SearchResult.java)
package com.example.vectordemo.model;
import lombok.Data;
@Data
public class SearchResult {
private String content;
private String metadata;
private float score;
}
控制器 (VectorController.java)
package com.example.vectordemo.controller;
import com.example.vectordemo.model.Document;
import com.example.vectordemo.model.SearchResult;
import com.example.vectordemo.service.VectorService;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/api/vector")
public class VectorController {
private final VectorService vectorService;
public VectorController(VectorService vectorService) {
this.vectorService = vectorService;
}
/**
* 添加单个文档
*/
@PostMapping("/documents")
public ResponseEntity<String> addDocument(@RequestBody Document document) {
vectorService.addDocument(document);
return ResponseEntity.ok("Document added successfully: " + document.getId());
}
/**
* 批量添加文档
*/
@PostMapping("/documents/batch")
public ResponseEntity<String> addDocuments(@RequestBody List<Document> documents) {
vectorService.addDocuments(documents);
return ResponseEntity.ok("Added " + documents.size() + " documents");
}
/**
* 搜索相似文档
*/
@GetMapping("/search")
public ResponseEntity<List<SearchResult>> search(
@RequestParam String query,
@RequestParam(defaultValue = "10") int limit) {
List<SearchResult> results = vectorService.searchSimilar(query, limit);
return ResponseEntity.ok(results);
}
/**
* 删除文档
*/
@DeleteMapping("/documents/{id}")
public ResponseEntity<String> deleteDocument(@PathVariable String id) {
vectorService.deleteDocument(id);
return ResponseEntity.ok("Document deleted: " + id);
}
/**
* 获取集合信息
*/
@GetMapping("/info")
public ResponseEntity<Map<String, Object>> getCollectionInfo() {
return ResponseEntity.ok(vectorService.getCollectionInfo());
}
}
主应用类 (VectorDemoApplication.java)
package com.example.vectordemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class VectorDemoApplication {
public static void main(String[] args) {
SpringApplication.run(VectorDemoApplication.class, args);
}
}
配置文件 (application.yml)
server:
port: 8080
spring:
application:
name: vector-demo
qdrant:
host: localhost
port: 6334
openai:
api:
key: ${OPENAI_API_KEY}
embedding:
model: text-embedding-ada-002
测试示例
// 测试数据导入和搜索
@Component
public class VectorDataLoader implements CommandLineRunner {
@Autowired
private VectorService vectorService;
@Override
public void run(String... args) {
// 创建示例文档
List<Document> documents = List.of(
new Document(
UUID.randomUUID().toString(),
"Java是一种面向对象的编程语言,由Sun Microsystems开发",
"category:programming"
),
new Document(
UUID.randomUUID().toString(),
"Spring Boot是一个基于Java的开源框架,用于简化Spring应用的开发",
"category:framework"
),
new Document(
UUID.randomUUID().toString(),
"向量数据库专门用于存储和检索高维向量数据的数据库系统",
"category:database"
),
new Document(
UUID.randomUUID().toString(),
"OpenAI的人工智能模型可以生成高质量的文本嵌入向量",
"category:AI"
)
);
// 批量添加文档
vectorService.addDocuments(documents);
// 执行搜索测试
List<SearchResult> results = vectorService.searchSimilar(
"什么数据库适合存储向量数据?", 5
);
System.out.println("=== 搜索结果 ===");
results.forEach(result -> {
System.out.println("相似度: " + result.getScore());
System.out.println("内容: " + result.getContent());
System.out.println("---");
});
}
}
使用说明
-
启动Qdrant:
docker run -p 6333:6333 -p 6334:6334 qdrant/qdrant
-
设置环境变量:
export OPENAI_API_KEY="your-openai-api-key"
-
运行应用:
mvn spring-boot:run
-
测试API:
# 添加文档 curl -X POST http://localhost:8080/api/vector/documents \ -H "Content-Type: application/json" \ -d '{"id":"doc-1","content":"Java编程语言","metadata":"programming"}'
搜索
curl "http://localhost:8080/api/vector/search?query=编程语言&limit=5"
获取集合信息
curl http://localhost:8080/api/vector/info
这个案例展示了如何使用Java与向量数据库(Qdrant)集成,包括:
- 文档向量化处理
- 相似度搜索
- 文档管理
- REST API提供外部服务
您可以根据实际需求,调整向量维度、距离计算方式,或切换到其他向量数据库(如Milvus、Pinecone等)。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播