ES101系列04 | Mapping、自定义Analyzer与聚合

47 阅读6分钟

本篇文章主要介绍 ElasticSearch 中 Mapping、自定义 Analyzer 的使用,同时简单介绍 ES 聚合方面的使用,都含有 Demo 方便大家测试。

Mapping

Mapping 类似数据库中的 schema 定义,用于定义字段名称、类型、相关配置。

字段的数据类型

字段类型使用场景底层实现实例数据
text全文搜索(如文章内容、长文本)。倒排索引(分词后存储),支持模糊匹配和相关性评分。"LanTech 指南 "
keyword精确匹配(如 ID、状态码)、聚合和排序。未分词的原始字符串,基于精确值匹配。"user_123"
数值类型范围查询(如价格、年龄)、数学运算和聚合。Lucene 的数值索引优化(如 integerlong)。42 / 3.14
date时间序列数据(如日志时间、事件时间戳)。存储为长整型时间戳(毫秒级),支持时区转换。"2023-10-05T12:30:00Z"
boolean真/假状态(如开关、是否有效)。布尔索引结构,仅存储 truefalsetrue
binary存储二进制数据(如图片、文件)。Base64 编码存储,不支持直接查询。"U29tZSBoZWxsbyB3b3JsZA=="
range区间查询(如价格区间、年龄区间)。Lucene 的数值/日期范围索引,支持 >=<= 等操作。{"gte": 10, "lte": 20}
object嵌套 JSON 对象(如用户信息中的地址字段)。内部文档结构,支持嵌套查询。{"city": "Beijing", "zip": "100000"}
nested复杂嵌套关系(如订单与多个商品的关联)。独立索引的子文档,需通过 nested 查询访问。[{"name": "book", "price": 15}]
geo_point地理位置数据(如经纬度)。存储为坐标对,支持地理距离计算和范围查询。{"lat": 40.7128, "lon": -74.0060}
数组类型存储多个相同类型值(如标签列表、商品分类)。多值字段(无需显式声明),底层以扁平化多值形式存储。["red", "blue", "green"]
ipIP 地址存储与查询(如访问日志中的 IP)。存储为 32 位或 128 位整数,支持 CIDR 范围查询。"192.168.1.1"

Dynamic Mapping

在写入文档时如果索引不存在会自动创建索引,该机制使得我们不用手动定义 Mappings,但是通常不用这个,因为容易推算错误,并且 ES 禁止对有数据写入的字段修改定义。

示例

# 插入测试数据
PUT mapping_test/_doc/1
{
    "uid" : "123",
    "isVip" : false,
    "isAdmin": "true",
    "age":19,
    "heigh":180
}

# 查看字段类型
GET mapping_test/_mapping

# Resp
{
  "mapping_test" : {
    "mappings" : {
      "properties" : {
        "age" : {
          "type" : "long"
        },
        "heigh" : {
          "type" : "long"
        },
        "isAdmin" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        },
        "isVip" : {
          "type" : "boolean"
        },
        "uid" : {
          "type" : "text",
          "fields" : {
            "keyword" : {
              "type" : "keyword",
              "ignore_above" : 256
            }
          }
        }
      }
    }
  }
}

如上结果所示,Dynamic Mapping 机制会自动推断类型,同时 text 类型会新增一个 keyword 类型支持精确查找。

控制 Dynamic Mappings

dynamic 字段不同情况下的表现。

truefalsestrict
文档可索引YESYESNO
字段可索引YESNONO
Mapping 被更新YESNONO

Index

index 用于控制字段是否被索引。

示例

"mobile" : {
  "type" : "text",
  "index": false
}

Index Options

Index Options记录内容作用默认类型
docs仅文档编号(doc id)仅记录文档 ID,节省空间,适用于只需文档匹配的场景非 text 类型字段默认
freqs文档编号(doc id)+ 词频(term frequency)记录文档 ID 和词频,支持基于频率的查询优化
positions文档编号 + 词频 + 位置(position)记录位置信息,支持短语查询(Phrase Query)和邻近查询(Proximity Query)text 类型字段默认
offsets文档编号 + 词频 + 位置 + 偏移量(offset)记录字符偏移量,支持高亮显示等精细文本处理

index_options 用于控制是否存储文档 ID、词频、位置和偏移量等,从而影响搜索效率和功能支持(如短语查询、高亮等)。同时记录的内容越多,占用存储空间越大。

null_value

  • 需要对 Null 值实现搜索。
  • 只有 Keyword 类型支持设定 null_value。

示例

"mobile" : {
  "type" : "keyword",
  "null_value": "NULL"
}

自定义 Analyzer

Character Filter

示例

POST _analyze
{
  "tokenizer":"keyword",
  "char_filter":["html_strip"],
  "text": "<b>hello world</b>"
}

能够将 html 的标签去除。

POST _analyze
{
  "tokenizer": "standard",
  "char_filter": [
      {
        "type" : "mapping",
        "mappings" : [ "- => _"]
      }
    ],
  "text": "123-456, I-test! test-990 650-555-1234"
}

能够将 text 中的 - 替换为 _

Tokenizer

示例

POST _analyze
{
  "tokenizer":"path_hierarchy",
  "text":"/user/ymruan/a/b/c/d/e"
}

能够按照目录层级进行切分。

Token Filter

示例

GET _analyze
{
  "tokenizer": "whitespace",
  "filter": ["lowercase","stop","snowball"],
  "text": ["The girls in China are playing this game!"]
}
  • lowercase - 仅小写
  • stop - 停用词过滤
  • snowball - 词干提取

Index Template

用于自动设定 Mappings 和 Settings,并按照一定的规则自动匹配到新创建的索引中。

  • 仅在索引被新创建时才回起作用。
  • 可以设置多个模版,设置会 merge 在一起。
  • 可以控制 order 的数值控制 merge 的过程。先应用 order 低的,后续高的会覆盖之前的设定。

示例

PUT /_template/template_test
{
    "index_patterns" : ["test*"],
    "order" : 1,
    "settings" : {
    	"number_of_shards": 1,
        "number_of_replicas" : 2
    },
    "mappings" : {
    	"numeric_detection": true
    }
}

表示当一个新索引以 test 开头时,会自动将索引的分片数设置为 2,同时会自动探测数字类型。

Dynamic Template

动态设定字段类型。例如:

  • 将所有字符串类型设定为 keyword。
  • is 开头的字段都设置为 boolean。

示例

PUT my_index
{
  "mappings": {
    "dynamic_templates": [
      {
        "strings_as_boolean": {
          "match_mapping_type": "string",
          "match": "is*",
          "mapping": {
            "type": "boolean"
          }
        }
      },
      {
        "strings_as_keywords": {
          "match_mapping_type": "string",
          "mapping": {
            "type": "keyword"
          }
        }
      }
    ]
  }
}

表示当字符串类型为 is 开头时设置为 boolean 类型,其余设置为 keyword 类型。

聚合(Aggregation)

  • ElasticSearch 除了提供搜索以外,还提供了针对 ES 进行同喜分析的功能。
  • 聚合是一个分析总结全套的数据,而不是寻找单个文档。
  • 性能高且实时性高(不用 T+1)。

本节示例需要在 Kibana 中添加官方提供的 Sample flight data 样例数据。

Bucket

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			}
		}
	}
}

# Resp
"aggregations" : {
"flight_dest" : {
  "doc_count_error_upper_bound" : 0,
  "sum_other_doc_count" : 3187,
  "buckets" : [
	{
	  "key" : "IT",
	  "doc_count" : 2371
	},
	{
	  "key" : "US",
	  "doc_count" : 1987
	},
	// ...
	]
  }
}

将国家分成了桶。

Metric

  • 基于数据集计算结果。
  • 大多数是数学计算,仅输出一个值。

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			},
			"aggs":{
				"avg_price":{
					"avg":{
						"field":"AvgTicketPrice"
					}
				},
				"max_price":{
					"max":{
						"field":"AvgTicketPrice"
					}
				},
				"min_price":{
					"min":{
						"field":"AvgTicketPrice"
					}
				}
			}
		}
	}
}

# Resp
// ...
{
  "key" : "IT",
  "doc_count" : 2371,
  "max_price" : {
	"value" : 1195.3363037109375
  },
  "min_price" : {
	"value" : 100.57646942138672
  },
  "avg_price" : {
	"value" : 586.9627099618385
  }
},
// ...

进行了平均值、最大值、最小值的计算。

嵌套

示例

GET kibana_sample_data_flights/_search
{
	"size": 0,
	"aggs":{
		"flight_dest":{
			"terms":{
				"field":"DestCountry"
			},
			"aggs":{
				"stats_price":{
					"stats":{
						"field":"AvgTicketPrice"
					}
				},
				"wather":{
				  "terms": {
				    "field": "DestWeather",
				    "size": 5
				  }
				}
			}
		}
	}
}

# Resp
// ...
{
  "key" : "IT",
  "doc_count" : 2371,
  "wather" : {
	"doc_count_error_upper_bound" : 0,
	"sum_other_doc_count" : 506,
	"buckets" : [
	  {
		"key" : "Clear",
		"doc_count" : 428
	  },
	  {
		"key" : "Sunny",
		"doc_count" : 424
	  },
	  {
		"key" : "Rain",
		"doc_count" : 417
	  },
	  {
		"key" : "Cloudy",
		"doc_count" : 414
	  },
	  {
		"key" : "Heavy Fog",
		"doc_count" : 182
	  }
	]
  },
  "stats_price" : {
	"count" : 2371,
	"min" : 100.57646942138672,
	"max" : 1195.3363037109375,
	"avg" : 586.9627099618385,
	"sum" : 1391688.585319519
  }
},
// ...

在使用国家分桶后再进行票价统计和 5 组最常见天气的分布。

写在最后

这是该系列的第四篇,主要讲解 ElasticSearch Mapping、自定义 Analyzer 和聚合分析的内容,可以自己去到 Kibana 的 Dev Tool 实战操作,未来会持续更新该系列,欢迎关注👏🏻。

同时欢迎关注公众号:LanTech指南。不定时分享职场思考、独立开发日志、大厂方法论和后端经验❤️

参考

  1. github.com/onebirdrock…
  2. www.elastic.co/elasticsear…