国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 開發 > Java > 正文

Spring Boot2.0整合ES5實現文章內容搜索實戰

2024-07-13 10:15:52
字體:
來源:轉載
供稿:網友

一、文章內容搜索思路

上一篇講了在怎么在 Spring Boot 2.0 上整合 ES 5 ,這一篇聊聊具體實戰。簡單講下如何實現文章、問答這些內容搜索的具體實現。實現思路很簡單:

  1. 基于「短語匹配」并設置最小匹配權重值
  2. 哪來的短語,利用 IK 分詞器分詞
  3. 基于 Fiter 實現篩選
  4. 基于 Pageable 實現分頁排序

這里直接調用搜索的話,容易搜出不盡人意的東西。因為內容搜索關注內容的連接性。所以這里處理方法比較 low ,希望多交流一起實現更好的搜索方法。就是通過分詞得到很多短語,然后利用短語進行短語精準匹配。

ES 安裝 IK 分詞器插件很簡單。第一步,在下載對應版本 https://github.com/medcl/elasticsearch-analysis-ik/releases。第二步,在 elasticsearch-5.5.3/plugins 目錄下,新建一個文件夾 ik,把 elasticsearch-analysis-ik-5.5.3.zip 解壓后的文件拷貝到 elasticsearch-5.1.1/plugins/ik 目錄下。最后重啟 ES 即可。

二、搜索內容分詞

安裝好 IK ,如何調用呢?

第一步,我這邊搜搜內容會以 逗號 拼接傳入。所以會先將逗號分割

第二步,在搜索詞中加入自己本身,因為有些詞經過 ik 分詞后就沒了... 這是個 bug

第三步,利用 AnalyzeRequestBuilder 對象獲取 IK 分詞后的返回值對象列表

第四步,優化分詞結果,比如都為詞,則保留全部;有詞有字,則保留詞;只有字,則保留字

核心實現代碼如下:

  /**   * 搜索內容分詞   */  protected List<String> handlingSearchContent(String searchContent) {    List<String> searchTermResultList = new ArrayList<>();    // 按逗號分割,獲取搜索詞列表    List<String> searchTermList = Arrays.asList(searchContent.split(SearchConstant.STRING_TOKEN_SPLIT));    // 如果搜索詞大于 1 個字,則經過 IK 分詞器獲取分詞結果列表    searchTermList.forEach(searchTerm -> {      // 搜索詞 TAG 本身加入搜索詞列表,并解決 will 這種問題      searchTermResultList.add(searchTerm);      // 獲取搜索詞 IK 分詞列表      searchTermResultList.addAll(getIkAnalyzeSearchTerms(searchTerm));    });    return searchTermResultList;  }  /**   * 調用 ES 獲取 IK 分詞后結果   */  protected List<String> getIkAnalyzeSearchTerms(String searchContent) {    AnalyzeRequestBuilder ikRequest = new AnalyzeRequestBuilder(elasticsearchTemplate.getClient(),        AnalyzeAction.INSTANCE, SearchConstant.INDEX_NAME, searchContent);    ikRequest.setTokenizer(SearchConstant.TOKENIZER_IK_MAX);    List<AnalyzeResponse.AnalyzeToken> ikTokenList = ikRequest.execute().actionGet().getTokens();    // 循環賦值    List<String> searchTermList = new ArrayList<>();    ikTokenList.forEach(ikToken -> {      searchTermList.add(ikToken.getTerm());    });    return handlingIkResultTerms(searchTermList);  }  /**   * 如果分詞結果:洗發水(洗發、發水、洗、發、水)   * - 均為詞,保留   * - 詞 + 字,只保留詞   * - 均為字,保留字   */  private List<String> handlingIkResultTerms(List<String> searchTermList) {    Boolean isPhrase = false;    Boolean isWord = false;    for (String term : searchTermList) {      if (term.length() > SearchConstant.SEARCH_TERM_LENGTH) {        isPhrase = true;      } else {        isWord = true;      }    }    if (isWord & isPhrase) {      List<String> phraseList = new ArrayList<>();      searchTermList.forEach(term -> {        if (term.length() > SearchConstant.SEARCH_TERM_LENGTH) {          phraseList.add(term);        }      });      return phraseList;    }    return searchTermList;  }

三、搜索查詢語句

構造內容枚舉對象,羅列需要搜索的字段,ContentSearchTermEnum 代碼如下:

import lombok.AllArgsConstructor;@AllArgsConstructorpublic enum ContentSearchTermEnum {  // 標題  TITLE("title"),  // 內容  CONTENT("content");  /**   * 搜索字段   */  private String name;  public String getName() {    return name;  }  public void setName(String name) {    this.name = name;  }}

循環進行「短語搜索匹配」搜索字段,然后并設置最低權重值為 1。核心代碼如下:

  /**   * 構造查詢條件   */  private void buildMatchQuery(BoolQueryBuilder queryBuilder, List<String> searchTermList) {    for (String searchTerm : searchTermList) {      for (ContentSearchTermEnum searchTermEnum : ContentSearchTermEnum.values()) {        queryBuilder.should(QueryBuilders.matchPhraseQuery(searchTermEnum.getName(), searchTerm));      }    }    queryBuilder.minimumShouldMatch(SearchConstant.MINIMUM_SHOULD_MATCH);  }

四、篩選條件

搜到東西不止,有時候需求是這樣的。需要在某個品類下搜索,比如電商需要在某個 品牌 下搜索商品。那么需要構造一些 fitler 進行篩選。對應 SQL 語句的 Where 下的 OR 和 AND 兩種語句。在 ES 中使用 filter 方法添加過濾。代碼如下:

  /**   * 構建篩選條件   */  private void buildFilterQuery(BoolQueryBuilder boolQueryBuilder, Integer type, String category) {    // 內容類型篩選    if (type != null) {      BoolQueryBuilder typeFilterBuilder = QueryBuilders.boolQuery();      typeFilterBuilder.should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME, type).lenient(true));      boolQueryBuilder.filter(typeFilterBuilder);    }    // 內容類別篩選    if (!StringUtils.isEmpty(category)) {      BoolQueryBuilder categoryFilterBuilder = QueryBuilders.boolQuery();      categoryFilterBuilder.should(QueryBuilders.matchQuery(SearchConstant.CATEGORY_NAME, category).lenient(true));      boolQueryBuilder.filter(categoryFilterBuilder);    }  }

type 是大類,category 是小類,這樣就可以支持 大小類 篩選。但是如果需要在 type = 1 或者 type = 2 中搜索呢?具體實現代碼很簡單:

typeFilterBuilder  .should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME, 1)  .should(QueryBuilders.matchQuery(SearchConstant.TYPE_NAME, 2)  .lenient(true));

通過鏈式表達式,兩個 should 實現或,即 SQL 對應的 OR 語句。通過兩個 BoolQueryBuilder 實現與,即 SQL 對應的 AND 語句。

五、分頁、排序條件

分頁排序代碼就很簡單了:

 @Override  public PageBean searchContent(ContentSearchBean contentSearchBean) {    Integer pageNumber = contentSearchBean.getPageNumber();    Integer pageSize = contentSearchBean.getPageSize();    PageBean<ContentEntity> resultPageBean = new PageBean<>();    resultPageBean.setPageNumber(pageNumber);    resultPageBean.setPageSize(pageSize);    // 構建搜索短語    String searchContent = contentSearchBean.getSearchContent();    List<String> searchTermList = handlingSearchContent(searchContent);    // 構建查詢條件    BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();    buildMatchQuery(boolQueryBuilder, searchTermList);    // 構建篩選條件    buildFilterQuery(boolQueryBuilder, contentSearchBean.getType(), contentSearchBean.getCategory());    // 構建分頁、排序條件    Pageable pageable = PageRequest.of(pageNumber, pageSize);    if (!StringUtils.isEmpty(contentSearchBean.getOrderName())) {      pageable = PageRequest.of(pageNumber, pageSize, Sort.Direction.DESC, contentSearchBean.getOrderName());    }    SearchQuery searchQuery = new NativeSearchQueryBuilder().withPageable(pageable)        .withQuery(boolQueryBuilder).build();    // 搜索    LOGGER.info("/n ContentServiceImpl.searchContent() [" + searchContent        + "] /n DSL = /n " + searchQuery.getQuery().toString());    Page<ContentEntity> contentPage = contentRepository.search(searchQuery);    resultPageBean.setResult(contentPage.getContent());    resultPageBean.setTotalCount((int) contentPage.getTotalElements());    resultPageBean.setTotalPage((int) contentPage.getTotalElements() / resultPageBean.getPageSize() + 1);    return resultPageBean;  }

利用 Pageable 對象,構造分頁參數以及指定對應的 排序字段、排序順序(DESC ASC)即可。

六、小結

這個思路比較簡單。希望對大家的學習有所幫助,也希望大家多多支持VeVb武林網。


注:相關教程知識閱讀請移步到JAVA教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 湛江市| 普兰县| 铅山县| 襄城县| 温泉县| 漠河县| 安新县| 淮南市| 达尔| 仙居县| 乐都县| 霍城县| 泾川县| 深州市| 南丰县| 越西县| 略阳县| 历史| 葵青区| 仙居县| 静安区| 四平市| 乌恰县| 玛多县| 来宾市| 如东县| 德庆县| 乌兰察布市| 平乡县| 溆浦县| 紫云| 长垣县| 石嘴山市| 平顶山市| 嘉义县| 钟山县| 武宁县| 岐山县| 邹平县| 甘泉县| 宁德市|