大纲

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; …