一、背景
搜索的智能提示是一个搜索应用的标配,主要作用是避免用户输入错误的搜索词,并将用户引导到相应的关键词上,提升用户体验。
由于中文的特点,如果搜索自动提示可以支持拼音的话会给用户带来更大的方便,免得切换输入法。
二、目标
- 基于用户的历史搜索关键字进行提示
- 同时支持汉字,拼音输入
- 支持前缀匹配,比如输入“ch”可能提示出“重庆”
- 支持缩写输入,比如输入“cq”能提示出“重庆”
- 多音字支持,比如输入“chongqing”或者“zhongqing”都能提示出“重庆”
- 输出结果,根据用户查询关键字的频率进行排序,暂时不考虑个性化需求
三、分析与解决方案
假设我们的搜索应用是基于solrcloud实现的,主要是对商家信息进行搜索,包括商家名称(store_name)、商家地址(address)。
(1). 用户每天输入大量的查询关键字,我们把查询的关键字记录下来,目前通过异步队列写入到mysql中,后期考虑写入到hbase中
(2). 用户输入的关键字可能是汉字、数字,英文,拼音,特殊字符等等,由于需要实现拼音提示,所以我们需要把汉字转换成拼音,java中考虑使用pinyin4j组件实现转换。
(3). 汉字转换拼音的过程中,顺便提取出拼音缩写,如“chongqing”,"zhongqing"--->"cq","zq"
(4). 要支持多音字提示,对查询串转换成拼音后,需要实现一个全排列组合,考虑到查询串可能比较长导致全排列比较的,具体算法需要做限制处理。
Solr Suggest实现智能提示
首先Solr作为一个应用广泛的搜索引擎系统,它内置了智能提示功能,叫做Suggest模块。该模块有两种可选方案做智能提示:
(1)、基于提示词文本做智能提示
(2)、基于索引中得某个字段建立索引词库做智能提示
suggest的配置相对简单, 下面开始写 主要是使方式, 自定义的建议词文本,放在跟solrconfig.xml同一级目录下即可
例如:solr\solr_home\collections\collection1\conf\suggest.txt
下面给出suggest在solrconfig.xml里配置的代码( 中文信息注意删去 ) 配置如下:
      <searchComponent name="suggest" 
      
        class
      
      ="solr.SpellCheckComponent">  
    <str name="queryAnalyzerFieldType">string</str>  
    <lst name="spellchecker">    
        <str name="name">suggest</str>    
        <str name="classname">org.apache.solr.spelling.suggest.Suggester</str>    
        <str name="lookupImpl">org.apache.solr.spelling.suggest.tst.TSTLookup</str>    
        <str name="field">my_word</str>  
        <
      
        float
      
       name="threshold">0.0001</
      
        float
      
      > 
        <!--
      
         使用自定义suggest词库词--> 
        
      
      <str name="sourceLocation">suggest.txt</str> 
        <str name="spellcheckIndexDir">spellchecker</str>
        
      
              <str name="comparatorClass">freq</str>  
        <str name="buildOnOptimize">
      
        true
      
      </str>  
        <str name="buildOnCommit">
      
        true
      
      </str>       
    </lst>    
  </searchComponent>      
   
  <requestHandler name="/suggest" 
      
        class
      
      ="org.apache.solr.handler.component.SearchHandler">    
    <lst name="defaults">    
        <str name="spellcheck">
      
        true
      
      </str>    
        <str name="spellcheck.dictionary">suggest</str>    
        <str name="spellcheck.count">10</str>    
        <str name="spellcheck.onlyMorePopular">
      
        true
      
      </str>  
        <str name="spellcheck.extendedResults">
      
        false
      
      </str>        
        <str name="spellcheck.collate">
      
        true
      
      </str>  
        <!--<str name="spellcheck.build">
      
        true
      
      </str>  -->    
    </lst>    
    <arr name="components">    
        <str>suggest</str>    
    </arr>    
  </requestHandler>
    
  说明:
1.solr的suggest基于solr.SpellCheckComponent
2.queryAnalyzerFieldType 参数为string,在这不要定义复杂分词,如果是根据某一个索引字段,意义不大
3.field字段名,表示基于schema中的某一个索引字段
4.threshold限制一些不常用的词出现,值越大过滤纸越多
5.sourceLocation用于设置字典,如果有一个字典能记录用户常搜索的字, 更灵活的控制,可以存放一些高质量的 query短语
6.spellcheckIndexDir如果已经设置spellcheck,那么可以在此制定目录
7.字典格式如下
# This is a sample dictionary file.
acquire
accidentally\t2.0
accommodate\t3.0
文本格式utf-8,#开头表示注释,被忽略
每一个词一行,后面带权重
8.配置词典后在requestHandler中设置spellcheck.onlyMorePopular为true,可以根据权重排序
9.spellcheck.count返回行
配置完成重启服务后,设置参数suggest/?spellcheck.build=true来创建spellchecker的索引
然后输入:http://ip:port/corename/suggest?q=xxx进行搜索了
接下来就是前台js实现的问题了。
当然也可以通过solrj来进行搜索
      CommonsHttpSolrServer server = 
      
        new
      
      
         CommonsHttpSolrServer(
                
      
      "http://ip:port/corename/"
      
        );
    SolrQuery params 
      
      = 
      
        new
      
      
         SolrQuery();
        String token 
      
      = "Solr"
      
        ;
        params.set(
      
      "qt", "/suggest"
      
        );
        params.set(
      
      "q"
      
        , token);
        params.set(
      
      "spellcheck.build", "true"
      
        );
        QueryResponse response 
      
      = 
      
        null
      
      
        ;
        
      
      
        try
      
      
         {
            response 
      
      =
      
         server.query(params);
            System.out.println(
      
      "查询耗时:" +
      
         response.getQTime());
        } 
      
      
        catch
      
      
         (SolrServerException e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } 
      
      
        catch
      
      
         (Exception e) {
            System.err.println(e.getMessage());
            e.printStackTrace();
        } 
      
      
        finally
      
      
         {
        } 
        SpellCheckResponse spellCheckResponse 
      
      =
      
         response
                .getSpellCheckResponse();
        
      
      
        if
      
       (spellCheckResponse != 
      
        null
      
      
        ) {
            List
      
      <Suggestion> suggestionList =
      
         spellCheckResponse
                    .getSuggestions();
            
      
      
        for
      
      
         (Suggestion suggestion : suggestionList) {
                System.out.println(
      
      "Suggestions NumFound: "
                        +
      
         suggestion.getNumFound());
                System.out.println(
      
      "Token: " +
      
         suggestion.getToken());
                System.out.print(
      
      "Suggested: "
      
        );
                List
      
      <String> suggestedWordList =
      
         suggestion.getAlternatives();
                
      
      
        for
      
      
         (String word : suggestedWordList) {
                    System.out.println(word 
      
      + ", "
      
        );
                }
                System.out.println();
            }
        }
      
    
  
-----OK


 
					 
					