课程搜索需求分析
需求分析
1、根据分类搜索课程信息。
2、根据关键字搜索课程信息,搜索方式为全文检索,关键字需要匹配课程的名称、 课程内容。
3、根据难度等级搜索课程。
4、搜索结点分页显示。
搜索流程
1、课程管理服务将数据写到MySQL数据库
2、使用Logstash将MySQL数据库中的数据写到ES的索引库。
3、用户在前端搜索课程信息,请求到搜索服务。
4、搜索服务请求ES搜索课程信息。
课程索引
如何维护课程索引信息?
1、当课程向MySQL添加后同时将课程信息添加到索引库。
采用Logstach实现,Logstach会从MySQL中将数据采集到ES索引库。
2、当课程在MySQL更新信息后同时更新该课程在索引库的信息。
采用Logstach实现。
3、当课程在MySQL删除后同时将该课程从索引库删除。
手工写程序实现,在删除课程后将索引库中该课程信息删除。
准备课程索引信息
课程发布成功在MySQL数据库存储课程发布信息,此信息作为课程索引信息。
创建课程发布表
课程信息分布在course_base、course_pic等不同的表中。
课程发布成功为了方便进行索引将这几张表的数据合并在一张表中,作为课程发布信息。
创建course_pub表
创建课程发布表模型
在课程管理服务创建模型:
@Data @ToString @Entity @Table(name="course_pub") @GenericGenerator(name = "jpa‐assigned", strategy = "assigned") public class CoursePub implements Serializable { private static final long serialVersionUID = ‐916357110051689487L; @Id @GeneratedValue(generator = "jpa‐assigned") @Column(length = 32) private String id; private String name; private String users; private String mt; private String st; private String grade; private String studymodel; private String teachmode; private String description; private String pic;//图片 private Date timestamp;//时间戳 private String charge; private String valid; private String qq; private Float price; private Float price_old; private String expires; private String teachplan;//课程计划 @Column(name="pub_time") private String pubTime;//课程发布时间 }
修改课程发布
在课程管理服务定义dao:
1)创建course_pub表的dao
public interface CoursePubRepository extends JpaRepository<CoursePub, String> { }
2) 修改课程发布service
//保存CoursePub public CoursePub saveCoursePub(String id, CoursePub coursePub){ if(StringUtils.isNotEmpty(id)){ ExceptionCast.cast(CourseCode.COURSE_PUBLISH_COURSEIDISNULL); } CoursePub coursePubNew = null; Optional<CoursePub> coursePubOptional = coursePubRepository.findById(id); if(coursePubOptional.isPresent()){ coursePubNew = coursePubOptional.get(); } if(coursePubNew == null){ coursePubNew = new CoursePub(); } BeanUtils.copyProperties(coursePub,coursePubNew); //设置主键 coursePubNew.setId(id); //更新时间戳为最新时间 coursePub.setTimestamp(new Date()); //发布时间 SimpleDateFormat simpleDateFormat = new SimpleDateFormat("YYYY‐MM‐dd HH:mm:ss"); String date = simpleDateFormat.format(new Date()); coursePub.setPubTime(date); coursePubRepository.save(coursePub); return coursePub; } //创建coursePub对象 private CoursePub createCoursePub(String id){ CoursePub coursePub = new CoursePub(); coursePub.setId(id); //基础信息 Optional<CourseBase> courseBaseOptional = courseBaseRepository.findById(id); if(courseBaseOptional == null){ CourseBase courseBase = courseBaseOptional.get(); BeanUtils.copyProperties(courseBase, coursePub); } //查询课程图片 Optional<CoursePic> picOptional = coursePicRepository.findById(id); if(picOptional.isPresent()){ CoursePic coursePic = picOptional.get(); BeanUtils.copyProperties(coursePic, coursePub); } //课程营销信息 Optional<CourseMarket> marketOptional = courseMarketRepository.findById(id); if(marketOptional.isPresent()){ CourseMarket courseMarket = marketOptional.get(); BeanUtils.copyProperties(courseMarket, coursePub); } //课程计划 TeachplanNode teachplanNode = teachplanMapper.selectList(id); //将课程计划转成json String teachplanString = JSON.toJSONString(teachplanNode); coursePub.setTeachplan(teachplanString); return coursePub; }
修改课程发布方法,添加调用saveCoursePub方法的代码,添加部分的代码如下:
//课程发布 @Transactional public CoursePublishResult publish(String courseId){ .... //创建课程索引 //创建课程索引信息 CoursePub coursePub = createCoursePub(courseId); //向数据库保存课程索引信息 CoursePub newCoursePub = saveCoursePub(courseId, coursePub); if(newCoursePub==null){ //创建课程索引信息失败 ExceptionCast.cast(CourseCode.COURSE_PUBLISH_CREATE_INDEX_ERROR); } .... }
搭建ES环境
开发环境使用ES单机环境,启动ES服务端。
注意:旧的ES环境,可以删除elasticsearch-6.2.1data
odes目录以完全清除ES环境。
安装elasticsearch-head并启动。
创建索引库
创建索引库
创建xc_course索引库,一个分片,0个副本。
创建映射
在postman里添加映射
Post http://localhost:9200/xc_course/doc/_mapping
{ "properties" : { "description" : { "analyzer" : "ik_max_word", "search_analyzer": "ik_smart", "type" : "text" }, "grade" : { "type" : "keyword" }, "id" : { "type" : "keyword" }, "mt" : { "type" : "keyword" }, "name" : { "analyzer" : "ik_max_word", "search_analyzer": "ik_smart", "type" : "text" }, "users" : { "index" : false, "type" : "text" }, "charge" : { "type" : "keyword" }, "valid" : { "type" : "keyword" }, "pic" : { "index" : false, "type" : "keyword" }, "qq" : { "index" : false, "type" : "keyword" }, "price" : { "type" : "float" }, "price_old" : { "type" : "float" }, "st" : { "type" : "keyword" }, "status" : { "type" : "keyword" }, "studymodel" : { "type" : "keyword" }, "teachmode" : { "type" : "keyword" }, "teachplan" : { "analyzer" : "ik_max_word", "search_analyzer": "ik_smart", "type" : "text" }, "expires" : { "type" : "date", "format": "yyyy-MM-dd HH:mm:ss" }, "pub_time" : { "type" : "date", "format": "yyyy-MM-dd HH:mm:ss" }, "start_time" : { "type" : "date", "format": "yyyy-MM-dd HH:mm:ss" }, "end_time" : { "type" : "date", "format": "yyyy-MM-dd HH:mm:ss" } } }
Logstash 创建索引
Logstash是ES下的一款开源软件,它能够同时 从多个来源采集数据、转换数据,然后将数据发送到Eleasticsearch
中创建索引。
本项目使用Logstash将MySQL中的数据采用到ES索引中。
这里资料给的配套文件有很多坑,我把坑都踩了一遍,下面分享解决方法。
下载Logstash
下载Logstash6.2.1版本,和本项目使用的Elasticsearch6.2.1版本一致,版本要一致。
安装logstash-input-jdbc
这里解压老师提供的logstash-6.2.1.zip即可,此logstash中已集成了logstash-input-jdbc插件。
创建模板文件
Logstash的工作是从MySQL中读取数据,向ES中创建索引,这里需要提前创建mapping的模板文件以便logstash
使用。
在logstach的config目录创建xc_course_template.json,内容如下:
{ "mappings" : { "doc" : { "properties" : { "charge" : { "type" : "keyword" }, "description" : { "analyzer" : "ik_max_word", "search_analyzer" : "ik_smart", "type" : "text" }, "end_time" : { "format" : "yyyy-MM-dd HH:mm:ss", "type" : "date" }, "expires" : { "format" : "yyyy-MM-dd HH:mm:ss", "type" : "date" }, "grade" : { "type" : "keyword" }, "id" : { "type" : "keyword" }, "mt" : { "type" : "keyword" }, "name" : { "analyzer" : "ik_max_word", "search_analyzer" : "ik_smart", "type" : "text" }, "pic" : { "index" : false, "type" : "keyword" }, "price" : { "type" : "float" }, "price_old" : { "type" : "float" }, "pub_time" : { "format" : "yyyy-MM-dd HH:mm:ss", "type" : "date" }, "qq" : { "index" : false, "type" : "keyword" }, "st" : { "type" : "keyword" }, "start_time" : { "format" : "yyyy-MM-dd HH:mm:ss", "type" : "date" }, "status" : { "type" : "keyword" }, "studymodel" : { "type" : "keyword" }, "teachmode" : { "type" : "keyword" }, "teachplan" : { "analyzer" : "ik_max_word", "search_analyzer" : "ik_smart", "type" : "text" }, "users" : { "index" : false, "type" : "text" }, "valid" : { "type" : "keyword" } } } }, "template" : "xc_course" }
配置mysql.conf
在logstash的config目录下配置mysql.conf文件供logstash使用,logstash会根据mysql.conf文件的配置的地址从
MySQL中读取数据向ES中写入索引。
配置输入数据源和输出数据源。
input { stdin { } jdbc { jdbc_connection_string => "jdbc:mysql://localhost:3306/xc_course?useUnicode=true&characterEncoding=utf-8&useSSL=true&serverTimezone=UTC" # the user we wish to excute our statement as jdbc_user => "root" #密码记得加双引号 jdbc_password => "" # the path to our downloaded jdbc driver #配置本地仓库的mysql数据源jar的路径 jdbc_driver_library => "D:/Maven/m2/repository/mysql/mysql-connector-java/5.1.40/mysql-connector-java-5.1.40.jar" # the name of the driver class for mysql jdbc_driver_class => "com.mysql.jdbc.Driver" jdbc_paging_enabled => "true" jdbc_page_size => "50000" #要执行的sql文件 #statement_filepath => "/conf/course.sql" statement => "select * from course_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)" #定时配置 schedule => "* * * * *" record_last_run => true #配置本地logstash_metadata存放路径,上一次检索数据的时间点 last_run_metadata_path => "D:/ElasticSearch01/logstash-6.2.1/config/logstash_metadata" } } output { elasticsearch { #ES的ip地址和端口 hosts => "localhost:9200" #hosts => ["localhost:9200","localhost:9202","localhost:9203"] #ES索引库名称 index => "xc_course" document_id => "%{id}" document_type => "doc" #覆盖本地的conf的json数据,同上 template =>"D:/ElasticSearch01/logstash-6.2.1/config/xc_course_template.json" template_name =>"xc_course" template_overwrite =>"true" } stdout { #日志输出 codec => json_lines } }
说明:
1、ES采用UTC时区问题
ES采用UTC 时区,比北京时间早8小时,所以ES读取数据时让最后更新时间加8小时
where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)
2、logstash每个执行完成会在D:/ElasticSearch01/logstash-6.2.1/config/logstash_metadata记录执行时间下次以此
时间为基准进行增量同步数据到索引库。
测试
logstash.bat ‐f ..configmysql.conf
启动测试前,要把D:ElasticSearch01logstash-6.2.1bin下的logstash.bat的最后一行的classpath加双冒号,不然启动不了,报找不到启动类。
改好后重新启动
只查到一个数据,这里也有坑。要把logstash_metadata里的上一次记录的时间改比数据库所有的时间都要小!!
statement => "select * from course_pub where timestamp > date_add(:sql_last_value,INTERVAL 8 HOUR)"
改了后,可以看到可以查到所有数据了!
再去查看Elasticsearch,但是数据获取不到,显示不出来??数据已经在更新了啊!
有延时?无论我怎么重启,重装都是这样。。。
最后各种翻博客,卸载重装,查看Elasticsearch的日志文件,不断摸索才发现问题所在。
这里说的是xc_course_template.json的pub_time中的日期格式和postman里添加映射的日期格式两次不一致导致的!
这是PDF复制过去的锅,原先日期格式的"-"为中文格式,要改成英文格式!!
照着上面步骤再重新弄一遍,把坑避免,可以看到终于成功了!!!
最后总结一下出现的bug:
1.mysql.conf配置文件中的数据库密码加双引号
2.bin下的logstash.bat的最后一行的classpath加双冒号
3.要把logstash_metadata里的上一次记录的时间改比数据库所有的时间都要小
4.xc_course_template.json的pub_time中的日期格式和postman里添加映射的日期格式中的"-"为中文格式,要改成英文格式
遇到bug的时候不要急躁,要有耐心,一次不行就卸载重装再试。可以先做其他事分散注意力,一定找问题出在哪里,通过他人的博客寻求思路。
不断摸索,一定可以迎刃而解,可以提升自己解决bug的能力。