需求分析:
在javashop電商系統中,商品資料是存在elasticsearch中,使用ik分詞器分詞,ik分詞器的詞庫內建了2萬多個。
但在實際運維過程中,因為商品的個性化,詞庫不一定可以滿足,為了搜尋引擎分詞(關鍵詞)更加準確,要求可對分詞詞庫進行手工維護。
思路:IK自定義詞庫是支援遠端熱載入的。
先看下官方的說明:
remote_ext_dict:
1.該 http 請求需要返回兩個頭部(header),一個是 Last-Modified,一個是 ETag,這兩者都是字串型別,只要有一個發生變化,該外掛就會去抓取新的分詞進而更新詞庫。
2.該 http 請求返回的內容格式是一行一個分詞,換行符用 \\n 即可。
滿足上面兩點要求就可以實現熱更新分詞了,不需要重啟 ES 例項。
由此,我們可以開放一個API供IK呼叫。
搜尋分詞(關鍵詞)架構思路1.管理端對關鍵詞進行維護;
2.管理端設定祕鑰(此祕鑰僅做載入分詞API驗證使用);
3.管理端展示分詞列表,根據最後修改時間倒序展示。
資料結構:關鍵詞表(es_custom_words):
祕鑰設定說明: 在系統設定表(es_setting)中新增分組(ES_SIGN),對祕鑰進行維護時修改此分組下的資料。
領域模型管理端管理端新增搜尋設定選單,對關鍵詞進行維護
模型
ES載入詞庫API在基礎API中新增載入詞庫API,此Api需要校驗祕鑰,失敗返回空字串,成功則從資料庫中載入資料並返回。
IK Analyzer 擴充套件配置如下:
base-api-domain改為自己的base-api域名或者IP:埠即可
原始碼說明:此處僅展示IK載入片段程式碼,關於管理分此維護相關不做展示
CustomWordsBaseController
package com.enation.app.javashop.base.api;import com.enation.app.javashop.core.base.SettingGroup;import com.enation.app.javashop.core.client.system.SettingClient;import com.enation.app.javashop.core.goods.GoodsErrorCode;import com.enation.app.javashop.core.goodssearch.model.EsSecretSetting;import com.enation.app.javashop.core.goodssearch.service.CustomWordsManager;import com.enation.app.javashop.framework.exception.ServiceException;import com.enation.app.javashop.framework.util.JsonUtil;import com.enation.app.javashop.framework.util.StringUtil;import io.swagger.annotations.Api;import io.swagger.annotations.ApiImplicitParam;import io.swagger.annotations.ApiImplicitParams;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;import springfox.documentation.annotations.ApiIgnore;/** * 自定義分詞控制器 * * @author liuyulei * @version v1.0 * @since v7.0.0 * 2019-05-26 */@RestController@RequestMapping("/load-customwords")@Api(description = "載入分詞庫")public class CustomWordsBaseController { @Autowired private CustomWordsManager customWordsManager; @Autowired private SettingClient settingClient; @GetMapping @ApiImplicitParams({ @ApiImplicitParam(name = "secret_key", value = "祕鑰", required = true, dataType = "String", paramType = "query") }) public String getCustomWords(@ApiIgnore String secretKey){ if(StringUtil.isEmpty(secretKey)){ return ""; } String value = settingClient.get(SettingGroup.ES_SIGN); if(StringUtil.isEmpty(value)){ return ""; } EsSecretSetting secretSetting = JsonUtil.jsonToObject(value,EsSecretSetting.class); if(!secretKey.equals(secretSetting.getSecretKey())){ throw new ServiceException(GoodsErrorCode.E310.code(),"祕鑰驗證失敗!"); } String res = this.customWordsManager.deploy(); try { return new String(res.getBytes(),"utf-8"); }catch (Exception e){ e.printStackTrace(); } return ""; }}
CustomWordsManager
package com.enation.app.javashop.core.goodssearch.service;/** * 自定義分詞表業務層 * @author fk * @version v1.0 * @since v7.0.0 * 2018-06-20 16:08:07 * * * update by liuyulei 2019-05-27 */public interface CustomWordsManager { /** * 部署替換 * @return */ String deploy();}
CustomWordsManagerImpl
package com.enation.app.javashop.core.goodssearch.service.impl;import com.enation.app.javashop.core.goodssearch.model.CustomWords;import com.enation.app.javashop.core.goodssearch.service.CustomWordsManager;import com.enation.app.javashop.framework.context.ThreadContextHolder;import com.enation.app.javashop.framework.database.DaoSupport;import com.enation.app.javashop.framework.util.DateUtil;import com.enation.app.javashop.framework.util.StringUtil;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.stereotype.Service;import javax.servlet.http.HttpServletResponse;import java.text.SimpleDateFormat;import java.util.List;/** * 自定義分詞表業務類 * * @author fk * @version v1.0 * @since v7.0.0 * 2018-06-20 16:08:07 * * update by liuyulei 2019-05-27 */@Servicepublic class CustomWordsManagerImpl implements CustomWordsManager { @Autowired @Qualifier("goodsDaoSupport") private DaoSupport daoSupport; @Override public String deploy() { String sql = "select * from es_custom_words where disabled = 1 order by modify_time desc"; List<CustomWords> list = this.daoSupport.queryForList(sql, CustomWords.class); HttpServletResponse response = ThreadContextHolder.getHttpResponse(); StringBuffer buffer = new StringBuffer(); if (StringUtil.isNotEmpty(list)) { int i = 0; for (CustomWords word : list) { if (i == 0) { SimpleDateFormat format = new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" ); try { response.setHeader("Last-Modified", format.parse(DateUtil.toString(word.getAddTime(),"yyyy-MM-dd hh:mm:ss")) + ""); response.setHeader("ETag", format.parse(DateUtil.toString(word.getModifyTime(),"yyyy-MM-dd hh:mm:ss")) + ""); }catch (Exception e){ e.printStackTrace(); } buffer.append(word.getName()); } else { buffer.append("\\n" + word.getName()); } i++; } } return buffer.toString(); }}
以上為此次分享內容,後續每週會不定期分享架構文章,大家可以關注我們!!!