找回密码
 立即注册
首页 业界区 业界 Spring AI 代码分析(六)--Vector Store

Spring AI 代码分析(六)--Vector Store

肇默步 2025-11-23 21:40:00
Spring AI Vector Store 分析

请关注微信公众号:阿呆-bot
1. 工程结构概览

spring-ai-vector-store 是 Spring AI 的向量存储抽象层,它提供了统一的接口来操作各种向量数据库。Spring AI 支持 20+ 种向量数据库实现,包括 Neo4j、Elasticsearch、Milvus、PGVector、Pinecone 等。
  1. spring-ai-vector-store/          # 核心抽象层
  2. ├── VectorStore.java             # 向量存储接口
  3. ├── SearchRequest.java           # 搜索请求
  4. ├── filter/                      # 过滤表达式
  5. │   └── Filter.java
  6. └── observation/                 # 观察性支持
  7. vector-stores/                   # 具体实现
  8. ├── spring-ai-neo4j-store/       # Neo4j 实现
  9. ├── spring-ai-elasticsearch-store/  # Elasticsearch 实现
  10. ├── spring-ai-milvus-store/      # Milvus 实现
  11. ├── spring-ai-pgvector-store/    # PostgreSQL/PGVector 实现
  12. └── ... (20+ 种实现)
复制代码
2. 技术体系与模块关系

向量存储采用统一的抽象接口,所有实现都遵循相同的模式:
1.png

3. 关键场景示例代码

3.1 基础使用

所有向量存储都使用相同的 API:
  1. @Autowired
  2. private EmbeddingModel embeddingModel;
  3. // Neo4j
  4. Neo4jVectorStore vectorStore = Neo4jVectorStore.builder(driver, embeddingModel)
  5.     .initializeSchema(true)
  6.     .build();
  7. // Elasticsearch
  8. ElasticsearchVectorStore vectorStore = ElasticsearchVectorStore.builder(restClient, embeddingModel)
  9.     .initializeSchema(true)
  10.     .build();
  11. // Milvus
  12. MilvusVectorStore vectorStore = MilvusVectorStore.builder(milvusClient, embeddingModel)
  13.     .initializeSchema(true)
  14.     .build();
  15. // 添加文档
  16. vectorStore.add(List.of(
  17.     new Document("Spring AI 是一个 AI 应用开发框架"),
  18.     new Document("支持多种 AI 模型和向量数据库")
  19. ));
  20. // 相似度搜索
  21. List<Document> results = vectorStore.similaritySearch(
  22.     SearchRequest.query("AI 框架")
  23.         .withTopK(5)
  24.         .withSimilarityThreshold(0.7)
  25. );
复制代码
3.2 元数据过滤

所有实现都支持元数据过滤:
  1. // 使用过滤表达式
  2. List<Document> results = vectorStore.similaritySearch(
  3.     SearchRequest.query("查询内容")
  4.         .withTopK(10)
  5.         .withFilterExpression("category == '技术' AND year > 2023")
  6. );
复制代码
3.3 批量处理

支持批量添加和批处理策略:
  1. // 使用批处理策略
  2. vectorStore = MilvusVectorStore.builder(milvusClient, embeddingModel)
  3.     .batchingStrategy(new TokenCountBatchingStrategy(1000))
  4.     .build();
  5. // 批量添加
  6. List<Document> documents = loadDocuments(); // 大量文档
  7. vectorStore.add(documents); // 自动批处理
复制代码
4. 核心实现图

4.1 统一抽象设计

2.png

5. 入口类与关键类关系

3.png

6. 关键实现逻辑分析

6.1 统一接口设计

VectorStore 接口提供了统一的向量存储抽象:
  1. public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
  2.     void add(List<Document> documents);
  3.     void delete(List<String> idList);
  4.     void delete(Filter.Expression filterExpression);
  5.     List<Document> similaritySearch(SearchRequest request);
  6. }
复制代码
这种设计让所有向量数据库都使用相同的 API,用户可以轻松切换不同的实现。
6.2 Neo4j 实现

Neo4j 使用 Cypher 查询和 HNSW 索引:
  1. public class Neo4jVectorStore extends AbstractObservationVectorStore {
  2.     @Override
  3.     public void add(List<Document> documents) {
  4.         // 1. 生成嵌入向量
  5.         List<float[]> embeddings = embeddingModel.embed(documents);
  6.         
  7.         // 2. 构建 Cypher 查询
  8.         String cypher = """
  9.             UNWIND $rows AS row
  10.             MERGE (n:Document {id: row.id})
  11.             SET n.text = row.properties.text,
  12.                 n.embedding = row.embedding,
  13.                 n.metadata = row.properties.metadata
  14.             """;
  15.         
  16.         // 3. 执行批量插入
  17.         try (var session = driver.session(sessionConfig)) {
  18.             session.executeWrite(tx -> {
  19.                 tx.run(cypher, Map.of("rows", documentRows));
  20.             });
  21.         }
  22.     }
  23.    
  24.     @Override
  25.     public List<Document> similaritySearch(SearchRequest request) {
  26.         // 1. 生成查询向量
  27.         float[] queryEmbedding = embeddingModel.embed(request.getQuery());
  28.         
  29.         // 2. 转换过滤表达式
  30.         String whereClause = filterConverter.convert(request.getFilterExpression());
  31.         
  32.         // 3. 构建向量搜索查询
  33.         String cypher = """
  34.             CALL db.index.vector.queryNodes(
  35.                 '%s',
  36.                 %d,
  37.                 $queryVector
  38.             )
  39.             YIELD node, score
  40.             WHERE %s
  41.             RETURN node, score
  42.             ORDER BY score DESC
  43.             LIMIT %d
  44.             """.formatted(indexName, topK, whereClause, request.getTopK());
  45.         
  46.         // 4. 执行查询并转换结果
  47.         return executeCypherQuery(cypher, queryEmbedding);
  48.     }
  49. }
复制代码
特点

  • 使用 Neo4j 的向量索引(HNSW)
  • 支持 Cypher 查询的元数据过滤
  • 支持 Cosine 和 Euclidean 距离
6.3 Elasticsearch 实现

Elasticsearch 使用 k-NN 搜索:
  1. public class ElasticsearchVectorStore extends AbstractObservationVectorStore {
  2.     @Override
  3.     public void add(List<Document> documents) {
  4.         // 1. 生成嵌入向量
  5.         List<float[]> embeddings = embeddingModel.embed(documents);
  6.         
  7.         // 2. 构建批量索引请求
  8.         BulkRequest bulkRequest = new BulkRequest();
  9.         for (int i = 0; i < documents.size(); i++) {
  10.             IndexRequest request = new IndexRequest(indexName)
  11.                 .id(documents.get(i).getId())
  12.                 .source(Map.of(
  13.                     "content", documents.get(i).getText(),
  14.                     "embedding", embeddings.get(i),
  15.                     "metadata", documents.get(i).getMetadata()
  16.                 ));
  17.             bulkRequest.add(request);
  18.         }
  19.         
  20.         // 3. 执行批量索引
  21.         restClient.bulk(bulkRequest);
  22.     }
  23.    
  24.     @Override
  25.     public List<Document> similaritySearch(SearchRequest request) {
  26.         // 1. 生成查询向量
  27.         float[] queryVector = embeddingModel.embed(request.getQuery());
  28.         
  29.         // 2. 构建 k-NN 搜索请求
  30.         KnnVectorQueryBuilder knnQuery = new KnnVectorQueryBuilder("embedding", queryVector, request.getTopK());
  31.         
  32.         // 3. 添加元数据过滤
  33.         if (request.getFilterExpression() != null) {
  34.             QueryBuilder filterQuery = filterConverter.convert(request.getFilterExpression());
  35.             knnQuery.addFilterQuery(filterQuery);
  36.         }
  37.         
  38.         // 4. 执行搜索
  39.         SearchRequest searchRequest = new SearchRequest(indexName)
  40.             .source(new SearchSourceBuilder().query(knnQuery));
  41.         
  42.         SearchResponse response = restClient.search(searchRequest);
  43.         return convertToDocuments(response);
  44.     }
  45. }
复制代码
特点

  • 使用 Elasticsearch 的 dense_vector 字段类型
  • 支持 k-NN 搜索和元数据过滤
  • 支持 Cosine、L2、Dot Product 相似度
6.4 Milvus 实现

Milvus 是专门的向量数据库:
  1. public class MilvusVectorStore extends AbstractObservationVectorStore {
  2.     @Override
  3.     public void add(List<Document> documents) {
  4.         // 1. 生成嵌入向量
  5.         List<List<Float>> embeddings = embeddingModel.embed(documents)
  6.             .stream()
  7.             .map(embedding -> Arrays.stream(embedding).boxed().collect(toList()))
  8.             .collect(toList());
  9.         
  10.         // 2. 构建插入数据
  11.         List<InsertParam.Field> fields = List.of(
  12.             new InsertParam.Field("id", documents.stream().map(Document::getId).collect(toList())),
  13.             new InsertParam.Field("content", documents.stream().map(Document::getText).collect(toList())),
  14.             new InsertParam.Field("embedding", embeddings),
  15.             new InsertParam.Field("metadata", documents.stream().map(Document::getMetadata).collect(toList()))
  16.         );
  17.         
  18.         // 3. 执行插入
  19.         InsertParam insertParam = InsertParam.newBuilder()
  20.             .withCollectionName(collectionName)
  21.             .withFields(fields)
  22.             .build();
  23.         
  24.         milvusClient.insert(insertParam);
  25.     }
  26.    
  27.     @Override
  28.     public List<Document> similaritySearch(SearchRequest request) {
  29.         // 1. 生成查询向量
  30.         float[] queryVector = embeddingModel.embed(request.getQuery());
  31.         
  32.         // 2. 构建搜索参数
  33.         SearchParam searchParam = SearchParam.newBuilder()
  34.             .withCollectionName(collectionName)
  35.             .withVectorFieldName("embedding")
  36.             .withVectors(List.of(Arrays.stream(queryVector).boxed().collect(toList())))
  37.             .withTopK(request.getTopK())
  38.             .withMetricType(metricType)
  39.             .withParams(Map.of("nprobe", 10))
  40.             .build();
  41.         
  42.         // 3. 添加过滤表达式
  43.         if (request.getFilterExpression() != null) {
  44.             String filterExpr = filterConverter.convert(request.getFilterExpression());
  45.             searchParam.withExpr(filterExpr);
  46.         }
  47.         
  48.         // 4. 执行搜索
  49.         R<SearchResults> response = milvusClient.search(searchParam);
  50.         return convertToDocuments(response.getData());
  51.     }
  52. }
复制代码
特点

  • 专门的向量数据库,性能优异
  • 支持多种索引类型(IVF_FLAT、HNSW 等)
  • 支持多种相似度度量(Cosine、L2、IP)
7. 实现对比分析

特性Neo4jElasticsearchMilvus数据库类型图数据库搜索引擎向量数据库索引算法HNSWk-NNHNSW/IVF_FLAT相似度度量Cosine, EuclideanCosine, L2, Dot ProductCosine, L2, IP元数据过滤Cypher WHEREQuery DSL表达式字符串适用场景图+向量混合查询全文+向量搜索纯向量搜索性能中等高极高扩展性好很好优秀8. 过滤表达式转换

每个实现都有自己的 FilterExpressionConverter,将统一的 Filter.Expression 转换为原生查询:
  1. // Neo4j: 转换为 Cypher WHERE 子句
  2. "n.metadata.category = '技术' AND n.metadata.year > 2023"
  3. // Elasticsearch: 转换为 Query DSL
  4. {
  5.   "bool": {
  6.     "must": [
  7.       {"term": {"metadata.category": "技术"}},
  8.       {"range": {"metadata.year": {"gt": 2023}}}
  9.     ]
  10.   }
  11. }
  12. // Milvus: 转换为表达式字符串
  13. "metadata['category'] == '技术' && metadata['year'] > 2023"
复制代码
9. 外部依赖

不同实现的依赖:
9.1 Neo4j


  • Neo4j Java Driver:Neo4j 官方驱动
  • Neo4j 5.15+:支持向量索引
9.2 Elasticsearch


  • Elasticsearch Java Client:官方 Java 客户端
  • Elasticsearch 8.0+:支持 k-NN 搜索
9.3 Milvus


  • Milvus Java SDK:Milvus 官方 SDK
  • Milvus 2.0+:向量数据库
10. 工程总结

Spring AI 向量存储的设计有几个值得学习的地方:
统一抽象。所有向量数据库都实现相同的 VectorStore 接口,这让用户可以轻松切换不同的实现,而不需要改业务代码。今天用 Neo4j,明天想换 Milvus?改个配置就行。
灵活的过滤机制。通过 Filter.Expression 和 FilterExpressionConverter,实现了统一的过滤表达式,但每个实现可以转换为自己的原生查询。写一次过滤表达式,所有数据库都能用。
观察性内置。所有实现都继承 AbstractObservationVectorStore,提供了统一的指标和追踪能力。可以监控搜索次数、延迟、错误率等,对生产环境很有用。
批处理支持。通过 BatchingStrategy,可以灵活配置批量操作的策略,提高性能。想一次插入 1000 条数据?配置个批处理策略就行。
自动模式初始化。大多数实现都支持 initializeSchema(true),自动创建必要的索引和表结构。不用手动建表,启动时自动搞定。
总的来说,Spring AI 向量存储抽象层设计既统一又灵活。统一的接口让代码简洁,灵活的实现让每个数据库都能发挥自己的优势。这种设计让 Spring AI 能够支持 20+ 种不同的向量数据库,同时保持代码的可维护性。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册