Elasticsearch中如何利用索引内synonyms字段实现同义词搜索
实现单文档自定义同义词替代外部文件方案
Elasticsearch的全局同义词分析器是索引级别的全局配置,无法直接引用单条文档的字段值,所以要实现每条文档自定义同义词的需求,得从查询阶段动态处理入手,以下是具体实现步骤:
1. 创建基础索引
不需要配置同义词分析器,只需定义字段类型,注意开启synonyms字段的fielddata方便后续脚本处理:
PUT /test_synonym { "mappings": { "properties": { "name": { "type": "text" }, "description": { "type": "text" }, "synonyms": { "type": "text", "fielddata": true } } } }
2. 插入批量数据
先修正你原数据里重复_id的问题(会导致后插入的数据覆盖前一条),然后执行批量插入:
POST /test_synonym/_bulk {"index":{"_index":"test_synonym","_id":"1"}} {"name":"test one two","description":"test numbers","synonyms": "tot, number"} {"index":{"_index":"test_synonym","_id":"2"}} {"name":"test coding","description":"test coding","synonyms": "tc, program"}
3. 方法一:查询时用脚本扩展同义词条件
当搜索某个词时,通过bool查询同时匹配原始字段和同义词组:
GET /test_synonym/_search { "query": { "bool": { "should": [ // 匹配name/description里的查询词 { "multi_match": { "query": "tot", "fields": ["name", "description"] } }, // 匹配同义词组包含查询词的文档(实现同义词反向匹配) { "script": { "script": { "source": """ def syns = doc['synonyms'].value.split(','); for (def syn : syns) { if (syn.trim() == params.query_term) { return true; } } return false; """, "params": { "query_term": "tot" } } } } ] } } }
4. 方法二:用Runtime字段预拆分同义词数组
提前定义一个Runtime字段,把逗号分隔的synonyms拆成数组,后续查询更简洁:
PUT /test_synonym/_mapping { "runtime": { "synonym_list": { "type": "keyword", "script": """ def synArray = new ArrayList(); if (doc['synonyms'].size() > 0) { def syns = doc['synonyms'].value.split(','); for (def syn : syns) { synArray.add(syn.trim()); } } emit(synArray); """ } } }
之后查询时直接用terms匹配同义词数组,结合原始字段查询:
GET /test_synonym/_search { "query": { "bool": { "should": [ { "multi_match": { "query": "program", "fields": ["name", "description"] } }, { "terms": { "synonym_list": ["program"] } } ] } } }
5. 进阶:实现双向同义词匹配(类似分析器替换效果)
如果要实现“搜索同义词组里的任意词,都能匹配到文档的原始字段内容”,可以用脚本把同义词组的所有词都作为查询条件:
GET /test_synonym/_search { "query": { "bool": { "should": [ { "multi_match": { "query": "tot", "fields": ["name", "description"] } }, { "script": { "script": { "source": """ def syns = doc['synonyms'].value.split(','); def queryTerms = new ArrayList(); queryTerms.add(params.query_term); for (def syn : syns) { queryTerms.add(syn.trim()); } return doc['name'].value.containsAny(queryTerms) || doc['description'].value.containsAny(queryTerms); """, "params": { "query_term": "tot" } } } } ] } } }
注意事项
- 别用索引时的同义词分析器:全局分析器没法引用单文档字段,必须在查询阶段处理。
- 性能优化:如果数据量较大,建议索引时就把
synonyms存成数组类型(比如["tot", "number"]),减少脚本拆分的开销。
内容的提问来源于stack exchange,提问作者Narayan bhat
相关产品推荐
相关产品推荐

