主题
OceanBase
OceanBase 嵌入存储与 OceanBase 数据库集成,提供向量相似度搜索与混合搜索能力。
Maven 依赖
xml
<dependency>
<groupId>dev.langchain4j</groupId>
<artifactId>langchain4j-community-oceanbase</artifactId>
<version>${latest version here}</version>
</dependency>1
2
3
4
5
2
3
4
5
注意:这是一个社区集成模块。你可能需要在项目配置中添加 langchain4j-community 仓库。
API
OceanBaseEmbeddingStore
要求
- OceanBase 数据库实例(4.3.5 或更新版本)
- Java >= 17
特性
- 存储带元数据(JSON 格式)的嵌入
- 使用 cosine、L2 或内积距离的向量相似度搜索
- 混合搜索:结合向量相似度与全文搜索(RRF 算法)
- 按元数据字段与表列过滤搜索结果
- 自动创建表与向量索引
- 可自定义字段名与距离度量
用量
基本示例
java
import dev.langchain4j.data.document.Metadata;
import dev.langchain4j.data.embedding.Embedding;
import dev.langchain4j.data.segment.TextSegment;
import dev.langchain4j.model.embedding.EmbeddingModel;
import dev.langchain4j.model.embedding.onnx.allminilml6v2q.AllMiniLmL6V2QuantizedEmbeddingModel;
import dev.langchain4j.store.embedding.EmbeddingSearchRequest;
import dev.langchain4j.store.embedding.EmbeddingSearchResult;
import dev.langchain4j.store.embedding.oceanbase.OceanBaseEmbeddingStore;
// Initialize embedding model
EmbeddingModel embeddingModel = new AllMiniLmL6V2QuantizedEmbeddingModel();
// Create embedding store
OceanBaseEmbeddingStore embeddingStore = OceanBaseEmbeddingStore.builder()
.url("jdbc:oceanbase://127.0.0.1:2881/test")
.user("root@test")
.password("password")
.tableName("embeddings")
.dimension(384)
.build();
// Add document with metadata
String id = embeddingStore.add(
embeddingModel.embed("Java is a programming language").content(),
TextSegment.from("Java is a programming language",
Metadata.from("category", "programming").put("language", "Java"))
);
// Search
Embedding queryEmbedding = embeddingModel.embed("programming language").content();
EmbeddingSearchResult<TextSegment> results = embeddingStore.search(
EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.maxResults(10)
.build()
);
// Process results
results.matches().forEach(match -> {
System.out.println("Score: " + match.score());
System.out.println("Text: " + match.embedded().text());
System.out.println("Metadata: " + match.embedded().metadata());
});1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
高级配置
java
OceanBaseEmbeddingStore embeddingStore = OceanBaseEmbeddingStore.builder()
.url("jdbc:oceanbase://127.0.0.1:2881/test")
.user("root@test")
.password("password")
.tableName("embeddings")
.dimension(384)
.metricType("cosine") // Options: "cosine", "l2", "ip"
.retrieveEmbeddingsOnSearch(true)
.idFieldName("id_field")
.textFieldName("text_field")
.metadataFieldName("metadata_field")
.vectorFieldName("vector_field")
.build();1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
距离度量
OceanBase 嵌入存储支持三种距离度量。距离值会被自动转换为 [0, 1] 区间内的相关性分数,其中 1 表示最相关的匹配。
余弦距离(默认)- "cosine"
最适合: 文本嵌入、语义相似度搜索
工作原理:
- OceanBase
cosine_distance返回值范围在 [0, 2]0= 相同向量(方向一致)1= 正交向量(互相垂直)2= 相反向量(方向完全相反)
- 转换为相关性分数:
score = (2 - distance) / 2 - 结果与向量的模长无关
java
.metricType("cosine") // Default, recommended for text embeddings1
L2 距离(欧氏)- "l2" 或 "euclidean"
最适合: 当方向与模长都很重要时
工作原理:
- 衡量向量之间的直线距离
- 范围:[0, ∞)
- 转换为相关性分数:
score = 1 / (1 + distance)
java
.metricType("l2") // or "euclidean"1
内积 - "inner_product" 或 "ip"
最适合: 归一化嵌入、性能至关重要的应用
工作原理:
- 衡量向量的点积
- 对归一化向量而言,范围是 [-1, 1]
- 转换为相关性分数:
score = (inner_product + 1) / 2
java
.metricType("inner_product") // or "ip"1
参考: OceanBase 向量距离函数
过滤
OceanBase 嵌入存储支持按元数据字段与表列过滤搜索结果。
按元数据字段过滤
java
import dev.langchain4j.store.embedding.filter.MetadataFilterBuilder;
import static dev.langchain4j.store.embedding.filter.MetadataFilterBuilder.metadataKey;
// Filter by single metadata field
Filter filter = metadataKey("category").isEqualTo("programming");
// Filter with multiple conditions
Filter filter = new And(
metadataKey("category").isEqualTo("programming"),
metadataKey("language").isEqualTo("Java")
);
// Filter with IN operator
Filter filter = metadataKey("language").isIn("Java", "Python", "C++");
// Search with filter
EmbeddingSearchResult<TextSegment> results = embeddingStore.search(
EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding)
.filter(filter)
.maxResults(10)
.build()
);1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
按表列过滤
你也可以直接按表列过滤(id、text、metadata、vector):
java
import dev.langchain4j.store.embedding.filter.comparison.IsIn;
import dev.langchain4j.store.embedding.filter.comparison.ContainsString;
import dev.langchain4j.store.embedding.filter.comparison.IsEqualTo;
// Filter by ID field
Filter filter = new IsIn("id", List.of("id1", "id2", "id3"));
// Filter by text field (contains)
Filter textFilter = new ContainsString("text", "programming");
// Filter by exact text match
Filter exactTextFilter = new IsEqualTo("text", "Java programming");1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
注:按表列过滤时,请使用你在 FieldDefinition 中定义的实际字段名。映射器会自动识别常用别名:
id→ id 字段text或document→ text 字段metadata→ metadata 字段vector或embedding→ vector 字段
支持的过滤操作
isEqualTo:相等比较isNotEqualTo:不相等比较isGreaterThan:大于比较isGreaterThanOrEqualTo:大于等于比较isLessThan:小于比较isLessThanOrEqualTo:小于等于比较isIn:IN 运算(多值)isNotIn:NOT IN 运算containsString:LIKE 运算(模式匹配)And:逻辑与Or:逻辑或Not:逻辑非
混合搜索
混合搜索结合向量相似度搜索与全文搜索,以提供更好的搜索结果。启用后,它会在 text 字段上自动创建一个全文索引,并用 倒数排名融合(RRF) 算法合并结果。
启用混合搜索
java
OceanBaseEmbeddingStore embeddingStore = OceanBaseEmbeddingStore.builder()
.url("jdbc:oceanbase://127.0.0.1:2881/test")
.user("root@test")
.password("password")
.tableName("embeddings")
.dimension(384)
.enableHybridSearch(true) // Enable hybrid search
.build();1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
执行混合搜索
java
// Perform hybrid search by providing both query embedding and query text
EmbeddingSearchResult<TextSegment> results = embeddingStore.search(
EmbeddingSearchRequest.builder()
.queryEmbedding(queryEmbedding) // Vector embedding for similarity search
.query("search text") // Text query for fulltext search
.maxResults(10)
.build()
);1
2
3
4
5
6
7
8
2
3
4
5
6
7
8
混合搜索的工作原理
- 向量搜索:使用查询嵌入执行相似度搜索
- 全文搜索:在 text 字段上使用
MATCH AGAINST执行全文搜索 - 结果融合:用 RRF(倒数排名融合)算法合并结果
- 公式:
score = Σ(1 / (k + rank)),其中 k=60 - 来自两种搜索的每个结果都依据其排名对最终分数作出贡献
- 结果被归一化并按组合后的 RRF 分数排序
- 公式:
优势:
- 更好的召回:既能按语义相似度、也能按精确关键词找到文档
- 更高的精度:RRF 有效地平衡了两种搜索
- 比单独的向量搜索更好地处理精确关键词匹配
实现细节
分数计算
该嵌入存储直接在 SQL 查询中计算相关性分数:
- Cosine:
score = (2 - cosine_distance) / 2 - L2/欧氏:
score = 1 / (1 + distance) - 内积:
score = (inner_product + 1) / 2
分数在 [0, 1] 区间内返回,其中 1 表示最相关的匹配。
元数据处理
- 元数据以 JSON 形式存储在数据库中
- 较大的
Long值(> 2^53-1)会被自动序列化为字符串以保留精度 - 过滤同时支持直接的列过滤与 JSON 元数据过滤
表结构
默认情况下,嵌入表会有以下列:
| 名称 | 类型 | 描述 |
|---|---|---|
| id | VARCHAR(36) | 主键。用于存储 UUID 字符串,这些字符串在嵌入存储时生成 |
| vector | JSON | 以 JSON 数组存储嵌入向量 |
| text | TEXT | 存储文本片段 |
| metadata | JSON | 以 JSON 存储元数据 |
限制
removeAll(Filter)与removeAll()方法尚不支持。请改用removeAll(Collection<String> ids)。- 按表列过滤时,字段名不区分大小写,但必须与实际列名或可识别的别名相符。