Elasticsearch的标准版本及以上是支持设置同义词功能的, 其实也就是除了OSS(开源)版以外其它的都支持.

环境说明

  • Elasticsearch 7.6.x
  • 与ES相匹配的IK分词插件
  • 示例中会分别使用到shell命令和Kibana, 以$开头的代表是shell命令, 否则表示Kibana的console命令

操作

同义词可以使用 synonym 参数来内嵌指定,或者必须 存在于集群每一个节点上的同义词文件中。 同义词文件路径由 synonyms_path 参数指定,应绝对或相对于 Elasticsearch config 目录。

下面以同义词的两种设置方式来介绍:

同义词文件方式

设置同义词文件

# 进入Elasticsearch目录执行,生成文件$ echo '"iPhone,苹果手机 => iPhone,苹果手机",    "2233,22娘,33娘 => bilibili,B站"' > config/analysis/synonyms.txt

创建索引

PUT /goods2{  "settings": {    "analysis": {      "filter": {        "my_synonym_filter": {          "type": "synonym",          "updateable": true,          "synonyms_path": "analysis/synonyms.txt"        }      },      "analyzer": {        "my_synonyms_analyzer": {          "tokenizer": "ik_smart",          "filter": [            "my_synonym_filter"          ]        }      }    }  },  "mappings": {    "properties": {      "title": {        "type": "text",        "analyzer": "ik_smart",        "search_analyzer": "my_synonyms_analyzer"      }    }  }}
my_synonym_filter是自定义的词汇过滤器, my_synonyms_analyzer是自定义的分析器, 可以看出后者是包含并引用了前者的.

在本索引中自定义的词汇过虑器和分析器也只能在当前索引中使用.

updateable指示能否动态更新, 必须为true才能动态更新同义词

synonyms_path指示同义词文件的位置

analysis.analyzer.tokenizer指示在这个分析器里用ik_smart的分词器, 在这个索引中的分析链是原始文本 => 分词器 => 词汇过滤器, 即原始文本先经过分词的结果再用来给词汇过滤器处理(在这个索引的作用是同义词).

mappings.properties.title.search_analyzer指示title字段在查询时使用my_synonyms_analyzer分析器, 同理mappings.properties.title.analyzer指示其在索引时使用的分析器.

查看分析结果

第一行分词的效果
# 字母大小写没有影响GET goods2/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "iphone"}GET goods2/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "苹果手机"}

上面两条语句的结果是一样的

{  "tokens" : [    {      "token" : "iphone",      "start_offset" : 0,      "end_offset" : 6,      "type" : "ENGLISH",      "position" : 0    },    {      "token" : "苹果",      "start_offset" : 0,      "end_offset" : 6,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "手机",      "start_offset" : 0,      "end_offset" : 6,      "type" : "SYNONYM",      "position" : 1    }  ]}
第二行分词的效果
GET goods2/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "2233"}GET goods2/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "22娘"}

结果

{  "tokens" : [    {      "token" : "bilibili",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "b",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "站",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 1    }  ]}

变更同义词并更新索引

# 进入Elasticsearch目录执行,生成文件# `iPhone,苹果手机 => iPhone,苹果手机`与`iPhone,苹果手机`的效果是一样的# 内容中的双引号`"`和行末的逗号`,`不是必须的(没有的话须要有换行符), 这里只是为了和和内嵌式的保持一致才这么写的$ echo '"iPhone,苹果手机",    "2233,22娘,33娘 => bilibili,B站,二次元"' > config/analysis/synonyms.txt
# 使新的同义词生效POST /goods2/_reload_search_analyzers
变更同义词后的第二行分词的效果
GET goods2/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "2233"}

结果

{  "tokens" : [    {      "token" : "bilibili",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "b",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "二次元",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 0    },    {      "token" : "站",      "start_offset" : 0,      "end_offset" : 4,      "type" : "SYNONYM",      "position" : 1    }  ]}

内嵌方式

创建索引

同义词配置就在synonyms属性里

PUT /goods3{  "settings": {    "analysis": {      "filter": {        "my_synonym_filter": {          "type": "synonym",          "synonyms": [            "iPhone,苹果手机 => iPhone,苹果手机",            "2233,22娘,33娘 => bilibili,B站"          ]        }      },      "analyzer": {        "my_synonyms_analyzer": {          "tokenizer": "ik_smart",          "filter": [            "my_synonym_filter"          ]        }      }    }  },  "mappings": {    "properties": {      "title": {        "type": "text",        "analyzer": "ik_smart",        "search_analyzer": "my_synonyms_analyzer"      }    }  }}

查看分析结果

下面的结果跟同义词文件方式的是一样的

GET goods3/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "iphone"}GET goods3/_analyze{  "analyzer": "my_synonyms_analyzer",  "text": "2233"}

变更同义词并更新索引

# 须要先关闭索引才能变更设置POST /goods3/_closePUT /goods3/_settings/{  "analysis": {    "filter": {      "my_synonym_filter": {        "type": "synonym",        "synonyms": [          "iPhone,苹果手机",          "2233,22娘,33娘 => bilibili,B站,二次元"        ]      }    }  }}# 重新开启索引POST /goods3/_open

查询实践

以索引goods2为例

# 插入一条数据POST /goods2/_doc/1{  "title":"bilibili是个好平台"}# 通过`2233`关键词查找GET /goods2/_search{  "query": {    "match": {      "title": "2233"    }  }}

结果

{  "took" : 0,  "timed_out" : false,  "_shards" : {    "total" : 1,    "successful" : 1,    "skipped" : 0,    "failed" : 0  },  "hits" : {    "total" : {      "value" : 1,      "relation" : "eq"    },    "max_score" : 0.2876821,    "hits" : [      {        "_index" : "goods2",        "_type" : "_doc",        "_id" : "1",        "_score" : 0.2876821,        "_source" : {          "title" : "bilibili是个好平台"        }      }    ]  }}

总结

  • 在Elasticsearch中设置同义词有内嵌式同义词文件式两种
  • 同义词文件式可以在不关闭索引的情况下动态更新同义词

参考资料

  • Elasticsearch Reference 7.6 重载分析器API
  • Elasticsearch: 权威指南
  • Elastic Stack 各版本的区别