向量数据库
向量数据库是一种特殊类型的数据库,在人工智能应用中扮演着至关重要的角色。
在向量数据库中,查询方式与传统关系型数据库有所不同。它们执行的是相似性搜索,而非精确匹配。当给定一个向量作为查询时,向量数据库会返回与该查询向量“相似”的向量。关于这种相似性在高层面上是如何计算的更多详细信息,请参阅向量相似性。
向量数据库用于将数据与AI模型集成。使用向量数据库的第一步是将数据加载到数据库中。当用户查询需要发送至AI模型时,首先会检索一组相似文档。这些文档随后作为用户问题的上下文,与用户查询一同发送给AI模型。这项技术被称为检索增强生成(RAG)。
以下章节描述了用于使用多种向量数据库实现的Spring AI接口,以及一些高级使用示例。
最后一节旨在揭示向量数据库中相似性搜索的基本方法。
API 概述
本节旨在介绍 Spring AI 框架中的 VectorStore 接口及其相关类。
Spring AI 提供了通过 VectorStore 接口及其只读对应接口 VectorStoreRetriever 与向量数据库交互的抽象 API。
VectorStoreRetriever 接口
Spring AI 提供了一个名为 VectorStoreRetriever 的只读接口,它仅暴露文档检索功能:
@FunctionalInterface
public interface VectorStoreRetriever {
List<Document> similaritySearch(SearchRequest request);
default List<Document> similaritySearch(String query) {
return this.similaritySearch(SearchRequest.builder().query(query).build());
}
}
此函数式接口专为仅需从向量存储中检索文档而无需执行任何变更操作的场景设计。它遵循最小权限原则,仅暴露文档检索所必需的功能。
VectorStore 接口
VectorStore 接口继承自 VectorStoreRetriever,并增加了数据变更功能:
public interface VectorStore extends DocumentWriter, VectorStoreRetriever {
default String getName() {
return this.getClass().getSimpleName();
}
void add(List<Document> documents);
void delete(List<String> idList);
void delete(Filter.Expression filterExpression);
default void delete(String filterExpression) { ... }
default <T> Optional<T> getNativeClient() {
return Optional.empty();
}
}
VectorStore 接口结合了读写操作,允许你在向量数据库中添加、删除和搜索文档。
SearchRequest 构建器
public class SearchRequest {
public static final double SIMILARITY_THRESHOLD_ACCEPT_ALL = 0.0;
public static final int DEFAULT_TOP_K = 4;
private String query = "";
private int topK = DEFAULT_TOP_K;
private double similarityThreshold = SIMILARITY_THRESHOLD_ACCEPT_ALL;
@Nullable
private Filter.Expression filterExpression;
public static Builder from(SearchRequest originalSearchRequest) {
return builder().query(originalSearchRequest.getQuery())
.topK(originalSearchRequest.getTopK())
.similarityThreshold(originalSearchRequest.getSimilarityThreshold())
.filterExpression(originalSearchRequest.getFilterExpression());
}
public static class Builder {
private final SearchRequest searchRequest = new SearchRequest();
public Builder query(String query) {
Assert.notNull(query, "Query can not be null.");
this.searchRequest.query = query;
return this;
}
public Builder topK(int topK) {
Assert.isTrue(topK >= 0, "TopK should be positive.");
this.searchRequest.topK = topK;
return this;
}
public Builder similarityThreshold(double threshold) {
Assert.isTrue(threshold >= 0 && threshold <= 1, "Similarity threshold must be in [0,1] range.");
this.searchRequest.similarityThreshold = threshold;
return this;
}
public Builder similarityThresholdAll() {
this.searchRequest.similarityThreshold = 0.0;
return this;
}
public Builder filterExpression(@Nullable Filter.Expression expression) {
this.searchRequest.filterExpression = expression;
return this;
}
public Builder filterExpression(@Nullable String textExpression) {
this.searchRequest.filterExpression = (textExpression != null)
? new FilterExpressionTextParser().parse(textExpression) : null;
return this;
}
public SearchRequest build() {
return this.searchRequest;
}
}
public String getQuery() {...}
public int getTopK() {...}
public double getSimilarityThreshold() {...}
public Filter.Expression getFilterExpression() {...}
}
要将数据插入向量数据库,需要将其封装在 Document 对象中。Document 类封装了来自数据源(如 PDF 或 Word 文档)的内容,其中包含以字符串形式表示的文本。它还包含以键值对形式存储的元数据,包括文件名等详细信息。
向量数据库的作用是存储这些嵌入向量并支持相似度搜索。它本身并不生成嵌入向量。如需创建向量嵌入,应使用 EmbeddingModel。
接口中的 similaritySearch 方法可用于检索与给定查询字符串相似的文档。这些方法可以通过以下参数进行微调:
-
k: 一个整数,用于指定返回的相似文档的最大数量。这通常被称为"Top K"搜索或"K最近邻"(KNN)搜索。 -
threshold: 一个取值范围为0到1的浮点数值,数值越接近1表示相似度越高。默认情况下,如果您设置了阈值(例如0.75),则仅返回相似度高于此值的文档。 -
Filter.Expression: 一个用于传递流畅DSL(领域特定语言)表达式的类,其功能类似于SQL中的where子句,但专门应用于Document的元数据键值对。 -
filterExpression: 一个基于ANTLR4的外部DSL,以字符串形式接收过滤器表达式。例如,对于country、year和isActive这类元数据键,您可以使用如下表达式:country == 'UK' && year >= 2020 && isActive == true.
有关 Filter.Expression 的更多信息,请参见元数据过滤器部分。
模式初始化
某些向量存储在使用前需要初始化其后端架构。默认情况下,系统不会自动为您完成初始化。您必须通过选择启用,即在相应的构造函数参数中传递一个 boolean 值,或者如果使用 Spring Boot,则在 application.properties 或 application.yml 文件中将相应的 initialize-schema 属性设置为 true。请查阅您所使用的向量存储的文档,以获取具体的属性名称。
批处理策略
在使用向量存储时,常常需要对大量文档进行嵌入处理。虽然一次性调用嵌入所有文档看似简单直接,但这种方法可能引发问题。嵌入模型以令牌为单位处理文本,并设有最大令牌限制,通常称为上下文窗口大小。这一限制决定了单次嵌入请求所能处理的文本量。若在一次调用中尝试嵌入过多令牌,可能导致错误或产生截断的嵌入结果。
为了应对这一令牌限制,Spring AI 实现了批处理策略。该策略将大型文档集分解为较小的批次,使其能够适配嵌入模型的最大上下文窗口。批处理不仅解决了令牌限制问题,还能带来性能提升,并更高效地利用 API 速率限制。
Spring AI通过BatchingStrategy接口提供了这一功能,该接口允许根据文档的token数量以子批次的方式处理文档。
核心的 BatchingStrategy 接口定义如下:
public interface BatchingStrategy {
List<List<Document>> batch(List<Document> documents);
}
该接口定义了一个单一方法 batch,它接收一个文档列表并返回一个文档批次列表。
默认实现
Spring AI 提供了一个名为 TokenCountBatchingStrategy 的默认实现。该策略根据文档的令牌数进行分批,确保每个批次不超过计算出的最大输入令牌数。
TokenCountBatchingStrategy 的关键特性:
-
使用 OpenAI 的最大输入 token 数量 (8191) 作为默认上限。
-
包含一个预留百分比(默认 10%),为潜在开销提供缓冲。
-
计算实际的最大输入 token 数量为:
actualMaxInputTokenCount = originalMaxInputTokenCount * (1 - RESERVE_PERCENTAGE)
该策略估算每个文档的令牌数量,将它们分组为批次而不超过最大输入令牌数,如果单个文档超出此限制,则抛出异常。
您也可以通过自定义TokenCountBatchingStrategy来更好地满足特定需求。只需在Spring Boot的@Configuration类中使用自定义参数创建新实例即可实现。
以下是一个创建自定义 TokenCountBatchingStrategy Bean 的示例:
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customTokenCountBatchingStrategy() {
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE, // Specify the encoding type
8000, // Set the maximum input token count
0.1 // Set the reserve percentage
);
}
}
在此配置中:
-
EncodingType.CL100K_BASE: 指定用于分词(tokenization)的编码类型。JTokkitTokenCountEstimator使用此编码类型来准确估算令牌数量。 -
8000: 设置最大输入令牌数量。该值应小于或等于您所使用的嵌入模型的最大上下文窗口大小。 -
0.1: 设置保留百分比。该值表示从最大输入令牌数量中保留的令牌百分比。这为处理过程中令牌数量可能增加的情况创建了一个缓冲区。
默认情况下,该构造函数使用 Document.DEFAULT_CONTENT_FORMATTER 进行内容格式化,并使用 MetadataMode.NONE 进行元数据处理。如果您需要自定义这些参数,可以使用带有额外参数的完整构造函数。
一旦定义,这个自定义的TokenCountBatchingStrategy bean 将被应用程序中的EmbeddingModel实现自动使用,从而取代默认策略。
TokenCountBatchingStrategy 内部使用一个 TokenCountEstimator(具体来说是 JTokkitTokenCountEstimator)来计算 token 数量,以实现高效的批处理。这确保了基于指定编码类型的准确 token 估计。
此外,TokenCountBatchingStrategy 提供了灵活性,允许您传入自己实现的 TokenCountEstimator 接口。这一特性使您能够使用适合特定需求的自定义令牌计数策略。例如:
TokenCountEstimator customEstimator = new YourCustomTokenCountEstimator();
TokenCountBatchingStrategy strategy = new TokenCountBatchingStrategy(
this.customEstimator,
8000, // maxInputTokenCount
0.1, // reservePercentage
Document.DEFAULT_CONTENT_FORMATTER,
MetadataMode.NONE
);
使用自动截断功能
一些嵌入模型,例如 Vertex AI 文本嵌入,支持 auto_truncate 功能。启用时,模型会静默截断超过最大尺寸的文本输入并继续处理;禁用时,对于过大的输入,模型会抛出明确的错误。
在使用批处理策略进行自动截断时,必须将批处理策略配置为远高于模型实际最大值的输入词元计数。这可以防止批处理策略因处理大型文档而引发异常,使嵌入模型能够在内部处理截断操作。
自动截断配置
启用自动截断功能时,请将批处理策略的最大输入标记数设置为远高于模型的实际限制。这样可以防止批处理策略对大型文档抛出异常,使嵌入模型能够在内部处理截断。
以下是一个使用 Vertex AI 并配置自动截断、自定义 BatchingStrategy,随后在 PgVectorStore 中应用的示例配置:
@Configuration
public class AutoTruncationEmbeddingConfig {
@Bean
public VertexAiTextEmbeddingModel vertexAiEmbeddingModel(
VertexAiEmbeddingConnectionDetails connectionDetails) {
VertexAiTextEmbeddingOptions options = VertexAiTextEmbeddingOptions.builder()
.model(VertexAiTextEmbeddingOptions.DEFAULT_MODEL_NAME)
.autoTruncate(true) // Enable auto-truncation
.build();
return new VertexAiTextEmbeddingModel(connectionDetails, options);
}
@Bean
public BatchingStrategy batchingStrategy() {
// Only use a high token limit if auto-truncation is enabled in your embedding model.
// Set a much higher token count than the model actually supports
// (e.g., 132,900 when Vertex AI supports only up to 20,000)
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE,
132900, // Artificially high limit
0.1 // 10% reserve
);
}
@Bean
public VectorStore vectorStore(JdbcTemplate jdbcTemplate, EmbeddingModel embeddingModel, BatchingStrategy batchingStrategy) {
return PgVectorStore.builder(jdbcTemplate, embeddingModel)
// other properties omitted here
.build();
}
}
在此配置中:
-
嵌入模型已启用自动截断功能,使其能够优雅地处理过大的输入。
-
批处理策略使用了人为设置的高令牌限制(132,900),这远大于模型的实际限制(20,000)。
-
向量存储使用了已配置的嵌入模型和自定义的
BatchingStrategyBean。
为什么这行得通
这种方法之所以有效,是因为:
-
TokenCountBatchingStrategy会检查是否存在任何单个文档超出配置的最大值,如果存在则抛出IllegalArgumentException。 -
通过在批处理策略中设置一个非常高的限制,我们确保此检查永远不会失败。
-
超出模型限制的文档或批会被静默截断,并由嵌入模型的自动截断功能进行处理。
最佳实践
在使用自动截断时:
-
将批处理策略的最大输入令牌数设置为至少是模型实际限制的5-10倍,以避免批处理策略过早触发异常。
-
监控日志中来自嵌入模型的截断警告(注意:并非所有模型都会记录截断事件)。
-
考虑静默截断对嵌入质量的影响。
-
使用示例文档进行测试,以确保截断后的嵌入仍然满足您的要求。
-
将此配置记录在案,以供未来的维护者参考,因为这是一种非标准配置。
虽然自动截断可以防止错误,但可能导致不完整的嵌入向量。长文档末尾的重要信息可能会丢失。如果您的应用需要嵌入所有内容,请在嵌入前将文档拆分为较小的片段。
Spring Boot 自动配置
如果你正在使用 Spring Boot 自动配置,必须提供一个自定义的 BatchingStrategy bean 来覆盖 Spring AI 自带的默认策略:
@Bean
public BatchingStrategy customBatchingStrategy() {
// This bean will override the default BatchingStrategy
return new TokenCountBatchingStrategy(
EncodingType.CL100K_BASE,
132900, // Much higher than model's actual limit
0.1
);
}
在您的应用程序上下文中引入该 Bean 后,所有向量存储所使用的默认批处理策略将自动被替换。
自定义实现
虽然 TokenCountBatchingStrategy 提供了稳健的默认实现,但你可以通过 Spring Boot 的自动配置功能,自定义批处理策略以满足特定需求。
要自定义批处理策略,请在您的 Spring Boot 应用程序中定义一个 BatchingStrategy bean:
@Configuration
public class EmbeddingConfig {
@Bean
public BatchingStrategy customBatchingStrategy() {
return new CustomBatchingStrategy();
}
}
随后,这个自定义的 BatchingStrategy 将在你的应用中被 EmbeddingModel 实现自动使用。
Spring AI 支持的向量存储配置为使用默认的 TokenCountBatchingStrategy。SAP Hana 向量存储目前尚未配置为支持批处理。
VectorStore 实现
以下是 VectorStore 接口的可用实现:
-
Azure Vector Search - Azure 向量存储。
-
Apache Cassandra - Apache Cassandra 向量存储。
-
Chroma Vector Store - Chroma 向量存储。
-
GemFire Vector Store - GemFire 向量存储。
-
MariaDB Vector Store - MariaDB 向量存储。
-
Milvus Vector Store - Milvus 向量存储。
-
Neo4j Vector Store - Neo4j 向量存储。
-
OpenSearch Vector Store - OpenSearch 向量存储。
-
Oracle Vector Store - Oracle Database 向量存储。
-
PgVector Store - PostgreSQL/PGVector 向量存储。
-
Pinecone Vector Store - Pinecone 向量存储。
-
Qdrant Vector Store - Qdrant 向量存储。
-
Redis Vector Store - Redis 向量存储。
-
SAP Hana Vector Store - SAP HANA 向量存储。
-
Typesense Vector Store - Typesense 向量存储。
-
Weaviate Vector Store - Weaviate 向量存储。
-
SimpleVectorStore - 一个简单的持久化向量存储实现,适用于教学目的。
未来版本可能会支持更多实现方式。
如果你有一个需要 Spring AI 支持的向量数据库,请在 GitHub 上提交 issue,或者更好的做法是,提交一个带有实现方案的 pull request。
关于每种VectorStore实现的详细信息,可在本章各小节中找到。
示例用法
要计算向量数据库的嵌入向量,你需要选择一个与所使用的上层AI模型相匹配的嵌入模型。
例如,在使用OpenAI的ChatGPT时,我们会用到OpenAiEmbeddingModel和一个名为text-embedding-ada-002的模型。
Spring Boot starter 的 OpenAI 自动配置使 EmbeddingModel 的实现能够在 Spring 应用上下文中进行依赖注入。
写入向量存储
将数据加载到向量存储中的通用用法通常是在批处理式作业中进行的,首先将数据加载到Spring AI的Document类中,然后调用VectorStore接口上的add方法。
给定一个指向源文件的 String 引用,该文件是一个 JSON 文件,其中包含我们希望加载到向量数据库的数据,我们使用 Spring AI 的 JsonReader 来加载 JSON 中的特定字段,将其分割成小块,然后将这些小块传递给向量存储实现。VectorStore 实现会计算嵌入向量,并将 JSON 和嵌入向量存储在向量数据库中:
@Autowired
VectorStore vectorStore;
void load(String sourceFile) {
JsonReader jsonReader = new JsonReader(new FileSystemResource(sourceFile),
"price", "name", "shortDescription", "description", "tags");
List<Document> documents = jsonReader.get();
this.vectorStore.add(documents);
}
从向量存储中读取
随后,当用户提问被输入到 AI 模型时,系统会进行相似性搜索以检索相关文档,这些文档随后被“填充”到提示中,作为用户问题的上下文。
对于只读操作,你可以使用 VectorStore 接口或更专注的 VectorStoreRetriever 接口:
@Autowired
VectorStoreRetriever retriever; // Could also use VectorStore here
String question = "<question from user>";
List<Document> similarDocuments = retriever.similaritySearch(question);
// Or with more specific search parameters
SearchRequest request = SearchRequest.builder()
.query(question)
.topK(5) // Return top 5 results
.similarityThreshold(0.7) // Only return results with similarity score >= 0.7
.build();
List<Document> filteredDocuments = retriever.similaritySearch(request);
可以在 similaritySearch 方法中传入额外参数,用于定义要检索的文档数量以及相似性搜索的阈值。
读写操作分离
使用独立的接口可以让你清晰地定义哪些组件需要写入权限,哪些仅需读取权限:
// Write operations in a service that needs full access
@Service
class DocumentIndexer {
private final VectorStore vectorStore;
DocumentIndexer(VectorStore vectorStore) {
this.vectorStore = vectorStore;
}
public void indexDocuments(List<Document> documents) {
vectorStore.add(documents);
}
}
// Read-only operations in a service that only needs retrieval
@Service
class DocumentRetriever {
private final VectorStoreRetriever retriever;
DocumentRetriever(VectorStoreRetriever retriever) {
this.retriever = retriever;
}
public List<Document> findSimilar(String query) {
return retriever.similaritySearch(query);
}
}
这种关注点分离有助于创建更易于维护且更安全的应用程序,因为它将变异操作的访问权限限制在真正需要它们的组件内。
使用 VectorStoreRetriever 进行检索操作
VectorStoreRetriever 接口提供了一个向量存储的只读视图,仅公开相似性搜索功能。这遵循了最小权限原则,在 RAG(检索增强生成)应用中尤其有用,因为你只需要检索文档而无需修改底层数据。
使用 VectorStoreRetriever 的优势
-
关注点分离:清晰地区分读操作与写操作。
-
接口隔离:仅需检索功能的客户端不会接触到变更方法。
-
函数式接口:对于简单的用例,可以使用 lambda 表达式或方法引用来实现。
-
降低依赖:仅需执行搜索的组件无需依赖完整的
VectorStore接口。
示例用法
当你仅需执行相似性搜索时,可以直接使用 VectorStoreRetriever:
@Service
public class DocumentRetrievalService {
private final VectorStoreRetriever retriever;
public DocumentRetrievalService(VectorStoreRetriever retriever) {
this.retriever = retriever;
}
public List<Document> findSimilarDocuments(String query) {
return retriever.similaritySearch(query);
}
public List<Document> findSimilarDocumentsWithFilters(String query, String country) {
SearchRequest request = SearchRequest.builder()
.query(query)
.topK(5)
.filterExpression("country == '" + country + "'")
.build();
return retriever.similaritySearch(request);
}
}
在这个示例中,服务仅依赖于 VectorStoreRetriever 接口,这清楚地表明它仅执行检索操作,而不会修改向量存储。
与RAG应用程序的集成
VectorStoreRetriever接口在RAG应用中尤为有用,当需要检索相关文档以向AI模型提供上下文时:
@Service
public class RagService {
private final VectorStoreRetriever retriever;
private final ChatModel chatModel;
public RagService(VectorStoreRetriever retriever, ChatModel chatModel) {
this.retriever = retriever;
this.chatModel = chatModel;
}
public String generateResponse(String userQuery) {
// Retrieve relevant documents
List<Document> relevantDocs = retriever.similaritySearch(userQuery);
// Extract content from documents to use as context
String context = relevantDocs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
// Generate response using the retrieved context
String prompt = "Context information:\n" + context + "\n\nUser query: " + userQuery;
return chatModel.generate(prompt);
}
}
这种模式允许在RAG应用中,将检索组件与生成组件清晰地分离开来。
Metadata Filters
本节介绍了可用于筛选查询结果的各种过滤器。
过滤字符串
您可以将一个SQL风格的过滤表达式作为 String 传递给 similaritySearch 的一个重载方法。
考虑以下示例:
-
"country == 'BG'" -
"genre == 'drama' && year >= 2020" -
"genre in ['comedy', 'documentary', 'drama']"
Filter.Expression
您可以使用FilterExpressionBuilder来创建一个Filter.Expression实例,它提供了一种流式API。一个简单的示例如下:
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();
您还可以使用以下运算符:
IS: 'IS' | 'is';
NULL: 'NULL' | 'null';
NOT NULL: 'NOT NULL' | 'not null';
考虑以下示例:
Expression exp = b.and(b.isNull("year")).build();
Expression exp = b.and(b.isNotNull("year")).build();
IS NULL 和 IS NOT NULL 尚未在所有向量存储中实现。
从向量存储中删除文档
Vector Store接口提供了多种删除文档的方法,您既可以通过指定文档ID删除,也可以使用过滤表达式进行删除。
按文档ID删除
删除文档的最简单方法是提供文档ID列表:
void delete(List<String> idList);
该方法会删除所有ID与所提供列表匹配的文档。若列表中任意ID在存储中不存在,该ID将被忽略。
// Create and add document
Document document = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(document));
// Delete document by ID
vectorStore.delete(List.of(document.getId()));
按过滤表达式删除
对于更复杂的删除条件,你可以使用过滤器表达式:
void delete(Filter.Expression filterExpression);
该方法接受一个 Filter.Expression 对象,用于定义应删除哪些文档的条件。当需要根据文档的元数据属性进行删除操作时,这种方法特别有用。
// Create test documents with different metadata
Document bgDocument = new Document("The World is Big",
Map.of("country", "Bulgaria"));
Document nlDocument = new Document("The World is Big",
Map.of("country", "Netherlands"));
// Add documents to the store
vectorStore.add(List.of(bgDocument, nlDocument));
// Delete documents from Bulgaria using filter expression
Filter.Expression filterExpression = new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("country"),
new Filter.Value("Bulgaria")
);
vectorStore.delete(filterExpression);
// Verify deletion with search
SearchRequest request = SearchRequest.builder()
.query("World")
.filterExpression("country == 'Bulgaria'")
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will be empty as Bulgarian document was deleted
按字符串过滤表达式删除
为了方便,您也可以通过基于字符串的过滤表达式来删除文档:
void delete(String filterExpression);
此方法将提供的字符串过滤器在内部转换为 Filter.Expression 对象。当您拥有字符串格式的过滤条件时,此方法非常有用。
// Create and add documents
Document bgDocument = new Document("The World is Big",
Map.of("country", "Bulgaria"));
Document nlDocument = new Document("The World is Big",
Map.of("country", "Netherlands"));
vectorStore.add(List.of(bgDocument, nlDocument));
// Delete Bulgarian documents using string filter
vectorStore.delete("country == 'Bulgaria'");
// Verify remaining documents
SearchRequest request = SearchRequest.builder()
.query("World")
.topK(5)
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will only contain the Netherlands document
调用删除 API 时的错误处理
所有删除方法在发生错误时都可能抛出异常:
最佳实践是将删除操作包裹在try-catch块中:
try {
vectorStore.delete("country == 'Bulgaria'");
}
catch (Exception e) {
logger.error("Invalid filter expression", e);
}
文档版本控制用例
一个常见场景是管理文档版本,您需要上传文档的新版本,同时移除旧版本。以下是使用筛选表达式处理此情况的方法:
// Create initial document (v1) with version metadata
Document documentV1 = new Document(
"AI and Machine Learning Best Practices",
Map.of(
"docId", "AIML-001",
"version", "1.0",
"lastUpdated", "2024-01-01"
)
);
// Add v1 to the vector store
vectorStore.add(List.of(documentV1));
// Create updated version (v2) of the same document
Document documentV2 = new Document(
"AI and Machine Learning Best Practices - Updated",
Map.of(
"docId", "AIML-001",
"version", "2.0",
"lastUpdated", "2024-02-01"
)
);
// First, delete the old version using filter expression
Filter.Expression deleteOldVersion = new Filter.Expression(
Filter.ExpressionType.AND,
Arrays.asList(
new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("docId"),
new Filter.Value("AIML-001")
),
new Filter.Expression(
Filter.ExpressionType.EQ,
new Filter.Key("version"),
new Filter.Value("1.0")
)
)
);
vectorStore.delete(deleteOldVersion);
// Add the new version
vectorStore.add(List.of(documentV2));
// Verify only v2 exists
SearchRequest request = SearchRequest.builder()
.query("AI and Machine Learning")
.filterExpression("docId == 'AIML-001'")
.build();
List<Document> results = vectorStore.similaritySearch(request);
// results will contain only v2 of the document
你也可以使用字符串过滤器表达式实现相同的功能:
// Delete old version using string filter
vectorStore.delete("docId == 'AIML-001' AND version == '1.0'");
// Add new version
vectorStore.add(List.of(documentV2));
删除文档时的性能考虑
-
当确切知道要删除哪些文档时,按ID列表删除通常更快。
-
基于过滤器的删除可能需要扫描索引以查找匹配的文档;不过,这取决于向量存储的具体实现。
-
大规模删除操作应分批进行,以避免对系统造成过大压力。
-
如果基于文档属性进行删除,应考虑使用过滤表达式,而不是先收集ID。
理解向量
章节总结
📄️ Azure AI 服务
本节将引导您设置 AzureVectorStore,用于存储文档嵌入向量,并使用 Azure AI 搜索服务执行相似性搜索。
📄️ Azure Cosmos DB
本节将指导您设置 CosmosDBVectorStore,以存储文档嵌入并执行相似性搜索。
📄️ Apache Cassandra 向量存储
本节将指导您如何设置 CassandraVectorStore 以存储文档嵌入并执行相似性搜索。
📄️ Chroma
本节将指导您设置 Chroma VectorStore 以存储文档嵌入并执行相似性搜索。
📄️ Couchbase
本节将引导您完成设置 CouchbaseSearchVectorStore 的步骤,以存储文档嵌入向量,并使用 Couchbase 执行相似性搜索。
📄️ Elasticsearch
本节将指导您设置 Elasticsearch VectorStore 用于存储文档嵌入向量并执行相似性搜索。
📄️ GemFire
本节将引导您完成 GemFireVectorStore 的设置,用于存储文档嵌入向量并执行相似性搜索。
📄️ MariaDB 向量存储
本节将引导您设置 MariaDBVectorStore,用于存储文档嵌入并执行相似性搜索。
📄️ Milvus
Milvus 是一款开源的向量数据库,在数据科学和机器学习领域引起了广泛关注。其突出的特点之一在于其对向量索引和查询的强大支持。Milvus 采用了最先进、前沿的算法来加速搜索过程,使其在处理大规模数据集时,也能高效地检索相似向量。
📄️ MongoDB Atlas
本节将指导您如何设置MongoDB Atlas作为向量存储,以便在Spring AI中使用。
📄️ Neo4j
本节将引导您设置Neo4jVectorStore,以存储文档嵌入并执行相似性搜索。
📄️ OpenSearch
本节将引导您设置 OpenSearchVectorStore,以存储文档嵌入并执行相似性搜索。
📄️ Oracle
Oracle Database 23ai(23.4+)的 AI 向量搜索功能现可作为 Spring AI VectorStore 使用,帮助您存储文档嵌入并执行相似性搜索。当然,所有其他功能也均可使用。
📄️ PGvector
本节将指导您如何设置PGvector向量存储,用于存储文档嵌入并执行相似性搜索。
📄️ Pinecone
本节将引导您设置 Pinecone VectorStore,用于存储文档嵌入并执行相似性搜索。
📄️ Qdrant
本节将指导您设置 Qdrant 向量数据库,用于存储文档嵌入并执行相似性搜索。
📄️ Redis
本节将引导你设置 RedisVectorStore 来存储文档嵌入并执行相似性搜索。
📄️ SAP Hana
您需要一个 SAP HANA Cloud 向量引擎账户 - 请参考 SAP HANA Cloud 向量引擎 - 试用账户配置指南来创建试用账户。
📄️ Typesense
本部分将引导您完成 TypesenseVectorStore 的设置,以存储文档嵌入并执行相似性搜索。
📄️ Weaviate
本部分将指导您设置Weaviate VectorStore,以存储文档嵌入向量并执行相似性搜索。