亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 學院 > 開發設計 > 正文

elasticsearch 實現聯想輸入搜索

2019-11-10 17:58:07
字體:
來源:轉載
供稿:網友

通常,在項目中需要聯想輸入(即輸入關鍵字,提示相關詞條,類似百度google的搜索)的需求,可能大家都是用的數據庫的like '%關鍵字%‘來實現。但是這樣實現有幾個問題。

第一、這樣的搜索無論是Oracle還是MySQL,都是無法使用索引的。在oracle中可能有全文檢索可以使用,但是個人感覺效果不是很好。

第二、輸入的關鍵字有like的通病,就是只有保含關鍵字的詞條才會被命中。如果中間加個空格之類的,db就無能為力了。

第三、如果要想對命中結果進行相關度排序,這個在常規數據庫是無法做到的。雖然,可以按照命中詞條的長度進行升序排序,但是加上排序,性能不是很好。

下面介紹一下使用elasticsearch實現聯想輸入的搜索,因為是搜索引擎,天生就不具備上面的3個問題。

在具體介紹使用方法之前,我們先找個搜索數據。我找的是ICD(就是疾病名稱的國標),誰讓咱一生都在跟他做斗爭。這個在網上一搜一堆。

有了數據,我們先要簡單描述一下我們要達到的一個目的。一般的搜索都支持漢字 和拼音兩種檢索方法。我們的這個檢索也滿足這個需求。

搜索需求描述:

1、支持漢字和簡拼兩種搜索方法。

2、輸入“高血壓”時,按照相關度,將帶“高血壓”名稱的疾病名稱按照相關度降序排序。

3、輸入“老年 高血壓”,時,將帶“老年”和“高血壓”名稱的疾病名稱按照相關度降序排序。

4、輸入拼音'gxy‘時,將拼音中帶有gxy相關的疾病按照相關度降序排序。

....

類似測試用例的需求,到此打住。

那么,我們一步一步實現這種需求。

首先,我們定義了一個ICD的類,算作我們的模型,其實沒有模型也可以,只要存入到es且知道各個field的名稱就行。這個里面我們只需要關注疾病名稱diseaseName及簡拼pinyin字段即可,這個字段默認是字符串,ES默認會幫我們分詞。

java代碼

import java.io.Serializable;  

import java.math.BigDecimal;  

/**

* ICD抽象對象

* @author donlianli@126.com

*/  

public class ICD implements Serializable{  

   PRivate static final long serialVersionUID = 6934803011248581109L;  

   //疾病ID  

   private int id;  

   //疾病編碼  

   private String code;  

   //疾病名稱  

   private String diseaseName;  

   //疾病加拼音  

   private String mergeName;  

   //漢語拼音簡拼  

   private String pinyin;  

   //是否惡心腫瘤  

   private boolean isTherioma;  

   //是否住院特殊病種  

   private boolean isSpecialDisease;  

     

   public ICD(BigDecimal id, String diseaseName, String code,  

           String pinyin, String isTherioma, String isSpecialDisease) {  

       this.id = id.intValue();  

       this.diseaseName = diseaseName;  

       this.code = code;  

       this.pinyin = pinyin;  

       if("是".equals(isTherioma)){  

           this.isTherioma = true;  

       }  

       else {  

           this.isTherioma = false;  

       }  

         

       if("是".equals(isSpecialDisease)){  

           this.isSpecialDisease = true;  

       }  

       else {  

           this.isSpecialDisease = false;  

       }  

       this.mergeName = diseaseName + "," + pinyin;  

   }  

   //set,get ......  

     

}  

第二步,將數據存儲到elasticsearch里面,我們取個名稱叫code,起個type名稱叫icd。ICD大概2w條數據,我使用默認的bulkIndex,存到es大概用了3秒。

我這里是把數據從oracle導入到elasticsearch。

Java代碼

import java.math.BigDecimal;  

import java.sql.Connection;  

import java.sql.PreparedStatement;  

import java.sql.ResultSet;  

import java.util.ArrayList;  

import java.util.List;  

 

import org.elasticsearch.action.bulk.BulkRequestBuilder;  

import org.elasticsearch.action.bulk.BulkResponse;  

import org.elasticsearch.action.index.IndexRequestBuilder;  

import org.elasticsearch.client.Client;  

 

import com.donlianli.es.ESUtils;  

import com.donlianli.es.db.DatabaseUtils;  

 

public class ICDManager {  

     

   public static void main(String[] argvs){  

       ICDManager manager = new ICDManager();  

       manager.indexDataDirect();  

   }  

   /**

    * 直接將數據初始化到ES中

    * 不創建mapping

    */  

   private void indexDataDirect() {  

       List<ICD> icdList = getIcdListFromDB();    

       System.out.println(" get icd from db finish,size:" + icdList.size());  

       bulkIndex(icdList);  

   }  

     

   private void bulkIndex(List<ICD> icdList) {  

       Client client = ESUtils.getCodeClient();  

       BulkRequestBuilder bulkRequest = client.prepareBulk();  

       long b = System.currentTimeMillis();  

       for(int i=0,l=icdList.size();i<l;i++){  

           //業務對象  

           ICD icd = icdList.get(i);  

           String json = ESUtils.toJson(icd);  

           IndexRequestBuilder indexRequest = client.prepareIndex("code","icd")  

           .setSource(json).setId(String.valueOf(icd.getId()));  

           //添加到builder中  

           bulkRequest.add(indexRequest);  

       }  

       BulkResponse bulkResponse = bulkRequest.execute().actionGet();  

       if (bulkResponse.hasFailures()) {  

           System.out.println(bulkResponse.buildFailureMessage());  

       }  

       long useTime = System.currentTimeMillis()-b;  

       System.out.println("useTime:" + useTime);  

   }  

   private List<ICD> getIcdListFromDB() {  

       Connection conn = DatabaseUtils.getOracleConnection();  

       String sql = "select * from icd_11";  

       PreparedStatement st = null;  

       ResultSet rs = null;  

       List<ICD> list = new ArrayList<ICD>();  

       try{  

           st = conn.prepareStatement(sql);  

           rs = st.executeQuery();  

           while(rs.next()){  

               BigDecimal id = rs.getBigDecimal("ID");  

               String diseaseName = rs.getString("DISEASE_NAME");  

               String code = rs.getString("CODE");  

               String pinyin = rs.getString("PINYIN");  

               String isTherioma = rs.getString("THERIOMA_FLAG");  

               String isSpecialDisease = rs.getString("OTHER_FLAG");  

                 

               list.add(new ICD(id,diseaseName,code,pinyin,isTherioma,isSpecialDisease));  

           }  

             

           return list;  

       }  

       catch(Exception e){  

           e.printStackTrace();  

       }  

       finally{  

           try{  

           if(rs!= null){  

               rs.close();  

           }  

           if(st!= null){  

               st.close();  

           }  

           conn.close();  

           }  

           catch(Exception e){  

               e.printStackTrace();  

           }  

       }  

       return null;  

   }  

}  

第三步,搜索接口,跑測試用例。

Java代碼

import org.elasticsearch.action.search.SearchResponse;  

import org.elasticsearch.client.Client;  

import org.elasticsearch.index.query.MultiMatchQueryBuilder;  

import org.elasticsearch.index.query.QueryBuilders;  

import org.elasticsearch.search.SearchHit;  

import org.elasticsearch.search.SearchHits;  

 

import com.donlianli.es.ESUtils;  

 

public class PinyinSearchTest {  

   public static void main(String[] args) {  

       Client client = ESUtils.getCodeClient();  

       String keyWord = "高血壓";  

//      String keyWord = "老年 高血壓";  

//      String keyWord = "gxy";  

       //多個字段匹配  

       MultiMatchQueryBuilder query = QueryBuilders.multiMatchQuery(keyWord, "diseaseName","pinyin");  

         

       long b = System.currentTimeMillis();  

       SearchResponse response = client.prepareSearch("code").setTypes("icd")  

               .setQuery(query)  

               .setFrom(0)  

               //前20個  

               .setSize(20)  

               .execute().actionGet();  

       long useTime = System.currentTimeMillis()-b;  

       System.out.println("search use time:" + useTime + " ms");  

         

       SearchHits shs = response.getHits();  

       for (SearchHit hit : shs) {  

           System.out.println("分數:"  

                   + hit.getScore()  

                   + ",ID:"  

                   + hit.getId()  

                   + ", 疾病名稱:"  

                   + hit.getSource().get("diseaseName")  

                   + ",拼音:" + hit.getSource().get("pinyin"));  

       }  

       client.close();  

   }  

}  

3.1,關鍵字:'高血壓'

search use time:174 ms分數:2.3859928,ID:6904, 疾病名稱:高血壓病,拼音:gxyb分數:2.136423,ID:6907, 疾病名稱:高血壓I期,拼音:gxyyq分數:2.12253,ID:6908, 疾病名稱:高血壓Ⅱ期,拼音:gxyeq分數:2.12253,ID:6910, 疾病名稱:高血壓危象,拼音:gxywx分數:2.0906634,ID:6917, 疾病名稱:腎性高血壓,拼音:sxgxy分數:2.0877438,ID:6909, 疾病名稱:高血壓Ⅲ期,拼音:gxysq分數:2.0821526,ID:18767, 疾病名稱:高原性高血壓,拼音:gyxgxy分數:1.9905697,ID:6906, 疾病名稱:惡性高血壓,拼音:exgxy分數:1.9510978,ID:7260, 疾病名稱:高血壓腦出血,拼音:gxyncx分數:1.9078629,ID:6923, 疾病名稱:腎血管性高血壓,拼音:sxgxgxy分數:1.8312198,ID:6914, 疾病名稱:高血壓性腎病,拼音:gxyxsb分數:1.8193114,ID:7367, 疾病名稱:高血壓性腦病,拼音:gxyxnb分數:1.8193114,ID:13470, 疾病名稱:妊娠引起高血壓,拼音:rsyqgxy分數:1.7919972,ID:6905, 疾病名稱:臨界性高血壓,拼音:ljxgxy分數:1.7919972,ID:6912, 疾病名稱:高血壓性心臟病,拼音:gxyxxzb分數:1.7894946,ID:6928, 疾病名稱:繼發性高血壓,拼音:jfxgxy分數:1.7062025,ID:6913, 疾病名稱:高血壓性腎衰竭,拼音:gxyxssj分數:1.7062025,ID:13485, 疾病名稱:孕產婦高血壓,拼音:ycfgxy分數:1.7062025,ID:14534, 疾病名稱:新生兒高血壓,拼音:xsegxy分數:1.7062025,ID:16181, 疾病名稱:應激性高血壓,拼音:yjxgxy

3.2關鍵字:'老年 高血壓'

search use time:144 ms分數:1.1089094,ID:6904, 疾病名稱:高血壓病,拼音:gxyb分數:0.99291986,ID:6907, 疾病名稱:高血壓I期,拼音:gxyyq分數:0.9864628,ID:6908, 疾病名稱:高血壓Ⅱ期,拼音:gxyeq分數:0.9864628,ID:6910, 疾病名稱:高血壓危象,拼音:gxywx分數:0.9716526,ID:6917, 疾病名稱:腎性高血壓,拼音:sxgxy分數:0.97029567,ID:6909, 疾病名稱:高血壓Ⅲ期,拼音:gxysq分數:0.96769714,ID:18767, 疾病名稱:高原性高血壓,拼音:gyxgxy分數:0.9251333,ID:6906, 疾病名稱:惡性高血壓,拼音:exgxy分數:0.9067884,ID:7260, 疾病名稱:高血壓腦出血,拼音:gxyncx分數:0.8866946,ID:6923, 疾病名稱:腎血管性高血壓,拼音:sxgxgxy分數:0.8510741,ID:6914, 疾病名稱:高血壓性腎病,拼音:gxyxsb分數:0.8455395,ID:7367, 疾病名稱:高血壓性腦病,拼音:gxyxnb分數:0.8455395,ID:13470, 疾病名稱:妊娠引起高血壓,拼音:rsyqgxy分數:0.8328451,ID:6905, 疾病名稱:臨界性高血壓,拼音:ljxgxy分數:0.8328451,ID:6912, 疾病名稱:高血壓性心臟病,拼音:gxyxxzb分數:0.831682,ID:6928, 疾病名稱:繼發性高血壓,拼音:jfxgxy分數:0.8074301,ID:6820, 疾病名稱:老年耳聾,拼音:lnel分數:0.80348647,ID:7612, 疾病名稱:老年痣,拼音:lnz分數:0.7929714,ID:6913, 疾病名稱:高血壓性腎衰竭,拼音:gxyxssj分數:0.7929714,ID:13485, 疾病名稱:孕產婦高血壓,拼音:ycfgxy

高血壓和老年的相關并都出來了。只可惜老年高血壓,沒有列入ICD.

3.3拼音:'gxy'

呃?怎么沒有出來?

這個問題折騰了我一天。一開始我以為是被es列入了禁用詞。后來,找到是因為沒有設置analyzer導致,在設analyzer的過程中竟然還犯了好幾個低級錯誤,導致我非常懷疑設置analyzer是否管用。

這個問題涉及到分詞,而分詞我還沒有好好研究過??傊?,在創建索引及mapping的時候,指定一個analyzer就可以解決這個問題。

創建index及mapping的代碼如下:

Java代碼

import static org.elasticsearch.common.xcontent.XContentFactory.jsonBuilder;  

 

import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;  

import org.elasticsearch.client.Client;  

import org.elasticsearch.common.settings.ImmutableSettings;  

import org.elasticsearch.common.settings.ImmutableSettings.Builder;  

import org.elasticsearch.common.xcontent.XContentBuilder;  

 

import com.donlianli.es.ESUtils;  

/**

* 創建code的mapping

* @author donlianli@126.com

*/  

public class CodeMappingTest {  

   static final String INDEX_NAME="code";  

   static final String TYPE_NAME="icd";  

     

   public static void  main(String[] argv) throws Exception{  

       Client client = ESUtils.getCodeClient();  

       Builder settings = ImmutableSettings.settingsBuilder()  

               .loadFromSource(getAnalysisSettings());  

       //首先創建索引庫  

       CreateIndexResponse  indexresponse = client.admin().indices()  

       //這個索引庫的名稱還必須不包含大寫字母  

       .prepareCreate(INDEX_NAME).setSettings(settings)  

       //這里直接添加type的mapping  

       .addMapping(TYPE_NAME, getMapping())  

       .execute().actionGet();  

         

       System.out.println("success:"+indexresponse.isAcknowledged());  

   }  

   private static String getAnalysisSettings() throws Exception {  

       XContentBuilder mapping = jsonBuilder()    

                  .startObject()    

                  //主分片數量  

                  .field("number_of_shards",5)  

                  .field("number_of_replicas",0)  

                    .startObject("analysis")    

                       .startObject("filter")  

                           //創建分詞過濾器  

                           .startObject("pynGram")  

                               .field("type","nGram")  

                               //從1開始  

                               .field("min_gram",1)  

                               .field("max_gram",15)  

                           .endObject()  

                       .endObject()      

                         

                       .startObject("analyzer")  

                               //拼音analyszer  

                               .startObject("pyAnalyzer")  

                               .field("type","custom")  

                               .field("tokenizer","standard")  

                               .field("filter", new String[]{"lowercase","pynGram"})  

                               .endObject()  

                       .endObject()      

                   .endObject()    

                 .endObject();    

       System.out.println(mapping.string());  

       return mapping.string();  

   }  

   /**

    * mapping 一旦定義,之后就不能修改。

    * @return

    * @throws Exception

    */  

   private static XContentBuilder getMapping() throws Exception{  

       XContentBuilder mapping = jsonBuilder()    

                  .startObject()    

                    .startObject("icd")    

                    //指定分詞器  

                    .field("index_analyzer","pyAnalyzer")  

                    .startObject("properties")          

                      .startObject("id")  

                           .field("type", "long")  

                           .field("store", "yes")  

                       .endObject()      

                         

                      .startObject("code")  

                           .field("type", "string")  

                           .field("store", "yes")  

                           .field("index", "analyzed")  

                       .endObject()    

                         

                        .startObject("diseaseName")  

                           .field("type", "string")  

                           .field("store", "yes")  

                           .field("index", "analyzed")  

                       .endObject()    

                         

                        .startObject("mergeName")  

                           .field("type", "string")  

                           .field("store", "yes")  

                           .field("index", "analyzed")  

                       .endObject()  

                         

                       .startObject("pinyin")  

                           .field("type", "string")  

                           .field("store", "yes")  

                           .field("index", "analyzed")  

                       .endObject()    

                         

                      .startObject("isTherioma")  

                           .field("type", "boolean")  

                           .field("store", "yes")  

                      .endObject()    

                       

                       .startObject("isSpecialDisease")  

                           .field("type", "boolean")  

                           .field("store", "yes")  

                      .endObject()    

                       

                    .endObject()    

                   .endObject()    

                 .endObject();    

       return mapping;  

   }  

(PS:其實還有一種簡單的方法,不用創建analyzer,在搜索的時候,使用'*gxy*'進行搜索也可以)

最后,我還把這個檢索跟oracle的like進行了比較。結果發現oracle只用20ms就能算出結果,而es卻用了將近100ms??梢娺@種吹捧的nosql,性能不見得比oracle強大啊,但是毋庸置疑的是,功能確實強大了。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
美女视频黄免费的亚洲男人天堂| 精品国产欧美一区二区五十路| 久久好看免费视频| 国产成人精品久久久| 最好看的2019年中文视频| 国产香蕉一区二区三区在线视频| 日本久久久久久久久久久| 亚洲香蕉成人av网站在线观看| 成人午夜在线观看| 中文字幕亚洲欧美在线| 国产美女91呻吟求| 性欧美亚洲xxxx乳在线观看| 性色av一区二区三区红粉影视| 在线a欧美视频| 91日本视频在线| 欧美理论电影在线播放| 高清欧美性猛交xxxx| 久久久久久久国产| 午夜精品久久久久久久久久久久| 国产精品夫妻激情| 国产精品777| 欧美一级淫片aaaaaaa视频| 国产日韩精品在线播放| 日本精品在线视频| 国产69精品久久久久9999| 亚洲欧洲激情在线| 国产精品久久久av| 91视频免费网站| 日韩精品亚洲元码| 亚洲大胆美女视频| 欧美成人免费视频| 亚洲成人av在线| 1769国内精品视频在线播放| www日韩中文字幕在线看| 国产盗摄xxxx视频xxx69| 欧美成人午夜影院| 搡老女人一区二区三区视频tv| 欧美激情欧美狂野欧美精品| 色综合久久精品亚洲国产| 日韩精品在线视频美女| 久久手机免费视频| 亚洲欧美激情精品一区二区| 欧美精品久久久久久久久久| 久久九九热免费视频| 日本国产欧美一区二区三区| 亚洲天堂av高清| 国产日韩精品入口| 中文字幕日韩在线视频| 国产综合视频在线观看| 亚洲国产精品热久久| 国产精品久久久久久久久久尿| 国产精品第七十二页| 青草青草久热精品视频在线观看| 久热在线中文字幕色999舞| 国产日本欧美一区二区三区在线| 久久亚洲精品成人| 狠狠躁夜夜躁人人躁婷婷91| 国产自摸综合网| 国产精品国模在线| 欧美黑人国产人伦爽爽爽| 国产亚洲欧美日韩一区二区| 国模吧一区二区三区| 久久精品成人欧美大片| 成人国产精品av| 国产成人一区二区三区电影| 亚洲永久在线观看| 欧美亚洲成人免费| 久久国产视频网站| 亚洲欧美国产一区二区三区| 国产一区二区丝袜| 成人黄色免费网站在线观看| 在线视频欧美日韩| 欧美在线精品免播放器视频| 国产精品视频yy9099| 午夜精品久久久久久久99热| 538国产精品一区二区免费视频| 欧美国产在线电影| 日韩中文字幕网址| 日韩久久免费电影| 免费av一区二区| 日韩风俗一区 二区| 91久久精品久久国产性色也91| 亚洲性夜色噜噜噜7777| 久久免费少妇高潮久久精品99| 成人在线小视频| 丁香五六月婷婷久久激情| 国产午夜精品全部视频在线播放| 国产一区二区香蕉| 日韩大陆毛片av| 中文字幕亚洲综合| 亚洲精品美女在线| 亚洲精品福利视频| 国产精品色午夜在线观看| 久久精品中文字幕电影| 国产亚洲欧洲在线| 国产不卡一区二区在线播放| 欧美成人免费网| 国产精品日韩av| 亚洲自拍av在线| 国产香蕉97碰碰久久人人| 色婷婷av一区二区三区在线观看| 亚洲四色影视在线观看| 92版电视剧仙鹤神针在线观看| 日本国产欧美一区二区三区| 日韩av电影在线网| 欧美亚洲成人网| 国产丝袜高跟一区| 亚洲欧美日韩久久久久久| 亚洲三级 欧美三级| 美日韩精品免费观看视频| 久久久国产视频| 98精品在线视频| 91久久国产精品| 日韩精品一二三四区| 国语自产偷拍精品视频偷| 国产精品美女主播| 欧美交受高潮1| 国产日本欧美一区二区三区在线| 久久久之久亚州精品露出| 另类少妇人与禽zozz0性伦| 日韩av在线影视| 91热福利电影| 国产福利视频一区二区| 91精品国产高清久久久久久| 在线观看欧美日韩国产| 日韩在线中文字| 国产视频久久久| 91在线免费看网站| 欧美日本亚洲视频| 精品成人乱色一区二区| 亚洲第一男人天堂| 日韩久久午夜影院| 亚洲人成电影在线| 中文字幕九色91在线| 久热在线中文字幕色999舞| 欧美精品福利视频| 成人久久18免费网站图片| 国产精品久久中文| 国产精品视频资源| 欧洲亚洲免费在线| 亚洲在线视频观看| 热久久这里只有| 亚洲第一页自拍| 国产精品国产亚洲伊人久久| 91chinesevideo永久地址| 欧美午夜宅男影院在线观看| 久久久精品日本| 国产91精品久久久久| 欧美日韩中文在线| 九九久久久久99精品| 国产精品网站入口| 国内外成人免费激情在线视频网站| 成人妇女淫片aaaa视频| 国产在线观看不卡| 欧美国产日韩在线| 国产精品一区二区久久精品| 亚洲欧洲自拍偷拍| 精品动漫一区二区| 国产婷婷色综合av蜜臀av| 久久99国产综合精品女同| 日韩一区视频在线| 日韩欧美在线视频| 91精品久久久久| 在线精品国产欧美|