Elasticsearch 是一款强大的分布式搜索和分析引擎,其核心功能之一就是 _search
API。_search
API 允许用户执行复杂的查询,并从 Elasticsearch 索引中检索匹配的文档。在搜索结果中,有一个名为 _source
的特殊字段,它包含了索引文档时的原始 JSON 数据。
_source
元数据在 Elasticsearch 中扮演着重要的角色,它不仅决定了搜索结果的呈现方式,还影响着更新、重建索引等操作。本文档将深入探讨 Elasticsearch 7.10 版本中 _search
API 的 _source
元数据,包括其基本概念、用法、禁用、过滤以及最佳实践。
_source
元数据基础_source
字段?在 Elasticsearch 中,当你索引一个文档时,原始的 JSON 数据会被存储在一个名为 _source
的特殊字段中。默认情况下,_source
字段是启用的,这意味着 Elasticsearch 会完整地保存你提供的 JSON 文档。
在搜索结果中,_source
字段的内容会直接返回,让你能够看到索引文档的原始数据。这对于数据的呈现、调试以及后续处理都非常重要。
_source
的基本用法要通过 _search
API 获取 _source
数据,你只需要发起一个正常的搜索请求。ElasticSearch 默认会返回 _source
字段。
示例:
假设我们有一个名为 products
的索引,现在我们插入一条文档数据:
PUT /products/_doc/1
{
"name": "高性能笔记本电脑",
"price": 8999.99,
"description": "这是一款高性能的笔记本电脑,配备了最新的处理器和显卡。",
"tags": ["电脑", "笔记本", "高性能"]
}
然后,我们执行一个简单的搜索请求,查询所有文档:
GET /products/_search
{
"query": {
"match_all": {}
}
}
ElasticSearch 的响应结果如下:
{
"took" : 2,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "products",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "高性能笔记本电脑",
"price" : 8999.99,
"description" : "这是一款高性能的笔记本电脑,配备了最新的处理器和显卡。",
"tags" : [
"电脑",
"笔记本",
"高性能"
]
}
}
]
}
}
可以看到,_source
字段包含了我们索引文档时提供的完整 JSON 数据。
_source
虽然 _source
字段非常有用,但在某些特殊情况下,你可能会考虑禁用它。
_source
字段要禁用 _source
字段,你需要在创建索引的 Mapping 中进行设置。具体来说,就是在 _source
字段的配置中,将 enabled
属性设置为 false
。
示例:
创建一个名为 products_no_source
的索引,并在其 Mapping 中禁用 _source
字段:
PUT /products_no_source
{
"mappings": {
"_source": {
"enabled": false
}
}
}
现在,我们尝试向这个索引中插入一个文档:
PUT /products_no_source/_doc/1
{
"name": "测试商品",
"price": 100
}
然后,我们执行一个搜索请求:
GET /products_no_source/_search
{
"query": {
"match_all": {}
}
}
你会发现,响应结果中不再包含 _source
字段:
{
"hits": {
"total": {
"value": 1,
"relation": "eq"
},
"max_score": 1.0,
"hits": [
{
"_index": "products_no_source",
"_type": "_doc",
"_id": "1",
"_score": 1.0
// 注意:这里没有 _source 字段
}
]
}
}
_source
的利弊禁用 _source
字段可以带来一些好处,但也伴随着一系列的限制。
好处:
_source
字段存储了原始的 JSON 文档,如果文档很大,禁用它可以显著减少索引的存储空间占用。坏处:
update
、update_by_query
和 reindex
API: 这些 API 都依赖于 _source
字段来获取文档的原始内容。如果禁用了 _source
,这些 API 将无法使用。_source
字段。_source
字段,你将无法查看索引时使用的原始文档,这会给查询和聚合的调试带来困难。基于以上利弊分析,通常情况下不推荐禁用 _source
字段。_source
带来的便利性和功能性远远超过了它所占用的存储空间。
如果你仅仅是为了节省磁盘空间,更好的做法是优化你的数据模型,或者使用 Elasticsearch 的索引压缩功能。索引压缩可以在不牺牲功能的前提下,有效地减少存储空间占用。
_source
数据源过滤即使你选择保留 _source
字段(通常也是推荐的做法),你仍然可以通过 _source
过滤来控制搜索结果中返回哪些字段,或者不返回哪些字段。
_source
过滤主要有以下几个好处:
_source
字段,可以更快地构建响应结果。ElasticSearch 提供了两种方式来进行 _source
过滤:
_search
请求中定义(推荐)你可以在创建索引的 Mapping 中,通过 includes
和 excludes
参数来指定 _source
字段中包含或排除哪些字段。
includes
:指定要包含的字段,支持通配符。excludes
:指定要排除的字段,支持通配符。示例:
创建一个名为 products_filtered_mapping
的索引,并在其 Mapping 中定义 _source
过滤规则:
PUT /products_filtered_mapping
{
"mappings": {
"_source": {
"includes": [
"name",
"price"
],
"excludes": [
"desc*",
"tags"
]
}
}
}
我们向索引中插入测试文档:
PUT /products_filtered_mapping/_doc/1
{
"name": "测试商品",
"price": 100,
"desc": "一个描述",
"description": "详细的描述信息",
"tags":["tag1","tag2"]
}
执行一个搜索请求:
GET /products_filtered_mapping/_search
可以发现,在返回结果中只包含name
和price
字段。
{
"took" : 0,
"timed_out" : false,
"_shards" : {
"total" : 1,
"successful" : 1,
"skipped" : 0,
"failed" : 0
},
"hits" : {
"total" : {
"value" : 1,
"relation" : "eq"
},
"max_score" : 1.0,
"hits" : [
{
"_index" : "products_filtered_mapping",
"_type" : "_doc",
"_id" : "1",
"_score" : 1.0,
"_source" : {
"name" : "测试商品",
"price" : 100
}
}
]
}
}
缺点:
这种方式最大的问题在于 Mapping 一旦创建就无法修改。这意味着你无法灵活地调整 _source
过滤规则,除非重建索引。因此,通常不推荐这种方式。
_search
请求中定义(推荐)更灵活的做法是在 _search
请求中直接指定 _source
过滤规则。这样,你可以根据不同的查询需求,动态地控制返回的字段。
示例:
我们已经有有一个名为 products
的索引。
GET /products/_search
{
"_source": ["name", "price"],
"query": {
"match_all": {}
}
}
这个请求只返回 name
和 price
字段。
GET /products/_search
{
"_source": {
"excludes": ["description", "tags"]
},
"query": {
"match_all": {}
}
}
这个请求排除了 description
和 tags
字段。
GET /products/_search
{
"_source": "name*",
"query": {
"match_all": {}
}
}
这个请求返回所有以 name
开头的字段。
GET /products/_search
{
"query":{
"match_all": {}
},
"_source": false
}
includes
和 excludes
:GET /products/_search
{
"_source": {
"includes": [
"name",
"price"
],
"excludes": [
"*.description" // 排除所有以 .description 结尾的字段
]
},
"query": {
"match_all": {}
}
}
这个请求返回 name
和 price
字段,但排除了所有以 .description
结尾的字段。
_source
过滤的最佳实践和注意事项excludes
不代表不能通过该字段检索: 即使你在 _source
中排除了某个字段,这并不意味着你不能通过该字段进行搜索或过滤。_source
过滤只影响搜索结果的返回内容,不影响索引的构建和查询的执行。_source
过滤对性能的影响: 虽然_source
过滤通常可以提高响应速度,因为减少数据传输。但是如果过滤规则过于复杂,可能会增加ES的处理时间。通常情况下,这种影响较小。_source
过滤来保护这些信息。但同时也要注意,不要过度过滤,以免影响查询的灵活性和性能。_search
参数: _source
过滤可以与其他 _search
API 参数(例如 query
、sort
、from
、size
等)结合使用,实现更精细的查询控制。_source
元数据是 Elasticsearch 中一个非常重要的概念,它存储了索引文档的原始 JSON 数据。虽然在某些极端情况下可以禁用 _source
,但通常情况下,保留 _source
并利用其提供的过滤功能是最佳选择。
通过 _source
过滤,你可以灵活地控制搜索结果中返回的字段,减少网络传输开销,提高响应速度,并保护敏感信息。
在实际应用中,建议:
_source
字段。_search
请求中定义 _source
过滤规则,而不是在 Mapping 中定义。includes
、excludes
和通配符。