问答搜索系统交接文档
Hzqi_110 edited this page 4 years ago

问答搜索系统交接文档

GIT 分支说明

目前在192.168.20.122:3000 上的项目LjSearch上有4个分支,本来这个项目是为了开发亮健相关的搜索项目的。而官网的搜索暂时也没有在开发。

  1. master 原来的搜索的旧项目,原封不动搬上git
  2. maven 原来的搜索项目,使用maven构建,使用cargo插件挂载tomcat
  3. springboot-version 使用springboot重新重构的项目
  4. springboot-version-test springboot-version分支的测试项目,主要是配置文件的不同

master

原来的搜索项目是遗留项目,接手时因为没有文档,而该项目的搜索引擎部分是使用Lucene的,开发与维护都比较困难,因此没有就地继续开发。

maven

因为原来的搜索项目都是原生Java项目,依赖包、配置等都是原生导入的,难以维护,因此将其整理成maven项目,使用maven导入依赖,方便依赖的管理。然后因为项目是Web项目,需要部署到tomcat上的,但是原生部署tomcat会变得繁琐和依赖复杂,所以使用maven的cargo插件,直接使用cargo自动挂载tomcat。

springboot-version

这是目前项目的主要开发分支,本意是用来重构搜索项目的,由于搜索项目的资料不完善,还没有进行官网搜索的重构,而后来有新的项目:问答搜索系统,所以直接在此开发问答搜索系统。

该项目搜索引擎部分使用了ElasticSearch,后端承载是springboot,数据库连接层使用了mybatis-plus,为了减轻ES的IO压力和数据库的压力,使用了Kafka作为搜索数据增删改查的触发消息队列。

springboot-version-test

与分支springboot-version代码是一样的,为了方便测试部署和正式部署,将测试的配置和正式的配置分开。

问答搜索系统

该分支拥有四个模块,用于区分不同的功能和依赖。

  • search-common 通用依赖、工具模块
  • search-models 项目所用到的实体类型、表映射对象、DAO层
  • search-service-quiz 问答搜索项目核心业务逻辑
  • search-web Web应用公用配置、入口模块

先简单说明下spring-boot的额外主要maven依赖:

  • org.springframework.kafka # spring-kafka springboot的kafka操作依赖
  • org.springframwwork.boot # spring-boot-starter-data-elasticsearch springboot的elasticsearch操作依赖,让操作es像使用JPA一样简单便捷

search-common

目前这个模块里主要存放一个Controller层响应的类型结构ResultObject和两个元祖类型Tuple

search-models

表格详细说明:(com.liangjian.search)

目录位置 名称 说明
config DBConfig mybatis-plus的配置
entity/param IdOffsetLimit QAProblemMapper的一个查询参数
entity QAProblem 「问题」实例的类型
entity QAReply 「回答」实例的类型(没有实际使用)
entity QAProblemTag 「问题标签」实例的类型
entity QAProblemJobFailedMsg Kafka的问题任务实例类型
es QAProblemEs 存储于ES中的「问题」的类型(结构与entity中的稍不同)
es QAReplyEs 存储与ES中的「回答」的类型(实际上没有使用)
kafka QAProblemJob Kafka队列用于存储的任务类型
kafka CrudType Kafka队列存储的任务中增删改查的类型

非代码说明:(resources目录中)

目录位置 名称 说明
es_index_mapping qa_problem.json ES中「问题」的文档mapping信息
es_index_setting qa_problem.json ES中「问题」的文档配置信息
mapper QAProblemMapper.xml 「问题」的复杂查询SQL

问题查询SQL说明

select a.*, c.replyCount, b.ReplyerName replyerName, b.CreateTime lastReply, b.Content content
        from QAProblem a
        LEFT JOIN (
        select a.Id, count(1) replyCount from QAProblem a left join QAReply b on a.Id = b.ProblemId where b.DoctorId > 0 and b.Level = 1 GROUP BY a.Id ) c on a.Id = c.Id
        LEFT JOIN (
        select b.ProblemId, max(b.Id) id from QAReply b GROUP BY b.ProblemId) d on a.Id = d.ProblemId
        LEFT JOIN QAReply b on a.Id = b.ProblemId and b.Id = d.id
        <if test="id != null">
            Where a.Id = #{id}
        </if>
        ORDER BY ReleaseTime Desc
        offset #{offset} row fetch next #{limit} row only

在数据库中查询「问题」的同时,将与这个「问题」相关联的「回答」查询出来,要求是DoctorId > 0 and Level = 1,聚合计算其「回答」个数、取最新的「回答」的日期、回答,一同存到ES的「问题」中。其ES「问题」结构为:(「问题」全部字段信息、「回答」数、最新「回答」的回复、最新「回答」的名称、最新「回答」的日期)。

ES部分说明

目前ElasticSearch版本使用的是7.5.1,安装了插件analysis-icu analysis-ikanalysis-pinyin,搜索部分是使用中文+中文拼音的方式进行搜索的。

search-web

作为入口模块和通用配置模块,这个模块没有什么实际的功能,只有通用的配置:

目录 名称 说明
config ElasticsearchConfig spring-boot结合ElasticSearch的配置
config KafkaConfig spring-boot结合Kafka的配置
config SwaggerConfig swagger文档配置

search-service-quiz

这个模块是问答搜索系统的核心业务部分,会重点说明,下面会使用mermiad代码语法来绘制流程图等,如果无法观看,下载本文档使用Typora打开。或者复制代码到https://mermaid-js.github.io/mermaid-live-editor/进行查看。

Controller层只是进行请求-响应的逻辑,不过多说明,从Service层开始:

目录 名称 说明
service IQAProblemService 查询逻辑及实际ES的CRUD逻辑
service IQAProblemFailedMsgService (暂时没有实际使用)
service IQAProblemTagService (暂时没有时间使用)
service QAProblemJbListener Kafka队列消费、处理逻辑

搜索逻辑

graph TD
start[开始] --> sp1[参数:keywork, page, size]
sp1 --> sp2[构造ES查询条件]
query[条件: state为1,tags或title与keyword有相似度的] --> sp2
sp2 --> sp3[第一次查询,获取最高分]
sp3 --> cond{是否成功}
cond --> |YES| sp4[调整要求的最低分,分页查询]
sp4 --> finish[返回响应结果]
cond --> |No| sp5[不修改最高分,直接分页查询]
sp5 --> finish

查看图片

需要注意的是,「修改要求的最低分」这个操作是为了排除大量的低分的不相关的结果,而目前的查询条件已经没有出现这种情况了,但是不排除以后不会有业务上的变更问题,所以代码我还留着,把要求的最低分设置成0。

联想逻辑

graph TD
start[开始] --> sp1[参数keyword]
sp1 --> sp2[构造条件start = 1]
sp1 --> cond{keyword是否为中文}
cond --> |Yes| sp3[添加条件:tags,title使用中文搜索]
cond --> |No| sp4[添加条件:tags,title使用拼音搜索]
sp2 --> sp5[搜索前10条]
sp3 --> sp5
sp4 --> sp5
sp5 --> sp6[仅取标题]

查看图片

逻辑与直接关键字搜索类似,但是仅查最前10条,而当输入的是纯ASCII字符的时候就要使用拼音匹配。

更新Es端数据的CRUD逻辑

这块为了降低实时的数据库压力和ES压力,使用了Kafka队列来存储操作的任务。

graph TD
subgraph kafka
start1[开始] --> ksp1[消费任务队列]
ksp1 --> ksp2[根据任务CRUD类型消费]
ksp2 --> ksp3[mybstis查询出拼接好的结构]
ksp3 --> ksp4[更新ES数据]
ksp4 --> kcond{是否成功}
kcond --> |Yes| ksp1
kcond --> |No| ksp5[丢入error队列]

start2[错误队列开始] --> esp1[消费队列]
esp1 --> esp2[根据任务CRUD类型消费]
esp2 --> esp3[mybstis查询出拼接好的结构]
esp3 --> esp4[更新ES数据]
esp4 --> econd{是否成功}
econd --> |Yes| esp1
econd --> |No| esp5[序列化到数据库,人工处理]
end

subgraph controller
start[开始] --> sp1[restful请求,参数均为'问题'的数据库id]
sp1 --> sp2[根据restful方法和参数构建kafka任务]
sp2 --> sp3[丢进kafka消息队列]
sp3 --> finish1[请求-响应完毕]
end

sp3 --> ksp1
ksp5 --> esp1

查看图片

目前的逻辑就是这样的。

备注

这个项目仍涉及到前台的使用端、数据爬取段、ElasticSearch管理端,有什么问题需要与各个部分沟通。

  • 编写日期: 2020-03-11