ES(ElasticSearch)-向量搜索

向量搜索

什么是向量搜索

​ 相对于传统的关键词搜索是在字典里精确查找单词,向量搜索则是在理解概念和语意。他的核心思想是:

1、将一切转化为数字(向量)

​ 利用人工智能模型(如 NLP 模型、图像模型)将文本、图片、音频等内容转换成一长串数字,这个数字序列称为向量(Vector)或嵌入(Embedding)。这个向量代表了原始内容的“语义”或“特征”。

​ 例如,“国王”、“王后”、“男人”、“女人”这些词,通过 AI 模型转换后,它们的向量在数学关系上会体现出 国王 - 男人 + 女人 ≈ 王后

2、在向量空间中搜索

​ 所有这些向量构成一个高维的“向量空间”。语义相近的内容,它们的向量在空间中的位置也更接近。

  • “苹果”、“香蕉”、“橘子”的向量会聚集在“水果”区域。
  • “苹果公司”、“iPhone”、“库克”的向量会聚集在“科技公司”区域。

3、相似度计算

​ 向量搜索就是计算一个“查询向量”和数据库中所有“文档向量”之间的距离相似度,并返回最相似(即距离最近)的 TOP K 个结果。

向量搜索 VS 传统搜索

  • 传统搜索:“找到所有含有‘车’字的文档。”
  • 向量搜索:“找到所有关于‘交通工具’的文档。”(即包括含有“汽车”、“自行车”、“飞机”、“轮船”的文档,因为它们语义相近,即使它们不包含“车”字)

向量搜索的巨大优势在于解决了传统搜索的痛点:

  • 同义词:搜索“单车”,也能找到“自行车”的结果。
  • 语义泛化:搜索“宠物医疗”,也能找到关于“猫咪绝育注意事项”或“狗粮健康营养”的文章。
  • 跨模态搜索:用一张狗的图片,搜索到关于“犬类”的文本描述。

ES如何存储向量

dense_vector:Elasticsearch的字段类型 dense_vector 可以存储向量

1、定义映射 (Mapping)

在创建索引时,必须显式地定义一个或多个 dense_vector 类型的字段,并指定其维度

维度 (dimensions):这是向量的长度(数字的个数)。它必须与你使用的 AI 模型生成的向量维度一致。常见的模型维度有 384、768、1024 等。

创建一个用于存储商品标题和其对应向量的索引。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
PUT /product_vector_index
{
"mappings": {
"properties": {
"product_name": { // 存储原始文本
"type": "text"
},
"product_name_embedding": { // 存储文本对应的向量
"type": "dense_vector",
"dims": 384, // 非常重要!必须与你的模型输出维度匹配,这里假设我们用的模型输出384维
"index": true, // ES 8.0+ 重要特性:设置为 true 以启用高效的近似最近邻 (ANN) 搜索索引
"similarity": "cosine" // 指定相似度算法,可选 l2_norm, dot_product, cosine
}
}
}
}

2、写入数据

1、向量化:搜索内容转化为向量值

​ 先使用外部 AI 模型(如 Hugging Face 的 sentence-transformers 库、OpenAI 的 Embeddings API)生成文本/图片/音频/视频的向量,这一步通常在ES之外完成。

2、写入ES索引的dense_vector 的字段

​ 假设我们已用模型生成好 “Wireless Bluetooth Headphones” 这个标题的向量,它是一个包含384个浮点数的数组

1
2
3
4
5
POST /product_vector_index/_doc/1
{
"product_name": "Wireless Bluetooth Headphones",
"product_name_embedding": [0.125, -0.056, 0.987, -0.342, 0.418, ...] // 此处应是384个浮点数
}

3、向量查询

1、纯向量查询

使用专门的 knn 选项,直接提供一个查询向量进行搜索。

场景:用户输入一段文本,你想找到语义上最相似的商品。

  1. 第一步:用同样的 AI 模型将用户的查询文本(如 “Headphones for phone”)转换为一个 384 维的查询向量。
  2. 第二步:在 ES 中执行 knn 查询。
1
2
3
4
5
6
7
8
9
10
GET /product_vector_index/_search
{
"knn": {
"field": "product_name_embedding", // 指定要搜索的向量字段
"query_vector": [0.056, -0.234, 0.874, -0.123, 0.543, ...], // 第一步生成的查询向量(384维)
"k": 5, // 返回最相似的 5 个结果
"num_candidates": 100 // 从每个分片中选取的候选数量,越大越精确但越慢
},
"_source": ["product_name"] // 指定返回哪些原始字段,这里我们只关心商品名
}

返回结果:ES 会返回与 "Headphones for phone" 语义最相似的 5 个商品,例如:

  1. “Wireless Bluetooth Headphones” (最相关)
  2. “Noise-Cancelling Earbuds” (也很相关,都是耳机)
  3. “Smartphone USB-C Audio Adapter” (相关,因为涉及手机音频)

即使这些文档里完全没有出现查询中的 “for”、”phone” 等单词。

2、混合查询

将传统的基于关键词的搜索 (query) 和现代的向量搜索 (knn) 组合在同一个请求中

场景:用户输入 “高质量的索尼耳机”,你既想匹配到关键词“索尼”,又想找到所有语义上关于“高质量耳机”的产品。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
GET /product_vector_index/_search
{
"query": {
"match": {
"product_name": "索尼" // 传统关键词查询:必须包含“索尼”
}
},
"knn": {
"field": "product_name_embedding",
"query_vector": [0.056, -0.234, 0.874, ...], // “高质量的索尼耳机”生成的向量
"k": 50, // 可以设置大一些
"num_candidates": 100
},
"rank": { // 告诉 ES 如何融合两种搜索的分数
"rrf": { // 使用倒数排名融合(RRF)算法,这是一种高效且常用的融合方式
}
},
"_source": ["product_name"]
}

这个查询会:

  1. **关键词查询 (query)**:先找出所有商品名中包含“索尼”的文档(例如 20 个)。
  2. **向量查询 (knn)**:同时找出与“高质量的索尼耳机”语义最相似的 50 个文档。
  3. **结果融合 (rrf)**:ES 的 RRF 算法会综合考虑每个文档在两次搜索中的排名,计算出一个最终的综合排名。
  4. 返回结果:最终返回的可能是
    • 既包含“索尼”又语义高度相关的(排名第一,例如 “Sony WH-1000XM5 Wireless Noise-Cancelling Headphones”)。
    • 包含“索尼”但语义不太相关的(排名靠后)。
    • 不包含“索尼”但语义极其相关的其他品牌高端耳机(排名中等)。

这种方式结合了两种搜索的优势:关键词的精确性和语义的智能性,能提供最相关、最令人惊喜的搜索结果。