在作搜索的时候,下拉联想词的搜索确定是最多见的一个场景,用户在输入的时候,要自动补全词干,说得简单点,就是以...开头搜索,若是是数据库,一句SQL就很容易实现,但在elasticsearch如何实现呢?java
你们可能会立马想到用elasticsearch自带的suggest功能,确实,在一些初级应用场景,特别是数据量比较少的状况下,suggest能够快速简易的解决问题。正则表达式
在数据量比较大的时候,性能有待提升,并且遇到复杂场景,suggest就会显得力不从心,看下面一个需求:spring
一、下拉结果须要根据城市过滤 数据库
二、下拉结果须要根据拼音搜索、首字母搜索、中文拼音混合搜索等app
若是使用suggest,是否是无从下手?框架
下面我介绍另外二种实现的方式,这二种试更加灵活,以上二个问题皆可解决,因为篇幅,我将在其余章节具体讲解拼音+混合搜索。curl
1、基于正则表达式搜索elasticsearch
要点:索引的时候,使用"keyword"做为tokenizer,把整个文本看成一个term。性能
curl -XPUT localhost:9200/search_words_index -d '{ "settings" : { "refresh_interval" : "5s", "number_of_shards" : 1, "number_of_replicas" : 1, "analysis" : { "analyzer": { "myAnalyzer": { "type": "custom", "tokenizer": "keyword", "filter": ["lowercase"] } } } }, "mappings": { "search_words_type": { "properties": { "words": { "type": "string", "index": "analyzed", "indexAnalyzer" : "myAnalyzer" } } } } } }'
搜索的时候,可以使用queryStringQuery或者wildcardQuery实现正则表达式查询,啰嗦一句,queryStringQuery与wildcardQuery的区别是,wildcardQuery是一种低级查询,不会进行analyzer的,而queryStringQuery则会,更具体区别的可参考官网资料。ui
下面以queryStringQuery方式为例进行说明,关键代码:
String reg=/key.*/;
QueryBuilders.queryStringQuery(reg).field("words").analyzer("myAnalyzer"));
这种方式的优势是简单,索引空间占用也不大,效率也还能够,但我更推荐下面的一种式,性能会更佳。
2、基于edge-ngram分词法
这种方式是典型的以空间换时间的作法,惟一的缺点是会加大索引开销,索引数据的时间也会加长,但这种开销都是在索引阶段,并不会影响查询阶段,只要有足够的磁盘和内存空间,效率仍是很不错的。
要点:索引阶段使用edge-ngram分词,按金字塔式的分割成独立的term。以下:
中华人民共和国
中华人民共和
中华人民共
中华人民
中华人
中华
中
索引以下:
curl -XPUT localhost:9200/search_words_index -d '{ "settings" : { "refresh_interval" : "5s", "number_of_shards" : 1, "number_of_replicas" : 1, "analysis" : {
"filter": {
"edge_ngram_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 30
},
"analyzer": { "myAnalyzer": { "type": "custom", "tokenizer": "keyword", "filter": ["edge_ngram_filter","lowercase"] } } } }, "mappings": { "search_words_type": { "properties": { "words": { "type": "string", "index": "analyzed", "indexAnalyzer" : "myAnalyzer" } } } } } }'
搜索的时候,直接使用term查询,若是比较复杂的状况下,如要按拼音、中文繁体转换等,则使用matchQuery,先对关键字进行一次分析。
QueryBuilders.termQuery("words", key);//低级查询,速度快
或者
QueryBuilders.matchQuery("words", key).analyzer("xxx");//可指定分词器来分析关键字
这种搜索结果保证必定是以..开头,由于在索引阶段就已经把term限定了。
注意:以上java代码示例都是基于spring-data-elasticsearch框架。