java B2B2C多用戶商城系統-搜索分詞架構分享


需求分析:

在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):

字段名 

提示文字

類型

長度

是否主鍵

id

id

int

10

name

關鍵詞

字符串

100

add_time

添加時間

長整型

20

modify_time

最后修改時間

長整型

20

disabled

是否可用:可用:1 ;隱藏: 0

整形

1

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

秘鑰設置說明: 在系統設置表(es_setting)中新增分組(ES_SIGN),對秘鑰進行維護時修改此分組下的數據。

領域模型

管理端

管理端添加搜索設置菜單,對關鍵詞進行維護

 

模型

屬性 

說明

備注

id

id

 

name

分詞名稱必填

 

addTime

添加時間

 

disabled

是否可用

可用:1;不可用:0

modifyTime

修改時間

 

 

 

 

 

 

 

 

 

 

 

ES加載詞庫API

在基礎API中添加加載詞庫API,此Api需要校驗秘鑰,失敗返回空字符串,成功則從數據庫中加載數據並返回。

IK Analyzer 擴展配置如下:

1 <?xml version="1.0" encoding="UTF-8"?>
2 <!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
3 <properties>
4         <comment>IK Analyzer 擴展配置</comment>
5         <!--用戶可以在這里配置遠程擴展字典 -->
6         <entry key="remote_ext_dict">http://base-api-domain/load-customwords?secret_key=secret_value</entry>
7 </properties>

其中secret_value可以隨意設置,此處配置的值,需要在管理端搜索分詞列表處保存

base-api-domain改為自己的base-api域名或者IP:端口即可

源碼

 ​說明:此處僅展示IK加載片段代碼,關於管理分此維護相關不做展示

CustomWordsBaseController

 1 package com.enation.app.javashop.base.api;
 2 
 3 import com.enation.app.javashop.core.base.SettingGroup;
 4 import com.enation.app.javashop.core.client.system.SettingClient;
 5 import com.enation.app.javashop.core.goods.GoodsErrorCode;
 6 import com.enation.app.javashop.core.goodssearch.model.EsSecretSetting;
 7 import com.enation.app.javashop.core.goodssearch.service.CustomWordsManager;
 8 import com.enation.app.javashop.framework.exception.ServiceException;
 9 import com.enation.app.javashop.framework.util.JsonUtil;
10 import com.enation.app.javashop.framework.util.StringUtil;
11 import io.swagger.annotations.Api;
12 import io.swagger.annotations.ApiImplicitParam;
13 import io.swagger.annotations.ApiImplicitParams;
14 import org.springframework.beans.factory.annotation.Autowired;
15 import org.springframework.web.bind.annotation.GetMapping;
16 import org.springframework.web.bind.annotation.RequestMapping;
17 import org.springframework.web.bind.annotation.RestController;
18 import springfox.documentation.annotations.ApiIgnore;
19 
20 /**
21  * 自定義分詞控制器
22  *
23  * @author liuyulei
24  * @version v1.0
25  * @since v7.0.0
26  * 2019-05-26
27  */
28 @RestController
29 @RequestMapping("/load-customwords")
30 @Api(description = "加載分詞庫")
31 public class CustomWordsBaseController {
32 
33     @Autowired
34     private CustomWordsManager customWordsManager;
35     @Autowired
36     private SettingClient settingClient;
37 
38     @GetMapping
39     @ApiImplicitParams({
40             @ApiImplicitParam(name = "secret_key", value = "秘鑰", required = true, dataType = "String", paramType = "query")
41 
42     })
43     public String getCustomWords(@ApiIgnore  String secretKey){
44 
45         if(StringUtil.isEmpty(secretKey)){
46             return "";
47         }
48         String value = settingClient.get(SettingGroup.ES_SIGN);
49         if(StringUtil.isEmpty(value)){
50             return "";
51         }
52         EsSecretSetting secretSetting = JsonUtil.jsonToObject(value,EsSecretSetting.class);
53         if(!secretKey.equals(secretSetting.getSecretKey())){
54             throw new ServiceException(GoodsErrorCode.E310.code(),"秘鑰驗證失敗!");
55         }
56         String res = this.customWordsManager.deploy();
57         try {
58             return new String(res.getBytes(),"utf-8");
59         }catch (Exception e){
60             e.printStackTrace();
61         }
62         return "";
63 
64     }
65 
66 
67 }

CustomWordsManager

 1 package com.enation.app.javashop.core.goodssearch.service;
 2 
 3 /**
 4  * 自定義分詞表業務層
 5  * @author fk
 6  * @version v1.0
 7  * @since v7.0.0
 8  * 2018-06-20 16:08:07
 9  *
10  * * update by liuyulei 2019-05-27
11  */
12 public interface CustomWordsManager    {
13 
14    /**
15     * 部署替換
16     * @return
17     */
18     String deploy();
19 
20 }

CustomWordsManagerImpl

 1 package com.enation.app.javashop.core.goodssearch.service.impl;
 2 
 3 import com.enation.app.javashop.core.goodssearch.model.CustomWords;
 4 import com.enation.app.javashop.core.goodssearch.service.CustomWordsManager;
 5 import com.enation.app.javashop.framework.context.ThreadContextHolder;
 6 import com.enation.app.javashop.framework.database.DaoSupport;
 7 import com.enation.app.javashop.framework.util.DateUtil;
 8 import com.enation.app.javashop.framework.util.StringUtil;
 9 import org.springframework.beans.factory.annotation.Autowired;
10 import org.springframework.beans.factory.annotation.Qualifier;
11 import org.springframework.stereotype.Service;
12 
13 import javax.servlet.http.HttpServletResponse;
14 import java.text.SimpleDateFormat;
15 import java.util.List;
16 
17 /**
18  * 自定義分詞表業務類
19  *
20  * @author fk
21  * @version v1.0
22  * @since v7.0.0
23  * 2018-06-20 16:08:07
24  *
25  * update by liuyulei 2019-05-27
26  */
27 @Service
28 public class CustomWordsManagerImpl implements CustomWordsManager {
29 
30     @Autowired
31     @Qualifier("goodsDaoSupport")
32     private DaoSupport daoSupport;
33 
34     @Override
35     public String deploy() {
36         String sql = "select * from es_custom_words where disabled = 1 order by modify_time desc";
37         List<CustomWords> list = this.daoSupport.queryForList(sql, CustomWords.class);
38         HttpServletResponse response = ThreadContextHolder.getHttpResponse();
39         StringBuffer buffer = new StringBuffer();
40         if (StringUtil.isNotEmpty(list)) {
41             int i = 0;
42             for (CustomWords word : list) {
43                 if (i == 0) {
44                     SimpleDateFormat format =   new SimpleDateFormat( "yyyy-MM-dd hh:mm:ss" );
45                     try {
46                         response.setHeader("Last-Modified", format.parse(DateUtil.toString(word.getAddTime(),"yyyy-MM-dd hh:mm:ss")) + "");
47                         response.setHeader("ETag", format.parse(DateUtil.toString(word.getModifyTime(),"yyyy-MM-dd hh:mm:ss")) + "");
48                     }catch (Exception e){
49                         e.printStackTrace();
50                     }
51 
52                     buffer.append(word.getName());
53                 } else {
54                     buffer.append("\n" + word.getName());
55                 }
56                 i++;
57             }
58         }
59         return buffer.toString();
60     }
61 }

以上為此次分享內容,后續每周會不定期分享架構文章,大家可以關注我們!!!

                                                                                                             易族智匯(javashop)原創文章 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM