向量操作
选择检索方 式
Elasticsearch 向量查询取决于服务端版本和字段 Mapping,不能只根据 API 方法名判断实际检索方式。
| 方式 | 实际行为 |
|---|---|
| ES 7 script_score | 通过脚本对匹配文档评分。 |
| ES 8.4+ _search knn | 对已建索引的 dense_vector 执行原生近似检索。 |
| Elastic7Dialect | 使用 script_score 评分并按 _score 降序返回,不是原生 knn。 |
| Elastic8Dialect | 生成顶层 knn,k=10、num_candidates=100;initPage 不会修改这两个值。 |
向量类型映射
ES 7 使用 dense_vector 并指定 dims,不使用下面 ES 8 的 index、similarity 选项。写入参数可用数值 List;展开结果中的向量字段也是数值 List,不是 JSON 文本。
ES 7.17 的 dense_vector 可以缺省,读取缺省字段得到 null;但不能显式写入或更新为 null。已有向量需要清空时,使用原生命令删除该字段,而不是把构造器更新值设为 null。
使用下方 ES 8.4+ kNN 示例前,先创建索引:
PUT /product_index
{
"mappings": {"properties": {
"category": {"type": "keyword"},
"embedding": {"type": "dense_vector", "dims": 3, "index": true, "similarity": "cosine"}
}}
}
索引的 similarity 决定检索度量;调用 orderByL2 不会把 cosine 索引改成 L2 索引。
限定向量候选范围
只检索 electronics 分类时,将条件放在 knn.filter 中,并将查询向量绑定为数值 List:
String command = """
POST /product_index/_search
{
"size": 10,
"knn": {
"field": "embedding",
"query_vector": ?,
"k": 10,
"num_candidates": 100,
"filter": {"term": {"category": ?}}
}
}
""";
List<Map<String, Object>> rows = jdbc.queryForList(
command, new Object[] { List.of(0.1f, 0.2f, 0.3f), "electronics" });
Elastic8Dialect 将构造器标量条件放在顶层 query,而不是 knn.filter。Elasticsearch 对 query 与 knn 按 OR 合并,因此 eq(...).orderByL2(...) 不是强制候选过滤。租户、分类等范围限制使用上面的命令。
KNN 近邻排序
ES 7 构造器生成 script_score,按 _score 降序返回,一次查询只支持一个向量评分,不是原生近似 kNN。
向量排序接入 L2、余弦和内积;Hamming、Jaccard 和 BM25 未接入这些构造器方法,不能用全文检索的 BM25 评分代替。
ES 7 的 embedding 字段声明 dense_vector、dims=3,不使用上面的 ES 8 index、similarity 选项。余弦评分使用 script_score:
String command = """
POST /product_index/_search
{
"size": 10,
"query": {"script_score": {
"query": {"term": {"category": ?}},
"script": {
"source": "cosineSimilarity(params.vec, 'embedding') + 1.0",
"params": {"vec": ?}
}
}}
}
""";
List<Map<String, Object>> rows = jdbc.queryForList(
command, new Object[] { "electronics", List.of(0.1f, 0.2f, 0.3f) });
匹配文档都应包含 embedding。脚本加 1 是为了得到非负分数,返回评分不是原始余弦相似度。