主题
Apache Cassandra 向量库
本节引导你设置 CassandraVectorStore 以存储文档嵌入并执行相似度搜索。
什么是 Apache Cassandra?
Apache Cassandra® 是一个真正的开源分布式数据库,以线性可扩展性、久经验证的容错性和低延迟著称,是承载关键任务事务数据的理想平台。
它的向量相似度搜索(VSS)基于 JVector 库,确保一流的Performance与相关性。
在 Apache Cassandra 中,一次向量搜索可以做得极其简单:
sql
SELECT content FROM table ORDER BY content_vector ANN OF query_embedding;更多相关文档可在此处阅读。
这个 Spring AI 向量库既能用于全新的 RAG 应用,也能 retrofit 到既有的数据和表之上。
该存储也可用于既有数据库中的非 RAG 场景,例如语义搜索、地理邻近搜索等。
该存储会根据其配置自动创建或增强模式。如果你不希望有模式改动,请用 initializeSchema 配置该存储。
使用 spring-boot-autoconfigure 时,依照 Spring Boot 惯例 initializeSchema 默认为 false,你必须显式开启模式创建/修改,方法是在 application.properties 文件中设置 ...initialize-schema=true。
什么是 JVector?
JVector 是一个纯 Java 的嵌入式向量搜索引擎。
它凭借以下特点,从其他 HNSW 向量相似度搜索实现中脱颖而出:
- 算法上快。JVector 采用受 DiskANN 及相关研究启发的尖端图算法,兼顾高召回与低延迟。
- 实现上快。JVector 使用 Panama SIMD API 加速索引构建与查询。
- 内存高效。JVector 用乘积量化压缩向量,使其在搜索期间能常驻内存。
- 磁盘感知。JVector 的磁盘布局旨在查询时把必要的 iops 降到最低。
- 并发。索引构建可线性扩展到至少 32 线程。线程翻倍,构建时间减半。
- 增量。索引边建边查。添加一个向量与能在搜索结果中找到它之间没有延迟。
前置条件
- 一个用于计算文档嵌入的
EmbeddingModel实例。它通常被配置为一个 Spring Bean。有以下可选方案:
Transformers Embedding- 在你的本地环境中计算嵌入。默认经由 ONNX 和 all-MiniLM-L6-v2 Sentence Transformers。开箱即用。- 若你想使用 OpenAI 的 Embeddings - 使用 OpenAI 的嵌入端点。你需要在 OpenAI 注册 创建账户,并在 API Keys 生成 api-key 令牌。
- 还有更多选择,参见
Embeddings API文档。
依赖
NOTE
Spring AI 的自动配置、starter 模块的制品名称发生了重大变更。 更多信息请参阅 升级说明。
TIP
对于依赖管理,我们建议使用 Spring AI BOM,如 依赖管理 一节所述。
把这些依赖添加到你的项目:
- 只要 Cassandra 向量库:
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-cassandra-store</artifactId>
</dependency>- 或者,一个 RAG 应用所需的一切(使用默认的 ONNX Embedding Model):
xml
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-vector-store-cassandra</artifactId>
</dependency>配置属性
你可以在 Spring Boot 配置中使用以下属性来定制 Apache Cassandra 向量库。
| 属性 | 默认值 |
|---|---|
spring.ai.vectorstore.cassandra.keyspace | springframework |
spring.ai.vectorstore.cassandra.table | aivectorstore |
spring.ai.vectorstore.cassandra.initialize-schema | false |
spring.ai.vectorstore.cassandra.index-name | spring.ai.vectorstore.cassandra.content-column-name |
| content | spring.ai.vectorstore.cassandra.embedding-column-name |
| embedding | spring.ai.vectorstore.cassandra.fixed-thread-pool-executor-size |
| 16 |
用量
基本用法
把 CassandraVectorStore 实例作为一个 Spring Bean 创建:
java
@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
return CassandraVectorStore.builder(embeddingModel)
.session(session)
.keyspace("my_keyspace")
.table("my_vectors")
.build();
}一旦有了向量库实例,你就可以添加文档并执行搜索:
java
// Add documents
vectorStore.add(List.of(
new Document("1", "content1", Map.of("key1", "value1")),
new Document("2", "content2", Map.of("key2", "value2"))
));
// Search with filters
List<Document> results = vectorStore.similaritySearch(
SearchRequest.builder().query("search text")
.topK(5)
.similarityThreshold(0.7)
.filterExpression("metadata.key1 == 'value1'")
.build()
);高级配置
对于更复杂的用例,你可以在 Spring Bean 中配置更多设置:
java
@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
return CassandraVectorStore.builder(embeddingModel)
.session(session)
.keyspace("my_keyspace")
.table("my_vectors")
// Configure primary keys
.partitionKeys(List.of(
new SchemaColumn("id", DataTypes.TEXT),
new SchemaColumn("category", DataTypes.TEXT)
))
.clusteringKeys(List.of(
new SchemaColumn("timestamp", DataTypes.TIMESTAMP)
))
// Add metadata columns with optional indexing
.addMetadataColumns(
new SchemaColumn("category", DataTypes.TEXT, SchemaColumnTags.INDEXED),
new SchemaColumn("score", DataTypes.DOUBLE)
)
// Customize column names
.contentColumnName("text")
.embeddingColumnName("vector")
// Performance tuning
.fixedThreadPoolExecutorSize(32)
// Schema management
.initializeSchema(true)
// Custom batching strategy
.batchingStrategy(new TokenCountBatchingStrategy())
.build();
}连接配置
有两种方式配置到 Cassandra 的连接:
- 使用注入的 CqlSession(推荐):
java
@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
return CassandraVectorStore.builder(embeddingModel)
.session(session)
.keyspace("my_keyspace")
.table("my_vectors")
.build();
}- 在构建器中直接使用连接详情:
java
@Bean
public VectorStore vectorStore(EmbeddingModel embeddingModel) {
return CassandraVectorStore.builder(embeddingModel)
.contactPoint(new InetSocketAddress("localhost", 9042))
.localDatacenter("datacenter1")
.keyspace("my_keyspace")
.build();
}元数据过滤
你可以在 CassandraVectorStore 上利用通用、可移植的元数据过滤器。要让元数据列可被搜索,它们必须是主键或 SAI 索引列。要把非主键列设为索引,请用 SchemaColumnTags.INDEXED 配置该元数据列。
例如,你可以使用文本表达式语言:
java
vectorStore.similaritySearch(
SearchRequest.builder().query("The World")
.topK(5)
.filterExpression("country in ['UK', 'NL'] && year >= 2020").build());或以编程方式使用表达式 DSL:
java
Filter.Expression f = new FilterExpressionBuilder()
.and(
f.in("country", "UK", "NL"),
f.gte("year", 2020)
).build();
vectorStore.similaritySearch(
SearchRequest.builder().query("The World")
.topK(5)
.filterExpression(f).build());这些可移植的过滤表达式会被自动转换为 CQL 查询。
高级示例:在 Wikipedia 数据集之上构建向量库
下面的示例演示如何在一个既有模式上使用该存储。这里我们使用来自 https://github.com/datastax-labs/colbert-wikipedia-data 项目的模式,它自带已经完成向量化的完整 wikipedia 数据集。
首先,在 Cassandra 数据库中创建模式:
bash
wget https://s.apache.org/colbert-wikipedia-schema-cql -O colbert-wikipedia-schema.cql
cqlsh -f colbert-wikipedia-schema.cql然后使用构建器模式配置该存储:
java
@Bean
public VectorStore vectorStore(CqlSession session, EmbeddingModel embeddingModel) {
List<SchemaColumn> partitionColumns = List.of(
new SchemaColumn("wiki", DataTypes.TEXT),
new SchemaColumn("language", DataTypes.TEXT),
new SchemaColumn("title", DataTypes.TEXT)
);
List<SchemaColumn> clusteringColumns = List.of(
new SchemaColumn("chunk_no", DataTypes.INT),
new SchemaColumn("bert_embedding_no", DataTypes.INT)
);
List<SchemaColumn> extraColumns = List.of(
new SchemaColumn("revision", DataTypes.INT),
new SchemaColumn("id", DataTypes.INT)
);
return CassandraVectorStore.builder()
.session(session)
.embeddingModel(embeddingModel)
.keyspace("wikidata")
.table("articles")
.partitionKeys(partitionColumns)
.clusteringKeys(clusteringColumns)
.contentColumnName("body")
.embeddingColumnName("all_minilm_l6_v2_embedding")
.indexName("all_minilm_l6_v2_ann")
.initializeSchema(false)
.addMetadataColumns(extraColumns)
.primaryKeyTranslator((List<Object> primaryKeys) -> {
if (primaryKeys.isEmpty()) {
return "test§¶0";
}
return String.format("%s§¶%s", primaryKeys.get(2), primaryKeys.get(3));
})
.documentIdTranslator((id) -> {
String[] parts = id.split("§¶");
String title = parts[0];
int chunk_no = parts.length > 1 ? Integer.parseInt(parts[1]) : 0;
return List.of("simplewiki", "en", title, chunk_no, 0);
})
.build();
}
@Bean
public EmbeddingModel embeddingModel() {
// default is ONNX all-MiniLM-L6-v2 which is what we want
return new TransformersEmbeddingModel();
}加载完整的 Wikipedia 数据集
要加载完整的 wikipedia 数据集:
从 https://s.apache.org/simplewiki-sstable-tar 下载
simplewiki-sstable.tar(这会花较长时间,文件有数十 GB)加载数据:
bash
tar -xf simplewiki-sstable.tar -C ${CASSANDRA_DATA}/data/wikidata/articles-*/
nodetool import wikidata articles ${CASSANDRA_DATA}/data/wikidata/articles-*/NOTE
- 如果该表中已有数据,在执行
tar时请检查 tarball 的文件不会覆盖既有的 sstable。 nodetool import的一个替代方案就是直接重启 Cassandra。- 如果索引中有任何失败,它们会被自动重建。
访问原生客户端
Cassandra 向量库实现通过 getNativeClient() 方法提供对底层原生 Cassandra 客户端(CqlSession)的访问:
java
CassandraVectorStore vectorStore = context.getBean(CassandraVectorStore.class);
Optional<CqlSession> nativeClient = vectorStore.getNativeClient();
if (nativeClient.isPresent()) {
CqlSession session = nativeClient.get();
// Use the native client for Cassandra-specific operations
}原生客户端让你得以访问那些可能未通过 VectorStore 接口暴露的 Cassandra 特定功能与操作。