Elasticsearch高级搜索排序( 中文+拼音+首字母+简繁转换+特殊符号过滤)

一、先摆需求:

1、中文搜索、英文搜索、中英混搜   如:“南京东路”,“cafe 南京东路店”

2、全拼搜索、首字母搜索、中文+全拼、中文+首字母混搜   如:“nanjingdonglu”,“njdl”,“南京donglu”,“南京dl”,“nang南东路”,“njd路”等等组合

3、简繁搜索、特殊符号过滤搜索   如:“龍馬”可通过“龙马”搜索,再比如 L.G.F可以通过lgf搜索,café可能通过cafe搜索

4、排序优先级为: 以关键字开头>包含关键字

二、生产效果图:

三、实现

1、索引设计

使用multi_field为搜索字段建立不同类型的索引,有全拼索引、首字母简写索引、Ngram索引以及IK索引,从各个角度分别击破,然后通过char-filter进行特殊符号与简繁转换。

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": 50
                },
                "pinyin_simple_filter":{
                    "type" : "pinyin",
                    "keep_first_letter":true,
                    "keep_separate_first_letter" : false,
                    "keep_full_pinyin" : false,
                    "keep_original" : false,
                    "limit_first_letter_length" : 50,
                    "lowercase" : true
                },
                "pinyin_full_filter":{
                    "type" : "pinyin",
                    "keep_first_letter":false,
                    "keep_separate_first_letter" : false,
                    "keep_full_pinyin" : true,
                    "none_chinese_pinyin_tokenize":true,
                    "keep_original" : false,
                    "limit_first_letter_length" : 50,
                    "lowercase" : true
                },
                "t2s_convert":{
                      "type": "stconvert",
                      "delimiter": ",",
                      "convert_type": "t2s"
               }
            },
            "char_filter" : {
                "charconvert" : {
                    "type" : "mapping",
                    "mappings_path":"char_filter_text.txt"
                }
            },
            "tokenizer":{
                "ik_smart":{
                   "type":"ik",
                   "use_smart":true
                }
            },
            "analyzer": {
                "ngramIndexAnalyzer": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter": ["edge_ngram_filter","lowercase"],
                    "char_filter" : ["charconvert"]
                },
                "ngramSearchAnalyzer": {
                    "type": "custom",
                    "tokenizer": "keyword",
                    "filter":["lowercase"],
                    "char_filter" : ["charconvert"]
                },
                "ikIndexAnalyzer": {
                    "type": "custom",
                    "tokenizer": "ik",
                    "char_filter" : ["charconvert"]
                },
                "ikSearchAnalyzer": {
                    "type": "custom",
                    "tokenizer": "ik",
                    "char_filter" : ["charconvert"]
                },
                "pinyiSimpleIndexAnalyzer":{
                    "tokenizer" : "keyword",
                    "filter": ["pinyin_simple_filter","edge_ngram_filter","lowercase"]
                },
                "pinyiSimpleSearchAnalyzer":{
                    "tokenizer" : "keyword",
                    "filter": ["pinyin_simple_filter","lowercase"]
                },
                "pinyiFullIndexAnalyzer":{
                    "tokenizer" : "keyword",
                    "filter": ["pinyin_full_filter","lowercase"]
                },
                "pinyiFullSearchAnalyzer":{
                    "tokenizer" : "keyword",
                    "filter": ["pinyin_full_filter","lowercase"]
                }
            }
       }
    },
    "mappings": {
        "search_words_type": {
            "properties": {
                "words": {
                    "type": "multi_field",
                    "fields":{
                          "words": {
                                "type": "string",
                                "index": "analyzed",
                                "indexAnalyzer" : "ngramIndexAnalyzer"
                          },
                          "SPY": {
                                "type": "string",
                                "index": "analyzed",
                                "indexAnalyzer" : "pinyiSimpleIndexAnalyzer"
                          },
                          "FPY": {
                                "type": "string",
                                "index": "analyzed",
                                "indexAnalyzer" : "pinyiFullIndexAnalyzer"
                          },
                          "IKS": {
                                "type": "string",
                                "index": "analyzed",
                                "indexAnalyzer" : "ikIndexAnalyzer"
                          }
                    }
                }
            }
        }
    }
}‘

拼音插件的使用请参考:https://github.com/medcl/elasticsearch-analysis-pinyin

2、搜索构建

以下是搜索实现代码(非完整代码,只摘录核心部分,主要是思路):

  /**
     * 纯中文搜索
     * @return
     */
    public List<Map> chineseSearch(String key,Integer cityId) throws Exception{
        DisMaxQueryBuilder  disMaxQueryBuilder=QueryBuilders.disMaxQuery();
        //以关键字开头(优先级最高)
        MatchQueryBuilder q1=QueryBuilders.matchQuery("words",key).analyzer("ngramSearchAnalyzer").boost(5);
        //完整包含经过分析过的关键字
//         boolean  whitespace=key.contains(" ");
//         int slop=whitespace?50:5;
        QueryBuilder q2=QueryBuilders.matchQuery("words.IKS", key).analyzer("ikSearchAnalyzer").minimumShouldMatch("100%");
        disMaxQueryBuilder.add(q1);
        disMaxQueryBuilder.add(q2);
        SearchQuery searchQuery=builderQuery(cityId,disMaxQueryBuilder);
        return  elasticsearchTemplate.queryForList(searchQuery,Map.class);
    }

 /**
     * 混合搜索
     * @return
     */
    public List<Map> chineseWithEnglishOrPinyinSearch(String key,Integer cityId) throws Exception{

        DisMaxQueryBuilder  disMaxQueryBuilder=QueryBuilders.disMaxQuery();
        //是否有中文开头,有则返回中文前缀
        String startChineseString=commonSearchService.getStartChineseString(key);
        /**
         * 源值搜索,不做拼音转换
         * 权重* 1.5
         */
        QueryBuilder normSearchBuilder=QueryBuilders.matchQuery("words",key).analyzer("ngramSearchAnalyzer").boost(5f);        

        /**
         * 拼音简写搜索
         * 1、分析key,转换为简写  case:  南京东路==>njdl,南京dl==>njdl,njdl==>njdl
         * 2、搜索匹配,必须完整匹配简写词干
         * 3、如果有中文前缀,则排序优先
         * 权重*1
         */
        String analysisKey=commonSearchService.anaysisKeyAndGetMaxWords(SearchIndex.INDEX_NAME_SEARCHWORDSSTATISTICS,key,"pinyiSimpleSearchAnalyzer");
        QueryBuilder pingYinSampleQueryBuilder=QueryBuilders.termQuery("words.SPY", analysisKey);

        /**
         * 拼音简写包含匹配,如 njdl可以查出 "城市公牛 南京东路店",虽然非南京东路开头
         * 权重*0.8
         */
        QueryBuilder  pingYinSampleContainQueryBuilder=null;
        if(analysisKey.length()>1){
            pingYinSampleContainQueryBuilder=QueryBuilders.wildcardQuery("words.SPY", "*"+analysisKey+"*").boost(0.8f);
        }

        /**
         * 拼音全拼搜索
         * 1、分析key,获取拼音词干   case :  南京东路==>[nan,jing,dong,lu],南京donglu==>[nan,jing,dong,lu]
         * 2、搜索查询,必须匹配所有拼音词,如南京东路,则nan,jing,dong,lu四个词干必须完全匹配
         * 3、如果有中文前缀,则排序优先
         * 权重*1
         */
        QueryBuilder pingYinFullQueryBuilder=null;
        if(key.length()>1){
            pingYinFullQueryBuilder=QueryBuilders.matchPhraseQuery("words.FPY", key).analyzer("pinyiFullSearchAnalyzer");
        }

        /**
         * 完整包含关键字查询(优先级最低,只有以上四种方式查询无结果时才考虑)
         * 权重*0.8
         */
        QueryBuilder containSearchBuilder=QueryBuilders.matchQuery("words.IKS", key).analyzer("ikSearchAnalyzer").minimumShouldMatch("100%");

        disMaxQueryBuilder
        .add(normSearchBuilder)
        .add(pingYinSampleQueryBuilder)
        .add(containSearchBuilder);

        //以下两个对性能有一定的影响,故作此判定,单个字符不执行此类搜索
        if(pingYinFullQueryBuilder!=null){
            disMaxQueryBuilder.add(pingYinFullQueryBuilder);
        }
        if(pingYinSampleContainQueryBuilder!=null){
            disMaxQueryBuilder.add(pingYinSampleContainQueryBuilder);
        }        

        QueryBuilder queryBuilder=disMaxQueryBuilder;

        //关键如果有中文,则必须包含在内容中
        if(StringUtils.isNotBlank(startChineseString)){
            queryBuilder=    QueryBuilders.filteredQuery(disMaxQueryBuilder,
                    FilterBuilders.queryFilter(QueryBuilders.queryStringQuery("*"+startChineseString+"*").field("words").analyzer("ngramSearchAnalyzer")));
            queryBuilder=QueryBuilders.functionScoreQuery(queryBuilder)
            .add(FilterBuilders.queryFilter(QueryBuilders.matchQuery("words",startChineseString).analyzer("ngramSearchAnalyzer")), ScoreFunctionBuilders.weightFactorFunction(1.5f));
        }                

        SearchQuery searchQuery=builderQuery(cityId,queryBuilder);

        return  elasticsearchTemplate.queryForList(searchQuery,Map.class);
    }    

注:以上JAVA示例代码皆以spring-data-elasticsearch框架为基础。

时间: 2024-10-31 03:30:01

Elasticsearch高级搜索排序( 中文+拼音+首字母+简繁转换+特殊符号过滤)的相关文章

ORACLE根据中文拼音首字母排序、取得中文拼音首字母函数

根据中文拼音首字母排序.取得中文拼音首字母函数     介绍根据中文的首字母.笔画.部首排序函数[NLSSORT]:           1).首字母           SELECT * FROM T_TABLE ORDER BY NLSSORT(NAME, 'NLS_SORT=SCHINESE_PINYIN_M');           2).笔画     SELECT * FROM T_TABLE ORDER BY NLSSORT(NAME, 'NLS_SORT=SCHINESE_STRO

JS获取中文拼音首字母,并通过拼音首字母快速查找页面内的中文内容

实现效果: 图一: 图二: 此例中输入的中文字符串"万万保重",有三个字是多音字,所以alert对话框中显示的是多种读音的组合: 如何实现? 如何实现通过拼音首字母快速查找页面内的中文内容呢? 过程原理是这样的:例如要对一些人名进行快速查找,当页面加载完成后,对所有人名建立一个索引,生成拼音首字母与姓名的对应关系:然后监听键盘事件,当用户按下键盘时,根据键值得到按下的是哪个字母,然后遍历索引中是否存在相同的拼音首字母: 这里还实现了根据字母组合来查找的功能,原理是这样的:当用户按键时,

JS获取中文拼音首字母,并通过拼音首字母高速查找页面内的中文内容

实现效果: 图一: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvdGVzdGNzX2Ru/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" /> 图二: 此例中输入的中文字符串"万万保重",有三个字是多音字.所以alert对话框中显示的是多种读音的组合. 怎样实现? 怎样实现通过拼音首字母高速查找页面内的中文内容呢? 过程原

根据名字拼音首字母简拼查找名字

#python3.6 from xpinyin import Pinyin #导入xpinyin库,可以把汉字转换成拼音 name_dict = dict() #设置一个保存名字和名字拼音首字母的字典,例:d={"ww":"王五"} fo=open("/home/gty/文档/data/namelist.txt", "r") # 读取文档,请读者自行更换路径 def namespy(names): p = Pinyin() s

mysql 中文字段排序( 按拼音首字母排序) 的查询语句

在处理使用Mysql时,数据表采用utf8字符集,使用中发现中文不能直接按照拼音排序 如果数据表tbl的某字段name的字符编码是latin1_swedish_ci select * from `tbl` order by birary(name) asc ; 如果数据表tbl的某字段name的字符编码是utf8_general_ci SELECT name FROM `tbl` WHERE 1 ORDER BY CONVERT( name USING gbk ) COLLATE gbk_chi

php 中文转拼音首字母问题

<?php /* 中文汉字转拼音首字母的PHP简易实现方法. 要求: 只能是GB2312码表里面中文字符 转换得到字符串对应的拼音首字母大写. 用法: echo zh2py::conv('Chinese 中华人民共和国');//Chinese ZHRMGHG 或 $py = new zh2py; echo $py->conv('Chinese 中华人民共和国');//Chinese ZHRMGHG */ class zh2py { //根据汉字区位表 //我们可以看到从16-55区之间是按拼音

【Solr】 solr对拼音搜索和拼音首字母搜索的支持

问:对于拼音和拼音首字母的支持,当你在搜商品的时候,如果想输入拼音和拼音首字母就给出商品的信息,怎么办呢? 实现方式有2种,但是他们其实是对应的.  用lucene实现 1.建索引, 多建一个索引字段,即拼音和拼音首字母这2个字段.合并的一个字段.  (拼音和拼音首字母,可以通过pinyin4j.jar.将想要进行拼音搜索的字段进行拼音转换.) 2.对输入的参数判断是否是拼音,(可以通过将传人的值转换为 utf-8 形式,如果转换后的字符串长度大于原来的字符串的长度,那么就不是拼音.否则就进行拼

利用排序规则特点计算汉字笔划和取得拼音首字母

SQL SERVER的排序规则平时使用不是很多,也许不少初学者还比较陌生,但有一个错误大家应是经常碰到: SQL SERVER数据库,在跨库多表连接查询时,若两数据库默认字符集不同,系统就会返回这样的错误:     "无法解决 equal to 操作的排序规则冲突." 一.错误分析: 这个错误是因为排序规则不一致造成的,我们做个测试,比如:create table #t1(name varchar(20) collate Albanian_CI_AI_WS, value int) cr

城市列表-根据拼音首字母排序

今天我们就简单的实现一下城市的排序 读取我们城市的信息并通过listview展示 首先看一下我们的布局文件 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layo