MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

_source过滤与存储字段在MGet API中的使用

2021-12-127.8k 阅读

ElasticSearch 基础概念回顾

在深入探讨 _source 过滤与存储字段在 MGet API 中的使用之前,我们先来回顾一下 ElasticSearch 的一些基础概念。

ElasticSearch 是一个分布式的开源搜索和分析引擎,它基于 Lucene 构建。在 ElasticSearch 中,数据以文档(document)的形式存储,文档被分组到索引(index)中。每个文档都有一个唯一的标识符(ID),并且可以包含多个字段(field)。

_source 字段是 ElasticSearch 中一个非常重要的概念。当一个文档被索引时,它的原始 JSON 表示会被存储在 _source 字段中。这意味着,当我们从 ElasticSearch 检索一个文档时,默认情况下会返回整个 _source 字段的内容。例如,假设我们有一个简单的文档,它描述了一本书:

{
    "title": "ElasticSearch in Action",
    "author": "Rivers",
    "publication_year": 2015,
    "pages": 300
}

当这个文档被索引到 ElasticSearch 中时,上述 JSON 内容会完整地存储在 _source 字段中。当我们执行查询获取这个文档时,默认会得到整个 _source 的内容。

存储字段(Stored Field)则是 ElasticSearch 中另一个概念。在默认情况下,ElasticSearch 会将文档的 _source 存储起来,以便在需要时能够完整地返回文档内容。但是,我们也可以选择将某些字段单独标记为存储字段。存储字段可以被直接从磁盘中读取,而不需要通过解析 _source 来获取值。这在某些特定场景下可以提高检索效率,比如我们只需要获取文档的某个特定字段,而不需要整个 _source 内容时。

MGet API 概述

MGet API 允许我们在一次请求中获取多个文档。这在很多场景下非常有用,比如在一个电商应用中,我们可能需要一次性获取多个商品的信息;或者在一个内容管理系统中,需要同时获取多篇文章的内容。

MGet API 的基本语法如下:

POST /_mget
{
    "docs" : [
        {
            "_index" : "your_index",
            "_id" : "your_id_1"
        },
        {
            "_index" : "your_index",
            "_id" : "your_id_2"
        }
    ]
}

在上述示例中,我们通过 _index 指定了索引名称,通过 _id 指定了要获取的文档 ID。MGet API 会在一次请求中返回这些指定文档的内容。

_source 过滤在 MGet API 中的使用

  1. 基本的 _source 过滤语法MGet API 中,我们可以通过 _source 参数来指定只返回文档 _source 中的部分字段。这样可以减少网络传输的数据量,提高查询性能。例如,我们还是以上面描述书的文档为例,假设我们只关心书的标题和作者,我们可以这样使用 MGet API
POST /books/_mget
{
    "docs" : [
        {
            "_id" : "1",
            "_source": ["title", "author"]
        }
    ]
}

在上述请求中,我们指定了索引为 books,文档 ID 为 1,并且通过 _source 参数指定只返回 titleauthor 字段。ElasticSearch 会返回如下响应:

{
    "docs" : [
        {
            "_index" : "books",
            "_id" : "1",
            "_source" : {
                "title": "ElasticSearch in Action",
                "author": "Rivers"
            }
        }
    ]
}
  1. 使用通配符进行 _source 过滤 除了指定具体的字段名称,我们还可以使用通配符来过滤 _source 字段。例如,如果我们有很多以 date_ 开头的字段,我们想获取所有这些字段,可以这样使用 MGet API
POST /your_index/_mget
{
    "docs" : [
        {
            "_id" : "your_id",
            "_source": ["date_*"]
        }
    ]
}

这样,ElasticSearch 会返回文档 _source 中所有以 date_ 开头的字段。

  1. 排除字段的 _source 过滤 有时候,我们可能想排除某些字段,而不是指定要包含哪些字段。在 MGet API 中,我们可以通过在字段名称前加上 - 来实现排除。例如,假设我们不想获取文档中的 pages 字段:
POST /books/_mget
{
    "docs" : [
        {
            "_id" : "1",
            "_source": ["-pages"]
        }
    ]
}

上述请求会返回除了 pages 字段之外的所有 _source 字段内容。

  1. 复杂的 _source 过滤条件 我们还可以结合包含和排除条件来实现更复杂的 _source 过滤。例如,我们想获取 titleauthor 字段,并且排除 publication_year 字段:
POST /books/_mget
{
    "docs" : [
        {
            "_id" : "1",
            "_source": ["title", "author", "-publication_year"]
        }
    ]
}

存储字段在 MGet API 中的使用

  1. 设置存储字段 要在 ElasticSearch 中使用存储字段,我们需要在映射(mapping)中进行设置。例如,假设我们有一个索引 products,其中有一个 price 字段,我们希望将其设置为存储字段。我们可以这样定义映射:
PUT /products
{
    "mappings": {
        "properties": {
            "price": {
                "type": "float",
                "store": true
            }
        }
    }
}

在上述映射定义中,我们通过 store: trueprice 字段设置为存储字段。

  1. 在 MGet API 中获取存储字段 一旦我们设置了存储字段,在 MGet API 中就可以直接获取这些字段的值,而不需要依赖 _source。例如:
POST /products/_mget
{
    "docs" : [
        {
            "_id" : "1",
            "stored_fields": ["price"]
        }
    ]
}

上述请求会返回 products 索引中 ID 为 1 的文档的 price 存储字段的值:

{
    "docs" : [
        {
            "_index" : "products",
            "_id" : "1",
            "fields" : {
                "price" : [19.99]
            }
        }
    ]
}

注意,存储字段的值会被放在 fields 字段中返回,而不是 _source 字段。

  1. 结合 _source 过滤和存储字段 在实际应用中,我们可能会同时使用 _source 过滤和存储字段。例如,我们想获取文档的 title 字段(来自 _source)和 price 存储字段:
POST /products/_mget
{
    "docs" : [
        {
            "_id" : "1",
            "_source": ["title"],
            "stored_fields": ["price"]
        }
    ]
}

ElasticSearch 会返回如下响应:

{
    "docs" : [
        {
            "_index" : "products",
            "_id" : "1",
            "_source" : {
                "title": "Sample Product"
            },
            "fields" : {
                "price" : [19.99]
            }
        }
    ]
}

性能考量

  1. _source 过滤对性能的影响 使用 _source 过滤可以显著减少网络传输的数据量,尤其是当文档的 _source 非常大时。例如,如果一个文档的 _source 包含大量的文本内容,但我们只需要获取几个关键的元数据字段,通过 _source 过滤可以避免传输大量不必要的文本,从而提高查询的响应速度。

另一方面,_source 过滤也会对 ElasticSearch 的内部处理产生一定影响。ElasticSearch 需要在返回文档之前对 _source 进行解析和过滤,这会消耗一定的 CPU 资源。但是,在大多数情况下,减少网络传输带来的性能提升要远远大于内部处理增加的开销。

  1. 存储字段对性能的影响 存储字段的主要优势在于,它们可以直接从磁盘中读取,而不需要解析 _source。这在需要频繁获取特定字段值的场景下非常高效。例如,在一个电商应用中,如果我们经常需要获取商品的价格,将价格字段设置为存储字段可以提高查询性能。

然而,使用存储字段也有一些缺点。首先,存储字段会占用额外的磁盘空间,因为每个存储字段都需要额外的存储空间。其次,在索引文档时,设置存储字段会增加索引的时间和资源消耗,因为 ElasticSearch 需要额外处理存储字段的存储。

实际应用场景

  1. 内容管理系统 在内容管理系统中,文章可能包含大量的文本内容,但在列表页面展示时,我们可能只需要文章的标题、摘要和发布时间。通过 _source 过滤,我们可以在 MGet API 中只获取这些必要的字段,减少网络传输和前端渲染的压力。同时,如果我们经常需要获取文章的阅读量(假设这是一个存储字段),可以结合存储字段来快速获取该值。

  2. 电商平台 在电商平台中,商品文档可能包含丰富的信息,如商品描述、图片等。但在搜索结果页面,我们可能只需要商品的名称、价格和库存信息。通过 _source 过滤,我们可以在 MGet API 中只获取这些关键信息,提高搜索结果的加载速度。而对于价格字段,由于经常需要获取和展示,将其设置为存储字段可以进一步优化性能。

常见问题及解决方法

  1. _source 过滤不生效 有时候,我们可能会遇到 _source 过滤不生效的情况。这可能是由于以下原因:

    • 索引映射问题:如果索引的映射设置不正确,可能会导致 _source 过滤无法按预期工作。例如,字段名称可能拼写错误,或者字段类型与过滤条件不兼容。我们需要仔细检查索引的映射定义,确保字段名称和类型正确。
    • 版本兼容性问题:在不同版本的 ElasticSearch 中,_source 过滤的语法和行为可能会有所不同。如果我们在升级 ElasticSearch 版本后发现 _source 过滤不生效,需要查阅相应版本的文档,确保使用的语法正确。
  2. 存储字段无法获取 如果存储字段无法获取,可能有以下原因:

    • 未正确设置存储字段:我们需要确保在索引映射中正确设置了 store: true。如果遗漏了这个设置,存储字段将不会被存储,也就无法获取。
    • 索引重建问题:如果在设置存储字段后对索引进行了重建,可能需要重新索引文档,以便存储字段能够生效。

代码示例综合演示

以下是一个使用 Python 的 Elasticsearch 客户端库来演示 _source 过滤和存储字段在 MGet API 中的使用的完整示例。

首先,我们需要安装 elasticsearch 库:

pip install elasticsearch

然后,我们编写如下 Python 代码:

from elasticsearch import Elasticsearch

# 连接到 ElasticSearch 集群
es = Elasticsearch([{'host': 'localhost', 'port': 9200}])

# 创建一个索引并定义映射,设置 price 为存储字段
index_name = 'products'
mapping = {
    "mappings": {
        "properties": {
            "title": {
                "type": "text"
            },
            "price": {
                "type": "float",
                "store": true
            }
        }
    }
}
es.indices.create(index=index_name, body=mapping)

# 索引一个文档
doc = {
    "title": "Sample Product",
    "price": 19.99
}
es.index(index=index_name, id=1, body=doc)

# 使用 MGet API 结合 _source 过滤和存储字段获取文档
mget_body = {
    "docs": [
        {
            "_id": 1,
            "_source": ["title"],
            "stored_fields": ["price"]
        }
    ]
}
response = es.mget(body=mget_body, index=index_name)
print(response)

上述代码首先连接到 ElasticSearch 集群,然后创建一个索引并定义映射,将 price 字段设置为存储字段。接着索引一个文档,最后使用 MGet API 结合 _source 过滤和存储字段获取文档,并打印响应结果。

通过以上详细的介绍、代码示例以及性能考量和实际应用场景分析,相信你对 _source 过滤与存储字段在 MGet API 中的使用有了更深入的理解。在实际应用中,根据具体的业务需求和性能要求,合理地使用这些特性可以优化 ElasticSearch 的查询性能和资源利用效率。