[深入探索ElasticsearchRetriever:灵活搜索的强大引擎]

93 阅读3分钟

引言

Elasticsearch 是一种分布式、RESTful 的搜索和分析引擎,它提供了多租户的全文搜索、关键字搜索、向量搜索、混合搜索以及复杂过滤功能。而 ElasticsearchRetriever 则是一个通用的封装工具,旨在通过查询 DSL 提供对 Elasticsearch 所有功能的灵活访问。对于大多数使用场景,ElasticsearchStoreElasticsearchEmbeddings 等类已经足够,但当这些类无法满足需求时,可以使用 ElasticsearchRetriever。本文旨在引导您开始使用 ElasticsearchRetriever 并展示其丰富功能。

主要内容

设置 Elasticsearch 实例

有两种主要方式来设置 Elasticsearch 实例:

  1. Elastic Cloud:Elastic Cloud 是一个托管的 Elasticsearch 服务。您可以注册一个免费试用账号。要连接到一个不需要登录凭据的 Elasticsearch 实例(例如启动启用了安全功能的 Docker 实例),您只需将 Elasticsearch 的 URL 和索引名称连同嵌入对象一起传递给构造函数。

  2. 本地安装 Elasticsearch:通过本地运行 Elasticsearch 来开始使用。最简单的方法是使用官方的 Elasticsearch Docker 镜像。可以参考 Elasticsearch Docker 文档 来获取更多信息。

安装和配置

安装 langchain-elasticsearch 包以及 langchain-community 以生成文本嵌入:

%pip install -qU langchain-community langchain-elasticsearch

通过以下代码来配置与本地运行的 Elasticsearch 的连接:

from elasticsearch import Elasticsearch

es_url = "http://localhost:9200"
es_client = Elasticsearch(hosts=[es_url])
es_client.info()

索引数据

以下是如何在 Elasticsearch 中索引示例文本文档的代码:

from typing import Iterable
from elasticsearch.helpers import bulk
from langchain_core.embeddings import Embeddings
from langchain_community.embeddings import DeterministicFakeEmbedding

# 创建索引
def create_index(es_client: Elasticsearch, index_name: str, text_field: str, dense_vector_field: str, num_characters_field: str):
    es_client.indices.create(
        index=index_name,
        mappings={
            "properties": {
                text_field: {"type": "text"},
                dense_vector_field: {"type": "dense_vector"},
                num_characters_field: {"type": "integer"},
            }
        },
    )

# 索引数据
def index_data(es_client: Elasticsearch, index_name: str, text_field: str, dense_vector_field: str, embeddings: Embeddings, texts: Iterable[str], refresh: bool = True) -> None:
    create_index(es_client, index_name, text_field, dense_vector_field, num_characters_field)
    vectors = embeddings.embed_documents(list(texts))
    requests = [
        {
            "_op_type": "index",
            "_index": index_name,
            "_id": i,
            text_field: text,
            dense_vector_field: vector,
            num_characters_field: len(text),
        }
        for i, (text, vector) in enumerate(zip(texts, vectors))
    ]
    bulk(es_client, requests)
    if refresh:
        es_client.indices.refresh(index=index_name)

# 示例数据
texts = ["foo", "bar", "world", "hello world", "hello", "foo bar", "bla bla foo"]
embeddings = DeterministicFakeEmbedding(size=3)
index_name = "test-langchain-retriever"
text_field = "text"
dense_vector_field = "fake_embedding"
num_characters_field = "num_characters"

index_data(es_client, index_name, text_field, dense_vector_field, embeddings, texts)

向量搜索示例

以下是如何使用 ElasticsearchRetriever 进行向量检索的示例:

from typing import Dict

# 向量查询
def vector_query(search_query: str) -> Dict:
    vector = embeddings.embed_query(search_query)
    return {
        "knn": {
            "field": dense_vector_field,
            "query_vector": vector,
            "k": 5,
            "num_candidates": 10,
        }
    }

vector_retriever = ElasticsearchRetriever.from_es_params(
    index_name=index_name,
    body_func=vector_query,
    content_field=text_field,
    url=es_url,  # 使用API代理服务提高访问稳定性
)

vector_retriever.invoke("foo")

常见问题和解决方案

  • 网络访问问题:在某些地区,由于网络限制,访问 Elasticsearch API 时可能会遇到问题。解决方案是在API调用时使用API代理服务以提高访问稳定性。

  • 数据不一致:在批量索引数据后,若查询到的数据不一致,建议在数据索引后调用 es_client.indices.refresh(index=索引名称) 以确保数据已被刷新。

总结和进一步学习资源

通过本文的介绍,我们了解了如何使用 ElasticsearchRetriever 来实现灵活的搜索功能。如果想深入了解其所有功能和配置,建议查阅 ElasticsearchRetriever 的详细文档

参考资料

如果这篇文章对你有帮助,欢迎点赞并关注我的博客。您的支持是我持续创作的动力! ---END---