• java B2B2C多用户商城系统-搜索分词架构分享


    需求分析:

    在javashop电商系统中,商品数据是存在elasticsearch中,使用ik分词器分词,ik分词器的词库内置了2万多个。

    但在实际运维过程中,因为商品的个性化,词库不一定可以满足,为了搜索引擎分词(关键词)更加准确,要求可对分词词库进行手工维护。

    思路:

    IK自定义词库是支持远程热加载的。

    先看下官方的说明:

    remote_ext_dict:

    1.该 http 请求需要返回两个头部(header),一个是 Last-Modified,一个是 ETag,这两者都是字符串类型,只要有一个发生变化,该插件就会去抓取新的分词进而更新词库。

    2.该 http 请求返回的内容格式是一行一个分词,换行符用 即可。

     

    满足上面两点要求就可以实现热更新分词了,不需要重启 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("
    " + word.getName());
    55                 }
    56                 i++;
    57             }
    58         }
    59         return buffer.toString();
    60     }
    61 }

    以上为此次分享内容,后续每周会不定期分享架构文章,大家可以关注我们!!!

                                                                                                                 易族智汇(javashop)原创文章 

  • 相关阅读:
    gcc和g++的区别
    configure svn server on win
    FD_SET,FD_ISSET,FD_ZERO,select
    intel中的cr寄存器
    Linux系统环境下的Socket编程详细解析
    可重入函数与不可重入函数
    初步认识迭代服务器和并发服务器
    排序
    fd_set 用法
    MFC消息映射
  • 原文地址:https://www.cnblogs.com/javashop-docs/p/12768014.html
Copyright © 2020-2023  润新知