1 前言
Elasticsearch 的 向量搜索(Vector Search) 是近年来为了支持语义搜索、推荐系统、图像搜索等 AI 场景而引入的重要功能。
1.1 什么是向量搜索?🧠
传统的关键字搜索匹配的是词语或短语,而 向量搜索(Vector Search) 匹配的是“语义相似性”。
举个例子:
用户搜索:如何提高网站性能?
- 传统搜索:查找包含“提高"和”性能“的文档。
- 向量搜索:找语义上与“网站优化”、“前端加速”、“服务器响应时间”相似的文章。
1.2 从哪个版本开始支持?🕰️
功能 |
支持版本 |
说明 |
dense_vector 字段 |
7.3+ |
支持存储向量,但无法建立索引或排序,仅能作为过滤字段使用。 |
dense_vector 支持排序和脚本评分 |
7.5+ |
可用 script_score 实现向量相似度排序(但效率较低)。 |
dense_vector + knn 支持向量索引(ANN) |
8.0+ |
正式支持原生 KNN 近似向量搜索(使用 HNSW 算法)。这是推荐使用的方式! |
text_embedding 管道字段 |
8.11+ |
内置支持向量生成(通过 Elastic Learned Sparse Encoder 模型)。 |
1.3 与其他工具对比
功能 |
Elasticsearch 8 |
FAISS |
Milvus |
Weaviate |
原生搜索 + 向量索引 |
✅ |
❌(需外接 ES) |
❌ |
❌ |
支持多字段查询 |
✅ |
❌ |
❌ |
❌ |
HNSW 向量搜索 |
✅ |
✅ |
✅ |
✅ |
自带嵌入模型(E5/ELSE) |
✅ |
❌ |
❌ |
✅(部分) |
1.4 许可说明
Elasticsearch 的原生向量索引(index: true
)功能属于 Elastic License,不是 Apache 2.0 免费许可。
如果你用的是 OpenSearch(ES 7.10 分支开源版本),其向量功能和支持程度也不同。
2 核心特性(以 Elasticsearch 8.x 为主)
2.1 dense_vector
字段类型
"content_vector": {
"type": "dense_vector",
"dims": 384,
"index": true,
"similarity": "cosine"
}
dims
:向量维度(必须与实际向量一致)。
index: true
:开启 ANN 索引(必须)。
similarity
:支持 cosine
, dot_product
, l2_norm
。
2.2 KNN 向量搜索(ANN 索引)
Elasticsearch 8.x 使用 HNSW(Hierarchical Navigable Small World)算法,允许高效的近似向量搜索(KNN)
"knn": {
"field": "content_vector",
"query_vector": [...],
"k": 3,
"num_candidates": 100
}
k
: 返回最相似的前 k
条记录。
num_candidates
: 从索引中选取的候选数量,越大越精确但慢。
2.3 支持模型推理(从 8.11 开始)
Elasticsearch 8.11+ 引入了内置的 text_embedding
管道处理器,允许你直接将文本变成向量(使用自带模型,无需 Python 推理):
{
"text_embedding": {
"field_map": {
"content": "content_vector"
},
"model_id": ".elser_model_2"
}
}
3 dense_vector 案例
安装依赖
sentence-transformers==4.1.0
huggingface_hub[hf_xet]
elasticsearch==8.13.0
3.1 创建索引
from elasticsearch import Elasticsearch
es = Elasticsearch(
"https://***.***.***.***:9200/",
basic_auth=("elastic", "***"),
verify_certs=False
)
index_name = "my_blog_article"
if es.indices.exists(index=index_name):
es.indices.delete(index=index_name)
mapping = {
"mappings": {
"properties": {
"title": {"type": "text"},
"content": {"type": "text"},
"content_vector": {
"type": "dense_vector",
"dims": 512,
"index": True,
"similarity": "cosine"
}
}
}
}
es.indices.create(index=index_name, body=mapping)
3.2 插入博客文章(文本 + 向量)
from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch
es = Elasticsearch(
"https://***.***.***.***:9200/",
basic_auth=("elastic", "***"),
verify_certs=False
)
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
articles = [
{"title": "Elasticsearch 简介", "content": "Elasticsearch 是一个基于 Lucene 的搜索引擎,适用于全文检索。"},
{"title": "机器学习基础", "content": "本文介绍了机器学习中的监督学习和非监督学习方法。"},
{"title": "如何使用 Python 操作 Elasticsearch", "content": "作者:有勇气的牛排, 通过 Python 的 elasticsearch 库,可以方便地查询和插入数据。"}
]
index_name = "my_blog_article"
for i, article in enumerate(articles):
vector = model.encode(article["content"]).tolist()
print(article)
print(vector)
doc = {
"title": article["title"],
"content": article["content"],
"content_vector": vector
}
es.index(index=index_name, id=i + 1, document=doc)

3.3 向量查询相似博客
from sentence_transformers import SentenceTransformer
from elasticsearch import Elasticsearch
es = Elasticsearch(
"https://***.***.***.***:9200/",
basic_auth=("elastic", "***"),
verify_certs=False
)
index_name = "my_blog_article"
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")
query_text = "有勇气的牛排"
query_vector = model.encode(query_text).tolist()
query_body = {
"knn": {
"field": "content_vector",
"query_vector": query_vector,
"k": 2,
"num_candidates": 10
},
"_source": ["title", "content"]
}
response = es.search(index=index_name, body=query_body)
for hit in response["hits"]["hits"]:
print(f"得分: {hit['_score']:.4f}")
print(f"标题: {hit['_source']['title']}")
print(f"内容: {hit['_source']['content']}\n")
输出
得分: 0.8303
标题: 如何使用 Python 操作 Elasticsearch
内容: 作者有勇气的牛排, 通过 Python 的 elasticsearch 库,可以方便地查询和插入数据。
得分: 0.6260
标题: Elasticsearch 简介
内容: Elasticsearch 是一个基于 Lucene 的搜索引擎,适用于全文检索。

<h2><a id="1__0"></a>1 前言</h2>
<p>Elasticsearch 的 <strong>向量搜索(Vector Search)</strong> 是近年来为了支持语义搜索、推荐系统、图像搜索等 AI 场景而引入的重要功能。</p>
<h3><a id="11__4"></a>1.1 什么是向量搜索?🧠</h3>
<p>传统的关键字搜索匹配的是词语或短语,而 <strong>向量搜索(Vector Search)</strong> 匹配的是“语义相似性”。</p>
<p><strong>举个例子:</strong></p>
<p>用户搜索:如何提高网站性能?</p>
<ul>
<li>传统搜索:查找包含“提高"和”性能“的文档。</li>
<li>向量搜索:找语义上与“网站优化”、“前端加速”、“服务器响应时间”相似的文章。</li>
</ul>
<h3><a id="12__15"></a>1.2 从哪个版本开始支持?🕰️</h3>
<table>
<thead>
<tr>
<th>功能</th>
<th>支持版本</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td><code>dense_vector</code> 字段</td>
<td><strong>7.3+</strong></td>
<td>支持存储向量,但无法建立索引或排序,仅能作为过滤字段使用。</td>
</tr>
<tr>
<td><code>dense_vector</code> 支持排序和脚本评分</td>
<td><strong>7.5+</strong></td>
<td>可用 <code>script_score</code> 实现向量相似度排序(但效率较低)。</td>
</tr>
<tr>
<td><code>dense_vector</code> + <code>knn</code> 支持向量索引(ANN)</td>
<td><strong>8.0+</strong></td>
<td>正式支持原生 KNN 近似向量搜索(使用 HNSW 算法)。这是推荐使用的方式!</td>
</tr>
<tr>
<td><code>text_embedding</code> 管道字段</td>
<td><strong>8.11+</strong></td>
<td>内置支持向量生成(通过 Elastic Learned Sparse Encoder 模型)。</td>
</tr>
</tbody>
</table>
<h3><a id="13__24"></a>1.3 与其他工具对比</h3>
<table>
<thead>
<tr>
<th>功能</th>
<th>Elasticsearch 8</th>
<th>FAISS</th>
<th>Milvus</th>
<th>Weaviate</th>
</tr>
</thead>
<tbody>
<tr>
<td>原生搜索 + 向量索引</td>
<td>✅</td>
<td>❌(需外接 ES)</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>支持多字段查询</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
<td>❌</td>
</tr>
<tr>
<td>HNSW 向量搜索</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
<td>✅</td>
</tr>
<tr>
<td>自带嵌入模型(E5/ELSE)</td>
<td>✅</td>
<td>❌</td>
<td>❌</td>
<td>✅(部分)</td>
</tr>
</tbody>
</table>
<h3><a id="14__33"></a>1.4 许可说明</h3>
<p>Elasticsearch 的原生向量索引(<code>index: true</code>)功能属于 <strong><a href="https://www.elastic.co/licensing" target="_blank">Elastic License</a></strong>,不是 Apache 2.0 免费许可。</p>
<p>如果你用的是 <strong>OpenSearch</strong>(ES 7.10 分支开源版本),其向量功能和支持程度也不同。</p>
<h2><a id="2__Elasticsearch_8x__39"></a>2 核心特性(以 Elasticsearch 8.x 为主)</h2>
<h3><a id="21_dense_vector__41"></a>2.1 <code>dense_vector</code> 字段类型</h3>
<pre><div class="hljs"><code class="lang-json"><span class="hljs-attr">"content_vector"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"type"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"dense_vector"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"dims"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">384</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"index"</span><span class="hljs-punctuation">:</span> <span class="hljs-keyword">true</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"similarity"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"cosine"</span>
<span class="hljs-punctuation">}</span>
</code></div></pre>
<p><code>dims</code>:向量维度(必须与实际向量一致)。</p>
<p><code>index: true</code>:开启 ANN 索引(必须)。</p>
<p><code>similarity</code>:支持 <code>cosine</code>, <code>dot_product</code>, <code>l2_norm</code>。</p>
<h3><a id="22_KNN_ANN__58"></a>2.2 KNN 向量搜索(ANN 索引)</h3>
<p>Elasticsearch 8.x 使用 HNSW(Hierarchical Navigable Small World)算法,允许高效的近似向量搜索(KNN)</p>
<pre><div class="hljs"><code class="lang-json"><span class="hljs-attr">"knn"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"field"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"content_vector"</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"query_vector"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span>...<span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"k"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">3</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"num_candidates"</span><span class="hljs-punctuation">:</span> <span class="hljs-number">100</span>
<span class="hljs-punctuation">}</span>
</code></div></pre>
<p><code>k</code>: 返回最相似的前 <code>k</code> 条记录。</p>
<p><code>num_candidates</code>: 从索引中选取的候选数量,越大越精确但慢。</p>
<h3><a id="23__811__75"></a>2.3 支持模型推理(从 8.11 开始)</h3>
<p>Elasticsearch 8.11+ 引入了内置的 <strong><code>text_embedding</code> 管道处理器</strong>,允许你直接将文本变成向量(使用自带模型,无需 Python 推理):</p>
<pre><div class="hljs"><code class="lang-json"><span class="hljs-punctuation">{</span>
<span class="hljs-attr">"text_embedding"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"field_map"</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">{</span>
<span class="hljs-attr">"content"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">"content_vector"</span>
<span class="hljs-punctuation">}</span><span class="hljs-punctuation">,</span>
<span class="hljs-attr">"model_id"</span><span class="hljs-punctuation">:</span> <span class="hljs-string">".elser_model_2"</span>
<span class="hljs-punctuation">}</span>
<span class="hljs-punctuation">}</span>
</code></div></pre>
<h2><a id="3_dense_vector__90"></a>3 dense_vector 案例</h2>
<p>安装依赖</p>
<pre><div class="hljs"><code class="lang-shell">sentence-transformers==4.1.0
huggingface_hub[hf_xet]
elasticsearch==8.13.0
</code></div></pre>
<h3><a id="31__101"></a>3.1 创建索引</h3>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-keyword">from</span> elasticsearch <span class="hljs-keyword">import</span> Elasticsearch
es = Elasticsearch(
<span class="hljs-string">"https://***.***.***.***:9200/"</span>,
basic_auth=(<span class="hljs-string">"elastic"</span>, <span class="hljs-string">"***"</span>),
verify_certs=<span class="hljs-literal">False</span>
)
index_name = <span class="hljs-string">"my_blog_article"</span>
<span class="hljs-comment"># 删除旧索引(如存在)</span>
<span class="hljs-keyword">if</span> es.indices.exists(index=index_name):
es.indices.delete(index=index_name)
<span class="hljs-comment"># 创建索引映射</span>
mapping = {
<span class="hljs-string">"mappings"</span>: {
<span class="hljs-string">"properties"</span>: {
<span class="hljs-string">"title"</span>: {<span class="hljs-string">"type"</span>: <span class="hljs-string">"text"</span>},
<span class="hljs-string">"content"</span>: {<span class="hljs-string">"type"</span>: <span class="hljs-string">"text"</span>},
<span class="hljs-string">"content_vector"</span>: {
<span class="hljs-string">"type"</span>: <span class="hljs-string">"dense_vector"</span>,
<span class="hljs-string">"dims"</span>: <span class="hljs-number">512</span>, <span class="hljs-comment"># 向量维度,取决于你的模型</span>
<span class="hljs-string">"index"</span>: <span class="hljs-literal">True</span>,
<span class="hljs-string">"similarity"</span>: <span class="hljs-string">"cosine"</span>
}
}
}
}
es.indices.create(index=index_name, body=mapping)
</code></div></pre>
<h3><a id="32____139"></a>3.2 插入博客文章(文本 + 向量)</h3>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> SentenceTransformer
<span class="hljs-keyword">from</span> elasticsearch <span class="hljs-keyword">import</span> Elasticsearch
es = Elasticsearch(
<span class="hljs-string">"https://***.***.***.***:9200/"</span>,
basic_auth=(<span class="hljs-string">"elastic"</span>, <span class="hljs-string">"***"</span>),
verify_certs=<span class="hljs-literal">False</span>
)
<span class="hljs-comment"># 输出512维向量</span>
model = SentenceTransformer(<span class="hljs-string">"BAAI/bge-small-zh-v1.5"</span>)
<span class="hljs-comment"># 示例博客文章</span>
articles = [
{<span class="hljs-string">"title"</span>: <span class="hljs-string">"Elasticsearch 简介"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"Elasticsearch 是一个基于 Lucene 的搜索引擎,适用于全文检索。"</span>},
{<span class="hljs-string">"title"</span>: <span class="hljs-string">"机器学习基础"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"本文介绍了机器学习中的监督学习和非监督学习方法。"</span>},
{<span class="hljs-string">"title"</span>: <span class="hljs-string">"如何使用 Python 操作 Elasticsearch"</span>, <span class="hljs-string">"content"</span>: <span class="hljs-string">"作者:有勇气的牛排, 通过 Python 的 elasticsearch 库,可以方便地查询和插入数据。"</span>}
]
index_name = <span class="hljs-string">"my_blog_article"</span>
<span class="hljs-comment"># 插入数据</span>
<span class="hljs-keyword">for</span> i, article <span class="hljs-keyword">in</span> <span class="hljs-built_in">enumerate</span>(articles):
vector = model.encode(article[<span class="hljs-string">"content"</span>]).tolist()
<span class="hljs-built_in">print</span>(article)
<span class="hljs-built_in">print</span>(vector)
doc = {
<span class="hljs-string">"title"</span>: article[<span class="hljs-string">"title"</span>],
<span class="hljs-string">"content"</span>: article[<span class="hljs-string">"content"</span>],
<span class="hljs-string">"content_vector"</span>: vector
}
es.index(index=index_name, <span class="hljs-built_in">id</span>=i + <span class="hljs-number">1</span>, document=doc)
</code></div></pre>
<p><img src="https://static.couragesteak.com/article/a0169054fcb54fd55f9cdd5e98ad7f1e.png" alt="elasticsearch插入向量数据" /></p>
<h3><a id="33__178"></a>3.3 向量查询相似博客</h3>
<pre><div class="hljs"><code class="lang-python"><span class="hljs-keyword">from</span> sentence_transformers <span class="hljs-keyword">import</span> SentenceTransformer
<span class="hljs-keyword">from</span> elasticsearch <span class="hljs-keyword">import</span> Elasticsearch
es = Elasticsearch(
<span class="hljs-string">"https://***.***.***.***:9200/"</span>,
basic_auth=(<span class="hljs-string">"elastic"</span>, <span class="hljs-string">"***"</span>),
verify_certs=<span class="hljs-literal">False</span>
)
index_name = <span class="hljs-string">"my_blog_article"</span>
<span class="hljs-comment"># 输出512维向量</span>
model = SentenceTransformer(<span class="hljs-string">"BAAI/bge-small-zh-v1.5"</span>)
<span class="hljs-comment"># query_text = "如何用 Python 查询 Elasticsearch"</span>
query_text = <span class="hljs-string">"有勇气的牛排"</span>
query_vector = model.encode(query_text).tolist()
<span class="hljs-comment"># 使用 knn 查询(Elasticsearch 8+ 支持)</span>
query_body = {
<span class="hljs-string">"knn"</span>: {
<span class="hljs-string">"field"</span>: <span class="hljs-string">"content_vector"</span>,
<span class="hljs-string">"query_vector"</span>: query_vector,
<span class="hljs-string">"k"</span>: <span class="hljs-number">2</span>,
<span class="hljs-string">"num_candidates"</span>: <span class="hljs-number">10</span>
},
<span class="hljs-string">"_source"</span>: [<span class="hljs-string">"title"</span>, <span class="hljs-string">"content"</span>]
}
response = es.search(index=index_name, body=query_body)
<span class="hljs-keyword">for</span> hit <span class="hljs-keyword">in</span> response[<span class="hljs-string">"hits"</span>][<span class="hljs-string">"hits"</span>]:
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"得分: <span class="hljs-subst">{hit[<span class="hljs-string">'_score'</span>]:<span class="hljs-number">.4</span>f}</span>"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"标题: <span class="hljs-subst">{hit[<span class="hljs-string">'_source'</span>][<span class="hljs-string">'title'</span>]}</span>"</span>)
<span class="hljs-built_in">print</span>(<span class="hljs-string">f"内容: <span class="hljs-subst">{hit[<span class="hljs-string">'_source'</span>][<span class="hljs-string">'content'</span>]}</span>\n"</span>)
</code></div></pre>
<p>输出</p>
<pre><div class="hljs"><code class="lang-shell">得分: 0.8303
标题: 如何使用 Python 操作 Elasticsearch
内容: 作者有勇气的牛排, 通过 Python 的 elasticsearch 库,可以方便地查询和插入数据。
得分: 0.6260
标题: Elasticsearch 简介
内容: Elasticsearch 是一个基于 Lucene 的搜索引擎,适用于全文检索。
</code></div></pre>
<p><img src="https://static.couragesteak.com/article/f387103f095efef33beda306d6c28431.png" alt="Elasticsearch向量化搜索" /></p>
留言