有勇气的牛排博客
☰

Elasticsearch8向量搜索|Python教程案例


知止自律打卡-微信小程序APP
一款微信自律打卡小程序,支持自定义打卡项目、习惯记录、项目日记、专注计时与数据统计。自律打卡、学习打卡、读书打卡、健身打卡、运动打卡、减肥打卡、戒色打卡、养生打卡、戒烟、戒酒、戒熬夜等多种场景。万能打卡模板自由创建。可自定义每日打卡目标,记录习惯连续天数,可视化追踪成长进度。
了解产品详情 →
微信搜索「知止自律打卡」
知止自律打卡微信小程序

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
)

# 输出512维向量
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)

elasticsearch插入向量数据

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"

# 输出512维向量
model = SentenceTransformer("BAAI/bge-small-zh-v1.5")

# query_text = "如何用 Python 查询 Elasticsearch"
query_text = "有勇气的牛排"

query_vector = model.encode(query_text).tolist()

# 使用 knn 查询(Elasticsearch 8+ 支持)
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 的搜索引擎,适用于全文检索。

Elasticsearch向量化搜索

标签:

×
×