商品中心—10.商品B端搜索系統的說明文檔

大綱

1.商品B端搜索系統的運行流程 + 緩存和索引設計

2.商品B端搜索系統監聽數據變更與寫入ES索引

3.商品B端搜索系統的歷史搜索詞的實現

4.商品B端搜索系統的搜索詞補全的實現

5.商品B端搜索系統的搜索接口實現

6.索引重建

1.商品B端搜索系統的運行流程 + 緩存和索引設計

(1)運行流程

(2)Redis緩存設計

(3)索引設計

(1)運行流程

(2)Redis緩存設計

使用Redis緩存用戶的搜索詞記錄,比如保存最近的10個搜索詞記錄,使?的數據結構:list。

key的格式:history_search_words:{userId}
value的格式:["?奶", "雞蛋", "??鳳?", "正?蒸餃"]

(3)索引設計

一.商品索引

二.索引字段說明

三.數據示例

四.搜索補全索引

一.商品索引

PUT /sku_info_index
{"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings": {"properties": {"skuName": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"basePrice": {"type": "integer"},"vipPrice": {"type": "integer"},"brandId": {"type": "keyword"},"brandName": {"type": "keyword"},"saleCount": {"type": "integer"},"createTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"},"updateTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"}}}
}

二.索引字段說明

三.數據示例

{"_index": "sku_info_index","_type": "_doc","_id": "8000177337","_score": 1.0,"_source": {"skuName": "Apple iPhone 13 Pro Max 256GB 蒼嶺綠? ?持移動聯通電信5G 雙卡雙待?機","brandName": "蘋果","createTime": "2022-03-12 08:24:57","brandId": 4,"vipPrice": 9799,"updateTime": "2022-03-12 08:24:57","basePrice": 9999}
}

四.搜索補全索引

put /completion_word_index
{"settings": {"number_of_shards": 3,"number_of_replicas": 1,"analysis": {"analyzer": {"ik_and_pinyin_analyzer": {"type": "custom","tokenizer": "ik_smart","filter": "my_pinyin"}},"filter": {"my_pinyin": {"type": "pinyin","keep_first_letter": true,"keep_full_pinyin": true,"keep_original": true,"remove_duplicated_term": true}}}},"mappings": {"properties": {"completion_word": {"type": "completion","analyzer": "ik_and_pinyin_analyzer"}}}
}

2.商品B端搜索系統監聽數據變更與寫入ES索引

(1)消息處理系統添加數據監聽配置

(2)商品B端搜索系統下的數據變更消息消費者

(3)sku表變更消息處理器

(4)item表變更消息處理器

(1)消息處理系統添加數據監聽配置

一.data_change_listen_config表

INSERT INTO data_change_listen_config (id, table_name, key_column, filter_flag, del_flag, create_user, create_time, update_user, update_time) 
VALUES (1, 'sku_info', 'sku_id', 1, 1, 0, '2022-02-25 13:42:28', 0, '2022-02-25 13:42:28');
INSERT INTO data_change_listen_config (id, table_name, key_column, filter_flag, del_flag, create_user, create_time, update_user, update_time) 
VALUES (2, 'item_info', 'item_id', 1, 1, 0, '2022-02-25 13:42:28', 0, '2022-02-25 13:42:28');

二.data_change_column_config表

INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (1, 1, 'sku_name', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');
INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (2, 1, 'channel', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');
INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (3, 1, 'features', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');
INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (4, 1, 'vip_price', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');
INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (5, 1, 'base_price', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');
INSERT INTO data_change_column_config (id, listen_id, listen_column, del_flag, create_user, create_time, update_user, update_time)
VALUES (6, 2, 'brand_id', 1, 0, '2022-02-25 13:43:28', 0, '2022-02-25 13:43:28');

三.data_change_message_config表

INSERT INTO data_change_message_config (id, listen_id, notify_column, message_topic, delay_level, message_type, del_flag, create_user, create_time, update_user, update_time)
VALUES (1, 1, 'id,sku_id', 'product_update_topic', 3, 1, 1, 0, '2022-02-25 13:45:24', 0, '2022-02-25 13:45:24');
INSERT INTO data_change_message_config (id, listen_id, notify_column, message_topic, delay_level, message_type, del_flag, create_user, create_time, update_user, update_time)
VALUES (3, 2, 'id,item_id', 'product_update_topic', 3, 1, 1, 0, '2022-02-25 13:45:24', 0, '2022-02-25 13:45:24');

(2)商品B端搜索系統下的數據變更消息消費者

@Configuration
public class ConsumerBeanConfig {//配置內容對象@Autowiredprivate RocketMQProperties rocketMQProperties;//監聽商品修改的MQ消息@Bean("productUpdateTopic")public DefaultMQPushConsumer productUpdateTopic(ProductUpdateListener productUpdateListener) throws MQClientException {DefaultMQPushConsumer consumer = new DefaultMQPushConsumer(RocketMqConstant.PRODUCT_UPDATE_CONSUMER_GROUP);consumer.setNamesrvAddr(rocketMQProperties.getNameServer());consumer.subscribe(RocketMqConstant.PRODUCT_UPDATE_TOPIC, "*");consumer.registerMessageListener(productUpdateListener);consumer.start();return consumer;}
}//搜索模塊在商品變更的時候更新商品索引
@Component
public class ProductUpdateListener implements MessageListenerConcurrently {@Autowiredprivate MessageHandlerManager messageHandlerManager;@Overridepublic ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> list, ConsumeConcurrentlyContext consumeConcurrentlyContext) {try {for (MessageExt messageExt : list) {String msg = new String(messageExt.getBody());log.info("執行商品索引數據更新邏輯,消息內容:{}", msg);TableDataChangeDTO tableDataChangeDTO = JsonUtil.json2Object(msg, TableDataChangeDTO.class);//處理消息messageHandlerManager.handleMessage(tableDataChangeDTO);}} catch (Exception e){log.error("consume error, 商品索引數據更新失敗", e);//本次消費失敗,下次重新消費return ConsumeConcurrentlyStatus.RECONSUME_LATER;}return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;}
}@Component
public class MessageHandlerManager {//繼承了MessageHandler的ItemInfoTableMessageHandler和SkuInfoTableMessageHandler都會被注入到這里@Autowiredprivate List<MessageHandler> messageHandlers;public void handleMessage(TableDataChangeDTO tableDataChangeDTO) throws IOException {MessageHandler messageHandlerToUse = messageHandlers.stream().filter(e -> StringUtils.equals(e.tableName(), tableDataChangeDTO.getTableName())).findFirst().orElse(null);if (messageHandlerToUse == null) {return;}messageHandlerToUse.handleMessage(tableDataChangeDTO);}
}

(3)sku表變更消息處理器

@Component
public class SkuInfoTableMessageHandler implements MessageHandler {@Autowiredprivate ProductSearchRepository productSearchRepository;@Overridepublic String tableName() {return "sku_info";}@Overridepublic void handleMessage(TableDataChangeDTO tableDataChangeDTO) throws IOException {String skuId = String.valueOf(tableDataChangeDTO.getKeyId());//到數據庫查詢索引相關的信息ProductSearchDO productSearchDO = productSearchRepository.queryProductSearchInfo(skuId);//保存索引數據到ESproductSearchRepository.saveProductSearchInfos(Collections.singletonList(productSearchDO));}
}@Repository
public class ProductSearchRepository {private static final String SKU_INFO_INDEX = "sku_info_index";@Autowiredprivate RestHighLevelClient restHighLevelClient;@Autowiredprivate SkuInfoMapper skuInfoMapper;//根據skuId查詢和商品索引相關的信息public ProductSearchDO queryProductSearchInfo(String skuId) {return skuInfoMapper.queryProductSearchInfo(skuId);}//批量保存商品索引數據public void saveProductSearchInfos(List<ProductSearchDO> productSearchDOS) throws IOException {BulkRequest bulkRequest = new BulkRequest();for (ProductSearchDO productSearchDO : productSearchDOS) {Map<String, Object> jsonMap = new HashMap<>();jsonMap.put("skuName", productSearchDO.getSkuName());jsonMap.put("basePrice", productSearchDO.getBasePrice());jsonMap.put("vipPrice", productSearchDO.getVipPrice());jsonMap.put("brandId", productSearchDO.getBrandId());jsonMap.put("brandName", productSearchDO.getBrandName());jsonMap.put("createTime", new Date());jsonMap.put("updateTime", new Date());IndexRequest indexRequest = new IndexRequest(SKU_INFO_INDEX).id(productSearchDO.getSkuId()).source(jsonMap);bulkRequest.add(indexRequest);}restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);}...
}

(4)item表變更消息處理器

@Component
public class ItemInfoTableMessageHandler implements MessageHandler {@Autowiredprivate ProductSearchRepository productSearchRepository;@Overridepublic String tableName() {return "item_info";}@Overridepublic void handleMessage(TableDataChangeDTO tableDataChangeDTO) throws IOException {String itemId = String.valueOf(tableDataChangeDTO.getKeyId());List<ProductSearchDO> productSearchDOS = productSearchRepository.queryProductSearchInfos(itemId);productSearchRepository.saveProductSearchInfos(productSearchDOS);}
}@Repository
public class ProductSearchRepository {private static final String SKU_INFO_INDEX = "sku_info_index";@Autowiredprivate RestHighLevelClient restHighLevelClient;@Autowiredprivate SkuInfoMapper skuInfoMapper;//根據itemId查詢和商品索引相關的信息public List<ProductSearchDO> queryProductSearchInfos(String itemId) {return skuInfoMapper.queryProductSearchInfos(itemId);}//批量保存商品索引數據public void saveProductSearchInfos(List<ProductSearchDO> productSearchDOS) throws IOException {BulkRequest bulkRequest = new BulkRequest();for (ProductSearchDO productSearchDO : productSearchDOS) {Map<String, Object> jsonMap = new HashMap<>();jsonMap.put("skuName", productSearchDO.getSkuName());jsonMap.put("basePrice", productSearchDO.getBasePrice());jsonMap.put("vipPrice", productSearchDO.getVipPrice());jsonMap.put("brandId", productSearchDO.getBrandId());jsonMap.put("brandName", productSearchDO.getBrandName());jsonMap.put("createTime", new Date());jsonMap.put("updateTime", new Date());IndexRequest indexRequest = new IndexRequest(SKU_INFO_INDEX).id(productSearchDO.getSkuId()).source(jsonMap);bulkRequest.add(indexRequest);}restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);}...
}

3.商品B端搜索系統的歷史搜索詞的實現

(1)商品B端保存歷史搜索詞的接?

(2)商品B端查詢歷史搜索詞的接?

(1)商品B端保存歷史搜索詞的接?

使用場景:商家輸入搜索詞搜索商品的時候
接口說明:把商家搜索過的詞保存到Redis的List數據結構中
//商品搜索服務
@DubboService(version = "1.0.0", interfaceClass = ProductSearchApi.class, retries = 0)
public class ProductSearchApiImpl implements ProductSearchApi {@Resourceprivate RedisCache redisCache;@Resourceprivate ProductSearchRepository productSearchRepository;//保存歷史搜索詞接口@Overridepublic JsonResult<HistorySearchWordResultDTO> saveHistorySearchWord(HistorySearchWordRequest request) {//在隊列頭部添加新的歷史搜索詞redisCache.lpush(HistorySearchWordConstants.getKey(request.getUserId()), request.getHistorySearchWord());//修改隊列只保存固定數量的搜索詞redisCache.ltrim(HistorySearchWordConstants.getKey(request.getUserId()), 0, HistorySearchWordConstants.HISTORY_WORD_COUNT_PER_USER - 1);return JsonResult.buildSuccess(new HistorySearchWordResultDTO(true));}...
}//保存用戶歷史搜索詞請求
@Data
public class HistorySearchWordRequest implements Serializable {//用戶idprivate Long userId;//新的歷史搜索詞private String historySearchWord;
}

(2)商品B端查詢歷史搜索詞的接?

使用場景:展示商家的搜索歷史記錄的時候
接口說明:從Redis列表中查詢商家的歷史搜索詞
//商品搜索服務
@DubboService(version = "1.0.0", interfaceClass = ProductSearchApi.class, retries = 0)
public class ProductSearchApiImpl implements ProductSearchApi {@Resourceprivate RedisCache redisCache;@Resourceprivate ProductSearchRepository productSearchRepository;//查詢歷史搜索詞接口@Overridepublic JsonResult<HistorySearchWordDTO> listHistorySearchWords(HistorySearchWordQuery request) {List<String> result = redisCache.lrange(HistorySearchWordConstants.getKey(request.getUserId()), 0, HistorySearchWordConstants.HISTORY_WORD_COUNT_PER_USER - 1);return JsonResult.buildSuccess(new HistorySearchWordDTO(result));}...
}//查詢商家歷史搜索詞請求
@Data
public class HistorySearchWordQuery implements Serializable {//用戶idprivate Long userId;
}

4.商品B端搜索系統的搜索詞補全的實現

(1)商品B端搜索系統的添加搜索補全詞的接?

(2)商品B端搜索系統查詢搜索補全詞的接口

(3)商品B端搜索詞補全的接口

(1)商品B端搜索系統的添加搜索補全詞的接?

使用場景:運營人員添加搜索補全詞的時候
接口說明:把搜索補全詞保存到ES的搜索補全詞索引中
//搜索詞
@DubboService(version = "1.0.0", interfaceClass = CompletionSearchWordApi.class, retries = 0)
public class CompletionSearchWordApiImpl implements CompletionSearchWordApi {@Autowiredprivate CompletionSearchWordService completionSearchWordService;//保存搜索補全詞接口@Overridepublic JsonResult<CompletionSearchWordResultDTO> saveCompletionSearchWord(CompletionSearchWordRequest request) {try {CompletionSearchWordResultDTO resultDTO = completionSearchWordService.saveCompletionSearchWord(request);return JsonResult.buildSuccess(resultDTO);} catch (ProductBizException e) {log.error("biz error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getMessage());}}...
}@Service
public class CompletionSearchWordServiceImpl implements CompletionSearchWordService {@Autowiredprivate CompletionSearchWordRepository completionSearchWordRepository;//保存搜索補全詞@Overridepublic CompletionSearchWordResultDTO saveCompletionSearchWord(CompletionSearchWordRequest request) throws IOException {return completionSearchWordRepository.saveCompletionSearchWord(request);}...
}//運營添加搜索補全詞請求
@Data
public class CompletionSearchWordRequest implements Serializable {//索引名稱private String indexName;//字段名稱private String fieldName;//要添加的補全詞private List<String> completionSearchWords;
}@Repository
public class CompletionSearchWordRepository {@Autowiredprivate RestHighLevelClient restHighLevelClient;//保存搜索補全詞public CompletionSearchWordResultDTO saveCompletionSearchWord(CompletionSearchWordRequest request) throws IOException {BulkRequest bulkRequest = new BulkRequest(request.getIndexName());List<String> completionSearchWords = request.getCompletionSearchWords();for (String completionSearchWord : completionSearchWords) {bulkRequest.add(new IndexRequest().source(XContentType.JSON, request.getFieldName(), completionSearchWord));}restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);return new CompletionSearchWordResultDTO(true);}...
}

(2)商品B端搜索系統查詢搜索補全詞的接口

使用場景:后臺展示搜索補全詞列表的時候
接口說明:從ES的搜索補全詞索引中分頁查詢數據
//搜索詞
@DubboService(version = "1.0.0", interfaceClass = CompletionSearchWordApi.class, retries = 0)
public class CompletionSearchWordApiImpl implements CompletionSearchWordApi {@Autowiredprivate CompletionSearchWordService completionSearchWordService;//查詢補全詞接口@Overridepublic JsonResult<PageResult<CompletionSearchWordDTO>> listCompletionSearchWordPage(QueryCompletionSearchWordPageRequest request) {try {PageResult<CompletionSearchWordDTO> resultDTO = completionSearchWordService.listCompletionSearchWordPage(request);return JsonResult.buildSuccess(resultDTO);} catch (ProductBizException e) {log.error("biz error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getErrorCode(), e.getErrorMsg());} catch (Exception e) {log.error("system error: request={}", JSON.toJSONString(request), e);return JsonResult.buildError(e.getMessage());}}...
}@Service
public class CompletionSearchWordServiceImpl implements CompletionSearchWordService {@Autowiredprivate CompletionSearchWordRepository completionSearchWordRepository;//查詢搜索補全詞@Overridepublic PageResult<CompletionSearchWordDTO> listCompletionSearchWordPage(QueryCompletionSearchWordPageRequest request) throws IOException {return completionSearchWordRepository.listCompletionSearchWordPage(request);}...
}//后臺查詢搜索詞列表請求
@Data
public class QueryCompletionSearchWordPageRequest extends PageRequest {//索引名稱private String indexName;//字段名稱private String fieldName;//補全詞private String completionSearchWord;
}@Repository
public class CompletionSearchWordRepository {@Autowiredprivate RestHighLevelClient restHighLevelClient;...//查詢搜索補全詞public PageResult<CompletionSearchWordDTO> listCompletionSearchWordPage(QueryCompletionSearchWordPageRequest request) throws IOException {SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();if (StringUtils.isNotBlank(request.getCompletionSearchWord())) {searchSourceBuilder.query(QueryBuilders.matchQuery(request.getFieldName(), request.getCompletionSearchWord()));}int from = (request.getPageNum() - 1) * request.getPageSize();searchSourceBuilder.from(from);searchSourceBuilder.size(request.getPageSize());SearchRequest searchRequest = new SearchRequest(request.getIndexName());searchRequest.source(searchSourceBuilder);SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);PageResult<CompletionSearchWordDTO> pageResult = new PageResult<>();List<CompletionSearchWordDTO> pageContent = new ArrayList<>();SearchHit[] hits = searchResponse.getHits().getHits();for (SearchHit hit : hits) {pageContent.add(new CompletionSearchWordDTO(((String) hit.getSourceAsMap().get(request.getFieldName()))));}pageResult.setContent(pageContent);pageResult.setTotalElements(searchResponse.getHits().getTotalHits().value);pageResult.setSize(request.getPageSize());pageResult.setNumber(request.getPageNum());return pageResult;}...
}

(3)商品B端搜索詞補全的接口

使用場景:商家在搜索框輸入搜索詞的時候
接口說明:根據輸入的搜索詞從ES的搜索補全詞索引中查詢對應的詞
//商品搜索
@DubboService(version = "1.0.0", interfaceClass = ProductSearchApi.class, retries = 0)
public class ProductSearchApiImpl implements ProductSearchApi {@Resourceprivate RedisCache redisCache;@Resourceprivate ProductSearchRepository productSearchRepository;...//搜索詞補全接口@Overridepublic JsonResult<CompletionSearchWordsDTO> listCompletionSearchWords(CompletionSearchWordQuery request) {try {CompletionSearchWordsDTO result = productSearchRepository.listCompletionSearchWords(request);return JsonResult.buildSuccess(result);} catch (Exception e) {e.printStackTrace();return JsonResult.buildError(e.getMessage());}}...
}//補全用戶搜索詞請求
@Data
public class CompletionSearchWordQuery {//索引名稱private String indexName;//字段名稱private String fieldName;//需要補全的詞(用戶輸入的內容)private String text;//返回多少個補全后的詞private int count;
}//商品搜索
@Repository
public class ProductSearchRepository {private static final String MY_SUGGEST = "my_suggest";@Resourceprivate RestHighLevelClient restHighLevelClient;//搜索詞補全public CompletionSearchWordsDTO listCompletionSearchWords(CompletionSearchWordQuery request) throws IOException {//1.構建CompletionSuggestion條件CompletionSuggestionBuilder completionSuggestionBuilder = SuggestBuilders.completionSuggestion(request.getFieldName());completionSuggestionBuilder.prefix(request.getText());completionSuggestionBuilder.skipDuplicates(true);completionSuggestionBuilder.size(request.getCount());SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.sort(new ScoreSortBuilder().order(SortOrder.DESC));searchSourceBuilder.suggest(new SuggestBuilder().addSuggestion(MY_SUGGEST, completionSuggestionBuilder));//2.封裝搜索請求SearchRequest searchRequest = new SearchRequest();searchRequest.indices(request.getIndexName());searchRequest.source(searchSourceBuilder);//3.查詢elasticsearchSearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//4.獲取響應中的補全的詞的列表CompletionSuggestion completionSuggestion = searchResponse.getSuggest().getSuggestion(MY_SUGGEST);List<CompletionSuggestion.Entry.Option> options = completionSuggestion.getEntries().get(0).getOptions();List<String> result = new ArrayList<>();for (CompletionSuggestion.Entry.Option option : options) {result.add(option.getText().string());}return new CompletionSearchWordsDTO(result);}...
}

5.商品B端搜索系統的搜索接口實現

(1)商品B端的搜索查詢接口

(2)商品B端的結構化查詢接口

(1)商品B端的搜索查詢接口

使用場景:商家搜索商品的時候
接口說明:根據輸入的搜索詞從商品索引中查詢skuId列表
//商品搜索
@DubboService(version = "1.0.0", interfaceClass = ProductSearchApi.class, retries = 0)
public class ProductSearchApiImpl implements ProductSearchApi {...//商品搜索查詢接口@Overridepublic JsonResult<PorductSearchDTO> searchProducts(ProductSearchQuery request) {try {PorductSearchDTO result = productSearchRepository.searchProducts(request);return JsonResult.buildSuccess(result);} catch (Exception e) {e.printStackTrace();return JsonResult.buildError(e.getMessage());}}...
}//商品搜索請求
@Data
public class ProductSearchQuery extends PageQuery {//索引名字private String indexName;//查詢參數private Map<String, String> queryTexts;//高亮字段private String highLightField;
}//商品搜索
@Repository
public class ProductSearchRepository {private static final String MY_SUGGEST = "my_suggest";@Resourceprivate RestHighLevelClient restHighLevelClient;...//商品搜索查詢接口public PorductSearchDTO searchProducts(ProductSearchQuery request) throws IOException {SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.trackTotalHits(true);//1.構建match條件request.getQueryTexts().forEach((field, text) -> {searchSourceBuilder.query(QueryBuilders.matchQuery(field, text));});//2.設置搜索高亮配置HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field(request.getHighLightField());highlightBuilder.preTags("<span stype=color:red>");highlightBuilder.postTags("</span>");highlightBuilder.numOfFragments(0);searchSourceBuilder.highlighter(highlightBuilder);//3.設置搜索分頁參數int from = (request.getPageNum() - 1) * request.getPageSize();searchSourceBuilder.from(from);searchSourceBuilder.size(request.getPageSize());//4.封裝搜索請求SearchRequest searchRequest = new SearchRequest(request.getIndexName());searchRequest.source(searchSourceBuilder);//5.查詢elasticsearchSearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//6.對結果進行高亮處理SearchHits hits = searchResponse.getHits();for (SearchHit hit : hits) {HighlightField highlightField = hit.getHighlightFields().get(request.getHighLightField());Map<String, Object> sourceAsMap = hit.getSourceAsMap();Text[] fragments = highlightField.fragments();StringBuilder builder = new StringBuilder();for (Text fragment : fragments) {builder.append(fragment.string());}sourceAsMap.put(request.getHighLightField(), builder.toString());}//7.封裝返回結果return buildPorductSearchDTO(hits, request.getPageNum(), request.getPageSize());}...
}

(2)商品B端的結構化查詢接口

使用場景:商家對搜索結果過濾和排序的時候
接口說明:根據用戶輸入的過濾和排序條件從商品索引中查詢skuId列表
//商品搜索
@DubboService(version = "1.0.0", interfaceClass = ProductSearchApi.class, retries = 0)
public class ProductSearchApiImpl implements ProductSearchApi {...//商品結構化查詢接口@Overridepublic JsonResult<PorductSearchDTO> structuredSearchProducts(ProductStructuredQuery request) {try {PorductSearchDTO result = productSearchRepository.structuredSearchProducts(request);return JsonResult.buildSuccess(result);} catch (Exception e) {e.printStackTrace();return JsonResult.buildError(e.getMessage());}}...
}//商品結構化查詢請求
@Data
public class ProductStructuredQuery extends PageQuery {//索引名字private String indexName;//Query DSLprivate Map<String, Object> queryDsl;
}//商品搜索
@Repository
public class ProductSearchRepository {private static final String MY_SUGGEST = "my_suggest";@Resourceprivate RestHighLevelClient restHighLevelClient;...//商品結構化查詢public PorductSearchDTO structuredSearchProducts(ProductStructuredQuery request) throws IOException {SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();searchSourceBuilder.trackTotalHits(true);//1.解析queryDSLString queryDsl = JSON.toJSONString(request.getQueryDsl());SearchModule searchModule = new SearchModule(Settings.EMPTY, false, Collections.emptyList());NamedXContentRegistry namedXContentRegistry = new NamedXContentRegistry(searchModule.getNamedXContents());XContent xContent = XContentFactory.xContent(XContentType.JSON);XContentParser xContentParser = xContent.createParser(namedXContentRegistry, LoggingDeprecationHandler.INSTANCE, queryDsl);searchSourceBuilder.parseXContent(xContentParser);//2.設置搜索分頁參數int from = (request.getPageNum() - 1) * request.getPageSize();searchSourceBuilder.from(from);searchSourceBuilder.size(request.getPageSize());//3.封裝搜索請求SearchRequest searchRequest = new SearchRequest(request.getIndexName());searchRequest.source(searchSourceBuilder);//4.查詢elasticsearchSearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//5.封裝返回結果return buildPorductSearchDTO(searchResponse.getHits(), request.getPageNum(), request.getPageSize());}...
}

6.索引重建

(1)問題分析

(2)解決方案

(3)操作演示

(4)其他說明

(1)問題分析

在實際中可能會遇到正在使?的索引需要變化字段類型、增減字段等,這時可能就需要創建新的mappings。

因為索引正在被應?使?,在進?操作時就要考慮怎么降低對應?的影響,以及如何把?前的數據遷移到新的索引中。

(2)解決方案

可以使?ES的索引別名功能來降低對應?的影響,實現不停機重建索引。可以使?ES的Scroll API + Bulk API,實現把?前的數據遷移到新的索引中。

(3)操作演示

一.假設目前正在被使用的商品索引為sku_info_index
二.首先給sku_info_index索引起別名sku_info_index_alias
三.然后需要新建一個索引sku_info_index_v2
四.接著使用Scroll API + Bulk API遷移數據
五.最后把sku_info_index_alias索引別名指向sku_info_index_v2索引

一.目前正在被使用的商品索引

sku_info_index現在正在被業務使?:

PUT /sku_info_index
{"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings":{"properties": {"skuName": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"basePrice": {"type": "integer"},"vipPrice": {"type": "integer"},"brandId": {"type": "keyword"},"brandName": {"type": "keyword"},"saleCount": {"type": "integer"},"createTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"},"updateTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"}}}
}

二.給sku_info_index索引起別名

讓應?使?sku_info_index_alias別名來操作數據:

PUT /sku_info_index/_alias/sku_info_index_alias

三.然后需要新建一個索引sku_info_index_v2

新建一個sku_info_index_v2索引:

PUT /sku_info_index
{"settings": {"number_of_shards": 3,"number_of_replicas": 1},"mappings": {"properties": {"skuName": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"basePrice": {"type": "integer"},"vipPrice": {"type": "integer"},"brandId": {"type": "keyword"},"brandName": {"type": "keyword"},"saleCount": {"type": "integer"},"label": {"type": "integer"},"createTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"},"updateTime": {"type": "date","format": "yyyy-MM-dd HH:mm:ss"}}}
}

四.接著使用Scroll API + Bulk API遷移數據

#https://www.elastic.co/guide/en/elasticsearch/reference/7.6/search-request-body.html#request-body-search-scroll
POST /sku_info_index/_search?scroll=1m
{"size": 3,"query": {"match_all": { }}
}POST /_bulk
{"index": {"_index": "sku_info_index_v2","_id": "8000177337"}
}
{"skuName": "Apple iPhone 13 Pro Max 256GB 蒼嶺綠? ?持移動聯通電信5G 雙卡雙待 ?機","brandName": "蘋果","createTime": "2022-03-12 08:24:57","brandId": 4,"vipPrice": 9799,"updateTime": "2022-03-12 08:24:57","basePrice": 9999,"label": "新品"
}
{"index": {"_index": "sku_info_index_v2","_id": "8000177338"}
}
{"skuName": "Apple iPhone 13 (A2634)128GB 綠? ?持移動聯通電信5G 雙卡雙待? 機","brandName": "蘋果","createTime": "2022-03-12 08:24:57","brandId": 4,"vipPrice": 5798,"updateTime": "2022-03-12 08:24:57","basePrice": 5999,"label": "爆品"
}
{"index": {"_index": "sku_info_index_v2","_id": "8000177339"}
}
{"skuName": "蘋果13mini Apple iphone 13 mini 5G新品?機 粉? 128GB","brandName": "蘋果","createTime": "2022-03-12 08:24:57","brandId": 4,"vipPrice": 4900,"updateTime": "2022-03-12 08:24:57","basePrice": 5100,"label": "超值特惠"
}

五.最后把sku_info_index_alias索引別名指向sku_info_index_v2索引

POST /_aliases
{"actions": [{"remove": {"index": "sku_info_index","alias": "sku_info_index_alias"}}, {"add": {"index": "sku_info_index_v2","alias": "sku_info_index_alias"}}]
}

(4)其他說明

一.如果在上ES前,就預計索引結構可能會發?變化。可以?開始就通過索引別名來操作數據,這樣當索引結構需要變更時可按上?的?案及演示實現不停機重建索引。

二.當使?索引別名時,ES Java API的代碼無需任何變化,?如下?是SearchRequest的構造法?法:

public SearchRequest(String... indices) {this(indices, new SearchSourceBuilder());
}

如果直接索引名是example_index,那么創建的SearchRequest對象如下,其中example_index為索引名:

new SearchRequest("example_index")

如果直接索引example_index對應的索引別名是example_index_alias,那么創建的SearchRequest對象就是:

new SearchRequest("example_index_alias")

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/85928.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/85928.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/85928.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

HCIP-Datacom Core Technology V1.0_4 OSPF路由計算

ospf是如何計算生成這些路由呢&#xff0c; 區域內路由計算 LSA概述 同一個區域內路由器去進行一個數據庫同步&#xff0c;形成一個LSDB&#xff0c;那么數據庫里面所存在的LSA,是如何利用它去進行計算和生成路由的呢&#xff0c;以及這些LSA分別包含了哪些信息&#xff0c;比…

微服務拆分之術與道:從原則到實踐的深度解析

引言&#xff1a;微服務的塞壬之歌 - 超越單體巨石 故事要從一家名為“巨石公司”&#xff08;Monolith Inc.&#xff09;的虛構企業說起。它的旗艦產品曾是公司的驕傲&#xff0c;但隨著歲月流逝&#xff0c;這個系統逐漸演變成了一個“大泥球”&#xff08;Big Ball of Mud&a…

【新手向】GitHub Desktop 的使用說明(含 GitHub Desktop 和 Git 的功能對比)

GitHub Desktop 是 GitHub 公司推出的一款桌面應用程序&#xff0c;旨在幫助開發人員更輕松地使用 GitHub&#xff0c;以下是其簡單的使用說明&#xff1a; 安裝與登錄 下載 GitHub Desktop |GitHub 桌面 訪問GitHub Desktop 官方網站&#xff0c;根據自己的操作系統下載對應的…

Linux驅動編程 - gpio、gpiod函數

????? 目錄 簡介&#xff1a; 1、GPIO 子系統有兩套API&#xff1a; 一、GPIO新、舊版互相兼容轉換 API 1、轉化函數 二、基于描述符接口(descriptor-based) &#xff08;以"gpiod_"為前綴&#xff09; 1、獲取 GPIO 2.1 struct gpio_desc *gpiod_get(s…

Tensorflow推理時遇見PTX錯誤,安裝CUDA及CuDNN, 解決問題!

問題原因&#xff1a; 使用TensorFlow一個小模型是進行推理的時候&#xff0c;報了PTX錯誤&#xff1a; Traceback (most recent call last): 20273 2025-06-18 10:20:38.345 INFO 1 --- [checkTask-1] c.l.a.d.a.util.AnalyzeCommonHelper : File "/home/python/commo…

C# 網絡編程-關于HTTP/HTTPS的基礎(一)

一、HTTP基礎概念 1. 請求-響應模型 HTTP是基于客戶端-服務器的無狀態協議&#xff0c;流程如下&#xff1a; 客戶端&#xff08;如瀏覽器&#xff09;發起請求。服務器接收請求并處理。服務器返回響應&#xff0c;包含狀態碼、Header和響應體。連接關閉&#xff0c;后續請求…

小程序右上角○關閉事件

小程序用戶真實離開事件追蹤&#xff1a;一場與技術細節的博弈 在數據分析的場景下&#xff0c;精準捕捉用戶行為至關重要。我們遇到了這樣一個需求&#xff1a;在小程序的埋點方案中&#xff0c;只記錄用戶真正意義上的離開&#xff0c;即通過點擊小程序右上角關閉按鈕觸發的…

數據庫高性能應用分析報告

數據庫高性能應用分析報告 引言摘要 在數字經濟加速發展的今天&#xff0c;數據庫性能已成為企業核心競爭力的關鍵要素。根據Gartner 2024年最新研究&#xff0c;全球企業因數據庫性能問題導致的直接經濟損失高達每年420億美元&#xff0c;同時性能優化帶來的業務提升可達到2…

Java使用itext pdf生成PDF文檔

Java使用itext pdf生成PDF文檔 Java使用itextpdf生成PDF文檔 在日常開發中&#xff0c;我們經常需要生成各種類型的文檔&#xff0c;其中PDF是最常用的一種格式。本文將介紹如何使用Java和iText庫生成包含中文內容的PDF文檔&#xff0c;并通過一個具體的示例來展示整個過程。…

利用VBA將Word文檔修改為符合EPUB3標準規范的HTML文件

Word本身具有將docx文件轉換為HTML文件的功能&#xff0c;但是轉換出來的HTML文檔源代碼令人不忍卒讀&#xff0c;占用空間大&#xff0c;可維護性極差&#xff0c;如果想給HTML文檔加上點自定義交互行為&#xff0c;也不是一般的麻煩。如果文檔中包含注釋&#xff0c;對于Word…

開發語言本身只是提供了一種解決問題的工具

前言 你是否曾經注意到&#xff0c;在中國的軟件工程師日常工作中&#xff0c;他們使用的工具界面大多為英文&#xff1f;從代碼編輯器到開發框架文檔&#xff0c;再到錯誤信息提示框&#xff0c;英語似乎已經成為了計算機領域事實上的標準語言。那么為什么在全球化日益加深的…

2024計算機二級Python真題精講|第一套(易錯點分析)

一、選擇題 1.計算機完成一條指令所花費的時間稱為一個( )。 A.執行時序 B.執行速度 C.執行速度 D.指令周期 答案 D 一般把計算機完成一條指令所花費發時間稱為一個指令周期。指令周期越短&#xff0c;指令執行就越快。 2.順序程序不具有&#xff08; &#xf…

BGP路由反射器(RR)實驗詳解,結尾有詳細腳本

目錄 路由反射器基礎概念 實驗拓撲與設計 實驗配置步驟 配置驗證與排錯 實驗總結 完整配置命令集 路由反射器基礎概念 在傳統的IBGP網絡中&#xff0c;為了防止路由環路&#xff0c;BGP規定通過IBGP學到的路由不能再傳遞給其他IBGP對等體&#xff0c;這導致所有IBGP路由…

(aaai2025) SparseViT: 用于圖像篡改檢測的Spare-Coding Transformer

論文&#xff1a;(aaai2025) SparseViT: Nonsemantics-Centered, Parameter-Efficient Image Manipulation Localization through Spare-Coding Transformer 代碼&#xff1a;https://github.com/scu-zjz/SparseViT 這個論文研究的是圖像篡改檢測&#xff08;Image Manipulatio…

C#測試調用Markdig解析Markdown的基本用法

Markdig是.NET平臺的高性能開源Markdown處理器&#xff0c;嚴格遵循 CommonMark 標準&#xff0c;確保解析一致性&#xff0c;其核心優勢在于擴展性強&#xff1a;通過模塊化管道模型&#xff0c;可輕松添加自定義語法或修改現有邏輯。Markdig內置支持表格、任務列表、數學公式…

MySQL 主從同步完整配置示例

以下是 MySQL 主從同步完整配置示例&#xff08;基于 Linux 系統&#xff09;&#xff0c;包含主庫和從庫的配置步驟&#xff1a; 一、主庫&#xff08;Master&#xff09;配置 1. 安裝 MySQL&#xff08;以 CentOS 為例&#xff09; yum install -y mysql-server systemctl …

可信啟動與fTPM的交互驗證(概念驗證)

安全之安全(security)博客目錄導讀 目錄 一、組件構成 二、Arm FVP平臺PoC構建 三、在Armv8-A Foundation FVP上運行PoC 四、微調fTPM TA 可信啟動&#xff08;Measured Boot&#xff09;是通過密碼學方式度量啟動階段代碼及關鍵數據&#xff08;例如使用TPM芯片&#xff…

SQL Server基礎語句4:數據定義

文章目錄 一、數據庫與架構1.1 創建與刪除數據庫1.1.1 使用CREATE DATABASE語句創建數據庫1.1.2 使用DROP DATABASE語句刪除數據庫1.1.3 使用SSMS創建數據庫1.1.4 使用SSMS刪除數據庫 1.2 CREATE SCHEMA&#xff1a;創建新架構1.2.1 Schema簡介1.2.2 使用CREATE SCHEMA語句創建…

上門按摩app會員系統框架搭建

一、邏輯分析 用戶注冊與登錄&#xff1a; 新用戶需要提供基本信息&#xff0c;如姓名、手機號、郵箱等進行注冊。手機號用于接收驗證碼進行身份驗證&#xff0c;郵箱可用于密碼找回等功能。注冊成功后&#xff0c;用戶可以使用手機號 / 郵箱和密碼進行登錄。登錄時需要驗證用戶…

java項目打包成jar包,并給jmeter使用

1.新建項目 編寫代碼&#xff0c;導入必要的jar包&#xff0c; 右鍵點擊項目&#xff0c;然后export&#xff0c;選擇main函數&#xff0c; package utils; import java.text.SimpleDateFormat; import java.util.Date; import java.util.Locale; import java.util.Random; …