聊的不止技术。跟着小帅写代码,还原和技术大牛一对一真实对话,剖析真实项目筑成的一砖一瓦,了解最新最及时的资讯信息,还可以学到日常撩妹小技巧哦,让我们开始探索主人公小帅的职场生涯吧!
(PS:本系列文章以幽默风趣风格为主,较真侠和杠精请绕道~)
一、美女的搜索条件
(上文提到,小帅看了行服小姐姐输入的搜索条件后,瞬间变成愁眉苦脸…)
小帅:“老胡,她这样搜,把我整不会了!果然,Input is evil.”
江华:“你搞笑吧,这不是很正常么,你在百度谷歌上不也是这样搜吗?”
小帅:“话是这样说,她不是要模糊搜索么,这样搜索根本匹配不到任何数据啊!她不加空格可能还能搜到一些商品。”
老胡:“有时候客户提的诉求并不严格是字面的意思,毕竟大家的理解层面不一样,行服说的模糊搜索,应该指的是带着她输入的字样的商品都能搜索到,所以你需要换一种实现方式。”
小帅:“那要怎样实现呢?老胡给点思路我参考下。”
老胡:“其实简单点可以尝试用分词检索,这里可以分成两部分。其一是建立倒排索引时,把需要检索的物料文本字段做好分词切片。其二是把搜索的关键词文本做分词切片,利用ElasticSearch提供的特性将两者做分词匹配,这样就能返回你当前需要检索的商品了。”
二、正排索引和倒排索引
我们知道ElasticSearch支持快速全文检索是因为它使用一种成为倒排索引的结构,那什么是倒排索引呢?我们先来重温一下正排索引和倒排索引的概念。
正排索引:指的是将文档按照顺序进行存储,每个文档包含了所有的字段信息,包括字段的值和位置,这样查询速度较快,但是占用的存储空间较大。简单来说就是-用主键ID来指向数据内容。
倒排索引:则是将文档中的每个字段中的每个单词进行索引,每个单词都有一个指向包含该单词的文档的指针,这样查询速度较快,而且占用的存储空间较小。简单来说就是-用数据内容关键词来指向主键ID。
三、分词和分词器
有了这些概念,我们能大概理解为什么ES能支持快速全文检索了,它的核心是关键词的提取。那在ES是怎样提取数据关键词的呢?这里涉及到一个重要的概念分词和分词器。
分词:是将文本按照一定的规则进行切分,得到一系列的词语。这些词语可以用来进行索引和搜索。
分词器:是一个插件,可以根据不同的需求选择不同的分词器,如英文分词器、中文分词器等。
中文的分词与英文的分词不同,因为中文没有明显的分隔符,所以需要根据词语的语法和语义进行切分。中文分词器可以使用一些现成的开源库,如IKAnalyzer、SmartChineseAnalyzer、jieba等。这些分词器可以进行中文分词,并且可以处理一些特殊情况,如人名、地名等。
这里,建立物料索引的时候我们可以使用分词器ik_max_word,使文本切割得比较细粒度且完整,query检索时候使用分词器ik_smart,减少分词数量尽量提高检索相关性。
## 测试分词器
GET _analyze
{
"analyzer": "ik_max_word",
"text":"1078823925183295617 测试用例店铺 8757783 8757784 aaazunTEST1"
}
GET _analyze
{
"analyzer": "ik_smart",
"text":"aaazunTEST1"
}
小帅:“我明白了,我这就去改一下。”
四、分词检索
(于是,小帅噼里啪啦一顿乱敲,利用reindex做数据迁移,利用elasticsearch分词能力,搜索系统2.1 elasticsearch分词版本出来了)
主要技能点
1、建立新索引skus_v2,主要用来更换mapping,使用分词器重建索引。PS:这里设置refresh时间为30s目的是加速迁移,大索引怎样迁移可以留言咨询,这里不一一展开。
PUT skus_v2
{
"settings": {
"refresh_interval": "30s",
"number_of_replicas": 1,
"number_of_shards": 5
},
"mappings": {
"_doc": {
"dynamic": "false",
"_all": {
"enabled": false
},
"dynamic_templates": [],
"properties": {
"skuNameChinese": { ## 篇幅有限,其它略写如productSkuId,productSpuId均为keyword类型
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
},
"analyzer": "ik_max_word",
"search_analyzer": "ik_smart"
}
}
}
}
}
2、利用elasticsearch的reindex功能,从旧索引skus_v1迁移数据到新索引skus_v2。
## size 表示每批次迁移多少条数据
## query 表示只迁移有效的数据
## "conflicts": "proceed" 表示不中断冲突
## "op_type": "create" 表示只向目标索引插入新数据
## external表示保留最新的 version 对应的数据
## slices 表示多少个分片并行迁移
## wait_for_completion=false表示异步,防止超时
POST _reindex?wait_for_completion=false&slices=5
{
"source": {
"index": "skus_v1",
"size": 10000,
"query": {
"bool": {
"must": [
{
"term": {
"delFlag": {
"value": 0,
"boost": 1
}
}
}
]
}
}
},
"dest": {
"index": "skus_v2",
"version_type": "external"
},
"conflicts": "proceed"
}
3、利用elasticsearch的别名alias功能,指向新索引skus_v2,做在线替换。
POST /_aliases
{
"actions": [
{
"remove": {
"index": "skus_v1",
"alias": "skus"
}
},
{
"add": {
"index": "skus_v2",
"alias": "skus"
}
}
]
}
4、例如搜索水杯的语句可以改写为这样。
GET skus/_search
{
"from": 0,
"size": 10,
"query": {
"bool": {
"must": [
{
"term": {
"delFlag": {
"value": 0,
"boost": 1
}
}
},
{
"match": {
"searchTag": {
"query": "水杯",
"operator": "AND",
"prefix_length": 0,
"max_expansions": 50,
"fuzzy_transpositions": true,
"lenient": false,
"zero_terms_query": "NONE",
"auto_generate_synonyms_phrase_query": true,
"boost": 1
}
}
}
],
"adjust_pure_negative": true,
"boost": 1
}
},
"_source": false,
"sort": [
{
"id": {
"order": "desc"
}
}
],
"collapse": {
"field": "productSpuId",
"inner_hits": {
"name": "productSpuId",
"ignore_unmapped": true,
"from": 0,
"size": 0,
"version": false,
"seq_no_primary_term": false,
"explain": false,
"track_scores": false
}
}
}
未完待续…