一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - Spring Boot2.0整合ES5實現文章內容搜索實戰

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

2021-03-14 12:15泥瓦匠BYSocket Java教程

這篇文章主要介紹了Spring Boot2.0整合ES5實現文章內容搜索實戰,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

一、文章內容搜索思路

上一篇講了在怎么在 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 分詞后的返回值對象列表

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

核心實現代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
/**
 * 搜索內容分詞
 */
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 代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import lombok.AllArgsConstructor;
@AllArgsConstructor
public enum ContentSearchTermEnum {
  // 標題
  TITLE("title"),
  // 內容
  CONTENT("content");
 
  /**
   * 搜索字段
   */
  private String name;
 
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
}

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

?
1
2
3
4
5
6
7
8
9
10
11
/**
 * 構造查詢條件
 */
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 方法添加過濾。代碼如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/**
 * 構建篩選條件
 */
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 中搜索呢?具體實現代碼很簡單:

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

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

五、分頁、排序條件

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

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@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)即可。

六、小結

這個思路比較簡單。希望對大家的學習有所幫助,也希望大家多多支持服務器之家。

原文鏈接:https://zhuanlan.zhihu.com/p/32562131

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 97精品国产自在现线免费观看 | 亚洲精品在线免费 | 亚洲AV永久无码精品澳门 | 色综合伊人色综合网亚洲欧洲 | bbbxxx乱大交欧美小说 | 国产男人搡女人免费视频 | 吉泽明步高清无码中文 | 秀逼逼 | 国产精品久久久久久影院 | 国产一区二区精品久 | 国产91网站在线观看 | 精品久久久久久久久久香蕉 | 胖女性大bbbbbb| 熟睡中的麻麻大白屁股小说 | 欧美三级不卡在线观线看高清 | 婷婷综合久久 | 天堂男人在线 | 成人福利 | 啊啊啊好大好爽视频 | 亚洲人成绝费网站色ww | 小女生RAPPER入口 | 四虎影视4hu最新地址在线884 | 性色香蕉AV久久久天天网 | 肉大捧一进一出视频免费播放 | 奇米色88欧美一区二区 | 慢慢娇淫 | 调教全程肉动画片在线观看 | 午夜一级免费视频 | 亚洲欧美日韩天堂在线观看 | 免费视频精品一区二区 | 色综合欧美色综合七久久 | 久久精品国产色蜜蜜麻豆国语版 | 欧美最新在线 | 精品一卡2卡3卡4卡5卡亚洲 | 国产东北三老头伦一肥婆 | 日韩国产欧美精品综合二区 | 激情涩涩| 99精品在线视频 | 国产精品一区三区 | 欧美伊香蕉久久综合类网站 | 国产福利微拍精品一区二区 |