向量數據庫是一種特殊類型的數據庫,在 AI 應用中發揮著至關重要的作用。
在向量數據庫中,查詢與傳統關系型數據庫不同。它們執行的是相似性搜索,而非精確匹配。當給定一個向量作為查詢時,向量數據庫會返回與該查詢向量“相似”的向量。
Spring AI 通過?VectorStore
?接口提供了一個抽象的 API,用于與向量數據庫交互。
VectorStore接口中主要方法:
public interface VectorStore extends DocumentWriter {default String getName() {return this.getClass().getSimpleName();}// 向向量數據庫寫入數據void add(List<Document> documents);// 根據id刪除數據void delete(List<String> idList);// 根據過濾表達式刪除數據void delete(Filter.Expression filterExpression);default void delete(String filterExpression) { ... };// 進行相似度搜索List<Document> similaritySearch(String query);List<Document> similaritySearch(SearchRequest request);default <T> Optional<T> getNativeClient() {return Optional.empty();}
}
支持的向量數據庫:
- Azure Vector Search
- Apache Cassandra
- Chroma Vector Store
- Elasticsearch Vector Store
- GemFire Vector Store
- MariaDB Vector Store
- Milvus Vector Store
- MongoDB Atlas Vector Store
- Neo4j Vector Store
- OpenSearch Vector Store
- Oracle Vector Store
- PgVector Store
- Pinecone Vector Store
- Qdrant Vector Store
- Redis Vector Store
- SAP Hana Vector Store
- Typesense Vector Store
- Weaviate Vector Store
- SimpleVectorStore - 一個簡單的持久化向量存儲實現,適合教學目的。
SimpleVectorStore使用
注意:需要提前啟動上篇博客中通過Ollama安裝的嵌入模型
SimpleVectorStore將向量數據存儲在內存中。
導入jar
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-vector-store</artifactId></dependency>
創建SimpleVectorStore對象
@Beanpublic SimpleVectorStore vectorStore() {return SimpleVectorStore.builder(embeddingModel).build();}
創建對象時,將存儲對象和嵌入模型進行關聯。關于嵌入模型的使用,參考:
Spring AI(5)——通過嵌入模型進行數據的向量化處理-CSDN博客
向SimpleVectorStore對象中寫入測試數據
本例中,創建Document對象時,三個參數分別為:文檔id,文檔內容,文檔的元數據。
@PostConstructpublic void init() {List<Document> documents = List.of(new Document("1", "今天天氣不錯", Map.of("country", "鄭州", "date", "2025-05-13")),new Document("2", "天氣不錯,適合旅游", Map.of("country", "開封", "date", "2025-05-15")),new Document("3", "去哪里旅游好呢", Map.of("country", "洛陽", "date", "2025-05-15")));// 存儲數據simpleVectorStore.add(documents);}
通過調試可以看出,?SimpleVectorStore對象中存儲的原始文檔信息和向量化后的數據。
進行相似度搜索
根據字符串內容進行搜索
@GetMapping("/store")public String store(String message) {// 相似度檢索List<Document> list= simpleVectorStore.similaritySearch("旅游");System.out.println(list.size());System.out.println(list.get(0).getText());return "success";}
通過調試可以看到,對所有的數據進行向量的相似度計算,并進行打分,計算結果按照score的降序排列?。
根據元數據過濾器進行搜索
使用字符串設置搜索條件
例如:
-
"country == 'BG'"
-
"genre == 'drama' && year >= 2020"
-
"genre in ['comedy', 'documentary', 'drama']"
SearchRequest request = SearchRequest.builder().query("World").filterExpression("country == 'Bulgaria'").build();
使用Filter.Expression設置搜索條件
可以使用?FilterExpressionBuilder
?創建?Filter.Expression
?的實例。一個簡單的示例如下:
FilterExpressionBuilder b = new FilterExpressionBuilder();
Expression expression = this.b.eq("country", "BG").build();
可以使用以下運算符構建復雜的表達式:
EQUALS: '=='
MINUS : '-'
PLUS: '+'
GT: '>'
GE: '>='
LT: '<'
LE: '<='
NE: '!='
可以使用以下運算符組合表達式:
AND: 'AND' | 'and' | '&&';
OR: 'OR' | 'or' | '||';
參考示例:
Expression exp = b.and(b.eq("genre", "drama"), b.gte("year", 2020)).build();
還可以使用以下運算符:
IN: 'IN' | 'in';
NIN: 'NIN' | 'nin';
NOT: 'NOT' | 'not';
參考示例:
Expression exp = b.and(b.in("genre", "drama", "documentary"), b.not(b.lt("year", 2020))).build();
測試案例:?
@GetMapping("/store")public String store(String message) {// 相似度檢索// List<Document> list = simpleVectorStore.similaritySearch("旅游");// 創建過濾器對象FilterExpressionBuilder b = new FilterExpressionBuilder();Filter.Expression filter = b.eq("country", "鄭州").build();// Filter.Expression filter = b.and(b.eq("country", "鄭州"), b.gte("date", "2025-05-15")).build();;// 創建搜索對象SearchRequest request = SearchRequest.builder().query("旅游") // 搜索內容.filterExpression(filter) // 指定過濾器對象.build();List<Document> list = simpleVectorStore.similaritySearch(request);System.out.println(list.size());System.out.println(list.get(0).getText());return "success";}
刪除數據
@GetMapping("/store2")public String store2(String message) {// 刪除數據simpleVectorStore.delete(List.of("3"));return "success";}
注意:也可以根據元數據過濾器進行刪除,本文不再演示
?Milvus進行向量存儲
關于milvus的介紹和環境安裝,參考:
LangChain4j(16)——使用milvus進行向量存儲-CSDN博客
注意:需要提前啟動上篇博客中通過Ollama安裝的嵌入模型
導入jar?
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-vector-store-milvus</artifactId>
</dependency>
yml配置
在原來的基礎上,增加如下配置:
spring:ai:vectorstore:milvus:client:host: "localhost"port: 19530databaseName: "myai"collectionName: "vector_store"embeddingDimension: 768indexType: IVF_FLATmetricType: COSINE
該配置中指定了milvus的數據庫名,collection名稱,向量緯度,索引的類型,向量搜索的算法等,更多的屬性設置可以參考官網:
屬性 | 描述 | 默認值 |
---|---|---|
spring.ai.vectorstore.milvus.database-name | 要使用的 Milvus 數據庫名稱。 | default |
spring.ai.vectorstore.milvus.collection-name | 用于存儲向量的 Milvus Collection 名稱 | vector_store |
spring.ai.vectorstore.milvus.initialize-schema | 是否初始化 Milvus 后端 | false |
spring.ai.vectorstore.milvus.embedding-dimension | 存儲在 Milvus Collection 中的向量維度。 | 1536 |
spring.ai.vectorstore.milvus.index-type | 為 Milvus Collection 創建的索引類型。 | IVF_FLAT |
spring.ai.vectorstore.milvus.metric-type | 用于 Milvus Collection 的度量類型(Metric Type)。 | COSINE |
spring.ai.vectorstore.milvus.index-parameters | 用于 Milvus Collection 的索引參數。 | {"nlist":1024} |
spring.ai.vectorstore.milvus.id-field-name | Collection 的 ID 字段名稱 | doc_id |
spring.ai.vectorstore.milvus.is-auto-id | 布爾標志,指示 ID 字段是否使用 auto-id | false |
spring.ai.vectorstore.milvus.content-field-name | Collection 的內容字段名稱 | content |
spring.ai.vectorstore.milvus.metadata-field-name | Collection 的元數據字段名稱 | metadata |
spring.ai.vectorstore.milvus.embedding-field-name | Collection 的嵌入字段名稱 | embedding |
spring.ai.vectorstore.milvus.client.host | 主機名稱或地址。 | localhost |
spring.ai.vectorstore.milvus.client.port | 連接端口。 | 19530 |
spring.ai.vectorstore.milvus.client.uri | Milvus 實例的 URI | - |
spring.ai.vectorstore.milvus.client.token | 用作身份識別和認證目的的 Token。 | - |
spring.ai.vectorstore.milvus.client.connect-timeout-ms | 客戶端通道的連接超時值。超時值必須大于零。 | 10000 |
spring.ai.vectorstore.milvus.client.keep-alive-time-ms | 客戶端通道的 Keep-alive 時間值。Keep-alive 值必須大于零。 | 55000 |
spring.ai.vectorstore.milvus.client.keep-alive-timeout-ms | 客戶端通道的 Keep-alive 超時值。超時值必須大于零。 | 20000 |
spring.ai.vectorstore.milvus.client.rpc-deadline-ms | 愿意等待服務器回復的截止時間。設置截止時間后,客戶端在遇到由網絡波動引起的快速 RPC 失敗時將等待。截止時間值必須大于或等于零。 | 0 |
spring.ai.vectorstore.milvus.client.client-key-path | 用于 TLS 雙向認證的 client.key 路徑,僅當 "secure" 為 true 時生效 | - |
spring.ai.vectorstore.milvus.client.client-pem-path | 用于 TLS 雙向認證的 client.pem 路徑,僅當 "secure" 為 true 時生效 | - |
spring.ai.vectorstore.milvus.client.ca-pem-path | 用于 TLS 雙向認證的 ca.pem 路徑,僅當 "secure" 為 true 時生效 | - |
spring.ai.vectorstore.milvus.client.server-pem-path | 用于 TLS 單向認證的 server.pem 路徑,僅當 "secure" 為 true 時生效。 | - |
spring.ai.vectorstore.milvus.client.server-name | 設置 SSL 主機名檢查的目標名稱覆蓋,僅當 "secure" 為 True 時生效。注意:此值會傳遞給 grpc.ssl_target_name_override | - |
spring.ai.vectorstore.milvus.client.secure | 保護此連接的授權,設置為 True 以啟用 TLS。 | false |
spring.ai.vectorstore.milvus.client.idle-timeout-ms | 客戶端通道的空閑超時值。超時值必須大于零。 | 24h |
spring.ai.vectorstore.milvus.client.username | 此連接的用戶名和密碼。 | root |
spring.ai.vectorstore.milvus.client.password | 此連接的密碼。 | milvus |
通過attu創建collection
注意:collection中的字段名稱使用的是上面屬性中的默認名稱:
spring.ai.vectorstore.milvus.id-field-name=doc_id
spring.ai.vectorstore.milvus.content-field-name=content
spring.ai.vectorstore.milvus.metadata-field-name=metadata
spring.ai.vectorstore.milvus.embedding-field-name=embedding
注入MilvusVectorStore對象
@Resourceprivate MilvusVectorStore milvusVectorStore;
添加測試數據
@PostConstructpublic void init() {List<Document> documents = List.of(new Document("今天天氣不錯", Map.of("country", "鄭州", "date", "2025-05-13")),new Document("天氣不錯,適合旅游", Map.of("country", "開封", "date", "2025-05-15")),new Document("去哪里旅游好呢", Map.of("country", "洛陽", "date", "2025-05-15")));// 存儲數據milvusVectorStore.add(documents);}
本例沒有指定Document的id,id會隨機生成
相似度搜索
@GetMapping("/search")public String search(String message) {// 相似度檢索List<Document> list = milvusVectorStore.similaritySearch("旅游");System.out.println(list.size());System.out.println(list.get(0).getText());return "success";}@GetMapping("/search5")public String search5(String message) {MilvusSearchRequest request = MilvusSearchRequest.milvusBuilder().query("旅游").topK(5).similarityThreshold(0.7).filterExpression("date == '2025-05-15'") // Ignored if nativeExpression is set//.searchParamsJson("{\"nprobe\":128}").build();// 相似度檢索List<Document> list = milvusVectorStore.similaritySearch(request);System.out.println(list.size());System.out.println(list.get(0).getText());return "success";}
top_k:?表示根據token排名,只考慮前k個token
similarity threshold:相似度的閾值