场景案例
车牌号分词示例
在本例中要实现一个停车场客户车辆信息查询的场景。根据客户输入停放车辆的车牌号进行模糊匹配找到该车辆停放位置信息。为了简化索引信息 parking_info 中只需要包含三个字段即可,分别是:
l license_plate 文本类型,车牌号,需要分词器实现模糊匹配
l spot_number 文本类型,停车位编号
l entry_time 日期类型,记录入场时间
在 Elasticsesarch 中实现模糊匹配的方式有很多,比如常见的前缀匹配和正则表达。今天我们换一种思路尝试直接从分词器入手,前面在 Token Filter 中提到过一个 n_gram 过滤器可以将单词按照多元语法切分,这样可以满足车牌号在写入时把拆分的结果冗余在倒排索引,例如 “京N12345” 就能得到数组 ["京",“京N”,“京N1”,“京N12”.....]。这样搜索时输入的部分字符可以直接在倒排索引直接匹配。自定义分词器 license_plate_analyzer 构成如下:
l Tokenizer
○ whitespace Tokenizer
l Token Filter
○ Lowercase Token Filter,单词转换为小写字母统一格式
○ Ngram Token Filter,车牌号根据多元语法拆分,长度在1-8位
使用 n_gram 拆分单词时如果切分间隔大于1时,需要修改索引 index.max_ngram_diff 属性默认值。在本例中,我们要把一个车牌号从首位开始依次进行切分,所以索引要设置 index.max_ngram_diff 为车牌号的整体长度8。现在我们定义好了写入阶段的分词器,想象一下,如果在搜索时也使用这个分词器将会发生什么?默认搜索条件此时也会经过 n_gram 的切分产生若干的单词,这将就会将搜索内容拆分的子部分当作条件进行匹配从而导致返回不相关的文档。为了解决这个问题就需要在搜索时指定分词器,让搜索的内容不进行拆分,其它设置和写入分词器保持一致。搜索分词器 license_plate_search 的结构如下:
l Tokenizer
○ whitespace Tokenizer
l Token Filter
○ Lowercase Token Filter,单词转换为小写字母统一格式
索引创建分词器设置和测试数据如下:
PUT parking_info { "settings": { "index.max_ngram_diff": 8, "analysis": { "analyzer": { "license_plate_analyzer": { "type": "custom", "tokenizer": "whitespace", "filter": [ "lowercase", "license_plate_filter" ] }, "license_plate_search": { "type": "custom", "tokenizer": "whitespace", "filter": [ "lowercase" ] } }, "filter": { "license_plate_filter": { "type": "ngram", "max_gram": 8, "min_gram": 1 } } } }, "mappings": { "properties": { "license_plate": { "type": "text", "analyzer": "license_plate_analyzer", "search_analyzer": "license_plate_search" }, "spot_number": { "type": "keyword" }, "entry_time": { "type": "date" } } } } POST _bulk {"index":{"_index":"parking_info","_id": "1"}} {"license_plate":"京YC5722", "spot_number":"A1530","entry_time":"2021-08-07T09:25:00Z"} {"index":{"_index":"parking_info","_id": "2"}} {"license_plate":"京B985A9", "spot_number":"A0750","entry_time":"2021-08-15T23:28:35Z"} {"index":{"_index":"parking_info","_id": "3"}} {"license_plate":"京AD7R535", "spot_number":"A1220","entry_time":"2021-08-15T15:19:54Z"} {"index":{"_index":"parking_info","_id": "4"}} {"license_plate":"京D53W72", "spot_number":"A0352","entry_time":"2021-08-15T21:33:34Z"} {"index":{"_index":"parking_info","_id": "5"}} {"license_plate":"京NLA911", "spot_number":"A0051","entry_time":"2021-08-15T20:15:07Z"}
接下来我们想要查询 “京D53W72” 车辆信息通过输入 "京D53W72 D53W72 W72" 都能够匹配到结果。
GET parking_info/_search { "query": { "match": { "license_plate":"京D53W72 D53W72 W72" } } }
《Elastic Stack 实战手册》——三、产品能力——3.4.入门篇——3.4.2.Elasticsearch基础应用——3.4.2.17.Text analysis, settings 及 mappings——3.4.2.17.4.Analyzers / Custom analyzers(15) https://developer.aliyun.com/article/1229753