Elasticsearch 是一個使用 Java 語言編寫、遵守 Apache 協議、支持 RESTful 風格的分布式全文搜索和分析引擎,它基于 Lucene 庫構建,并提供多種語言的 API。Elasticsearch 可以對任何類型的數據進行索引、查詢和聚合分析,無論是文本、數字、地理空間、結構化還是非結構化的。
Elasticsearch 的核心功能是搜索,它可以對數據進行分詞匹配、相關性評分、高亮顯示等操作,返回相關度高的結果列表。Elasticsearch 也可以用作數據分析,它可以對數據進行統計、分類、聚類等操作,返回聚合結果或圖表。
本文將用我開源的 waynboot-mall 項目作于代碼講解,Elasticsearch 版本是 7.10.1。
waynboot-mall 是一套全部開源的微商城項目,包含三個項目:運營后臺、H5 商城和后端接口。實現了一套完整的商城業務,有首頁展示、商品分類、商品詳情、sku 詳情、商品搜索、加入購物車、結算下單、支付寶/微信支付、訂單列表、商品評論等一系列功能。
本文大綱如下,
圖片
Elasticsearch 的典型應用場景有以下幾種:
waynboot-mall 商城選擇使用 Elasticsearch 作為搜索引擎,負責對商品數據進行索引和檢索,選擇 Elasticsearch 的原因有以下幾點:
Elasticsearch 的插件非常豐富,我給大家介紹其中 waynboot 項目使用的 Elasticsearch 插件。
IK Analyzer 是一個開源的中文分詞器,由阿里巴巴集團發布。它采用了細粒度切分和歧義處理等技術,能夠較好地處理各種中文文本。IK Analyzer 支持普通模式、搜索模式和拼音模式三種分詞方式,并可以根據需要自定義字典。
Pinyin Analyzer 插件是一個用于將中文字符轉換為拼音的插件,它集成了 NLP 工具(nlp-lang)。該插件包含了分析器:pinyin,分詞器:pinyin 和 token-filter:pinyin。該插件還提供了一些可選的參數,可以控制拼音的輸出格式,例如是否保留首字母,是否保留全拼,是否保留非中文字符等。
在 waynboot-mall 項目中,給 Elasticsearch 定義了專門的數據訪問層 waynboot-data-elastic,該層目錄結構如下:
|-- waynboot-data // 數據訪問層 | |-- waynboot-data-elastic // Elasticsearch訪問配置模塊 | |-- config | |-- constant | |-- mananger
包目錄說明如下:
在 waynboot-mall 項目中,Elasticsearch 主要用于支持首頁商品的分詞搜索、分頁排序等功能。Elasticsearch 版本是 7.0,以下實戰講解都是在 7.0 版本基礎上進行。
要使用 Elasticsearch ik 分詞器進行中文分詞搜索,首先需要安裝相應的插件 elasticsearch-analysis-ik,然后在創建索引時指定使用中文分詞器作為字段的 analyzer 屬性。
在日常對 Elasticsearch 的操作中,我們可以通過 rest api 的方式進行操作。
如下我們可以創建一個索引名稱為 goods,包含兩個屬性 title、content。并且 這兩個屬性都使用 ik 分詞器。注意這里我用的 Elasticsearch 提供 Rest api 方式創建索引。
PUT /goods { "settings": { "index": { "number_of_shards": 1, "number_of_replicas": 0 } }, "mappings": { "properties": { "title": { "type": "text", "analyzer": "ik_max_word" }, "content": { "type": "text", "analyzer": "ik_max_word" } } } }
創建索引后,就可以向索引中添加兩條數據,例如:
POST /books/_doc/1 { "title": "格林童話", "content": "這本書介紹了很多童話故事,有白雪公主、獅子王、美人魚等。" } POST /books/_doc/2 { "title": "中國童話故事", "content": "這本書介紹了很多中國童話故事。" }
然后我們就可以使用 match 語法來進行中文分詞檢索,這里我查詢 goods 索引中,title 屬性是 "動畫" 的記錄。如下:
GET /books/_search { "query":{ "match":{ "title": "童話" } } }
查詢結果如下:
{ "took": 0, "timed_out": false, "_shards": { "total": 1, "successful": 1, "skipped": 0, "failed": 0 }, "hits": { "total": { "value": 2, "relation": "eq" }, "max_score": 0.11190013, "hits": [ { "_index": "books", "_type": "_doc", "_id": "1", "_score": 0.11190013, "_source": { "title": "格林童話", "content": "這本書介紹了很多童話故事,有白雪公主、獅子王、美人魚等。" } }, { "_index": "books", "_type": "_doc", "_id": "2", "_score": 0.099543065, "_source": { "title": "中國童話故事", "content": "這本書介紹了很多中國童話故事。" } } ] } }
可以看到,查詢結果中匹配了標題包含“童話”的文檔,這說明 Elasticsearch 使用了中文分詞器對查詢字符串和文檔進行了分詞,并根據相關性得分返回了結果。
在 waynboot-mall 項目中,商城首頁頂部提供了商品搜索欄,用戶可以輸入商品名稱搜索自己想要的商品,搜索結果展示后,還可以進行熱門、新品過濾以及價格、銷量等進行排序。
圖片
可以看到搜索功能還是比較復雜的,在 waynboot-mall 項目中,這些邏輯全部在 Elasticsearch 內部進行處理,代碼如下:
@RestController @AllArgsConstructor @RequestMapping("search") public class SearchController extends BaseController { private IGoodsService iGoodsService; private ElasticDocument elasticDocument; @GetMapping("result") public R result(SearchVO searchVO) throws IOException { // 獲取篩選、排序條件 Long memberId = MobileSecurityUtils.getUserId(); String keyword = searchVO.getKeyword(); Boolean filterNew = searchVO.getFilterNew(); Boolean filterHot = searchVO.getFilterHot(); Boolean isNew = searchVO.getIsNew(); Boolean isHot = searchVO.getIsHot(); Boolean isPrice = searchVO.getIsPrice(); Boolean isSales = searchVO.getIsSales(); String orderBy = searchVO.getOrderBy(); SearchHistory searchHistory = new SearchHistory(); if (memberId != null && StringUtils.isNotEmpty(keyword)) { searchHistory.setCreateTime(LocalDateTime.now()); searchHistory.setUserId(memberId); searchHistory.setKeyword(keyword); } Page<SearchVO> page = getPage(); // 查詢包含關鍵字、已上架商品 SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder(); BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); MatchQueryBuilder matchFiler = QueryBuilders.matchQuery("isOnSale", true); MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("name", keyword); MatchPhraseQueryBuilder matchPhraseQueryBuilder = QueryBuilders.matchPhraseQuery("keyword", keyword); boolQueryBuilder.filter(matchFiler).should(matchQuery).should(matchPhraseQueryBuilder).minimumShouldMatch(1); searchSourceBuilder.timeout(new TimeValue(10, TimeUnit.SECONDS)); // 按是否新品排序 if (isNew) { searchSourceBuilder.sort(new FieldSortBuilder("isNew").order(SortOrder.DESC)); } // 按是否熱品排序 if (isHot) { searchSourceBuilder.sort(new FieldSortBuilder("isHot").order(SortOrder.DESC)); } // 按價格高低排序 if (isPrice) { searchSourceBuilder.sort(new FieldSortBuilder("retailPrice").order("asc".equals(orderBy) ? SortOrder.ASC : SortOrder.DESC)); } // 按銷量排序 if (isSales) { searchSourceBuilder.sort(new FieldSortBuilder("sales").order(SortOrder.DESC)); } // 篩選新品 if (filterNew) { MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isNew", true); boolQueryBuilder.filter(filterQuery); } // 篩選熱品 if (filterHot) { MatchQueryBuilder filterQuery = QueryBuilders.matchQuery("isHot", true); boolQueryBuilder.filter(filterQuery); } // 組裝Elasticsearch查詢條件 searchSourceBuilder.query(boolQueryBuilder); // Elasticsearch分頁相關 searchSourceBuilder.from((int) (page.getCurrent() - 1) * (int) page.getSize()); searchSourceBuilder.size((int) page.getSize()); // 執行Elasticsearch查詢 List<JSONObject> list = elasticDocument.search("goods", searchSourceBuilder, JSONObject.class); List<Integer> goodsIdList = list.stream().map(jsonObject -> (Integer) jsonObject.get("id")).collect(Collectors.toList()); if (goodsIdList.isEmpty()) { return R.success().add("goods", Collections.emptyList()); } // 根據Elasticsearch中返回商品ID查詢商品詳情并保持es中的排序 List<Goods> goodsList = iGoodsService.searchResult(goodsIdList); Map<Integer, Goods> goodsMap = goodsList.stream().collect(Collectors.toMap(goods -> Math.toIntExact(goods.getId()), o -> o)); List<Goods> returnGoodsList = new ArrayList<>(goodsList.size()); for (Integer goodsId : goodsIdList) { returnGoodsList.add(goodsMap.get(goodsId)); } if (CollectionUtils.isNotEmpty(goodsList)) { AsyncManager.me().execute(new TimerTask() { @Override public void run() { searchHistory.setHasGoods(true); iSearchHistoryService.save(searchHistory); } }); } return R.success().add("goods", returnGoodsList); } }
這里對上面商城的搜索代碼給大家做一個講解:
本文給大家講解了 waynboot-mall 項目中對于 elasticsearch 的使用以及代碼實戰講解。希望能幫助大家更好理解 elasticsearch,大家在自己的項目中如果要引入 elasticsearch,可以直接參照本文的示例代碼即可使用。
本文鏈接:http://www.www897cc.com/showinfo-26-75299-0.htmlElasticsearch使用實戰以及代碼詳解
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: MediatR讓進程內通信如此簡單,基于MediatR實現事件訂閱發布功能
下一篇: Python中容易被忽視的核心功能