ES使用实例

ES安装
开发环境使用ES单机环境,启动ES服务端。
注意:旧的ES环境,可以删除elasticsearch-6.2.1\data\nodes目录以完全清除ES环境。
安装elasticsearch-head并启动

创建索引库
创建xc_course索引库,一个分片,0个副本。

创建映射
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-input-jdbc 是ruby开发的,先下载ruby并安装
下载地址: https://rubyinstaller.org/downloads/
下载2.5版本即可。
安装完成查看是否安装成功
在这里插入图片描述
Logstash5.x以上版本本身自带有logstash-input-jdbc,6.x版本本身不带logstash-input-jdbc插件,需要手动安装
在这里插入图片描述
安装成功后我们可以在logstash根目录下的以下目录查看对应的插件版本
在这里插入图片描述

创建模板文件
Logstash的工作是从MySQL中读取数据,向ES中创建索引,这里需要提前创建mapping的模板文件以便logstash
使用。
在logstach的config目录创建xc_course_template.json,内容如下:
本教程的xc_course_template.json目录是:D:/ElasticSearch/logstash-6.2.1/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中写入索引。
参考https://www.elastic.co/guide/en/logstash/current/plugins-inputs-jdbc.html
配置输入数据源和输出数据源。

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 => mysql
# the path to our downloaded jdbc driver
jdbc_driver_library => "F:/develop/maven/repository3/mysql/mysql‐connector‐java/5.1.41/mysql‐
connector‐java‐5.1.41.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
last_run_metadata_path => "D:/ElasticSearch/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"
template =>"D:/ElasticSearch/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:/ElasticSearch/logstash-6.2.1/config/logstash_metadata记录执行时间下次以此
时间为基准进行增量同步数据到索引库。

测试
启动logstash.bat:
.\logstash.bat ‐f …\config\mysql.conf
在这里插入图片描述
修改course_pub中的数据,并且修改timestamp为当前时间,观察Logstash日志是否读取到要索引的数据。
最后用head登录ES查看索引文档内容是否修改
在这里插入图片描述

1、配置appliction.yml

server:
port: 40100
spring:
application:
name: xc‐search‐service
elasticsearch:
hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔
course:
index: xc_course
type: doc

2、配置RestHighLevelClient和RestClient

package com.xuecheng.search.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ElasticsearchConfig {
@Value("${xuecheng.elasticsearch.hostlist}")
private String hostlist;
@Bean
public RestHighLevelClient restHighLevelClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")
[1]), "http");
}
//创建RestHighLevelClient客户端
return new RestHighLevelClient(RestClient.builder(httpHostArray));
}
@Bean
public RestClient restClient(){
//解析hostlist配置信息
String[] split = hostlist.split(",");
//创建HttpHost数组,其中存放es主机和端口的配置信息
HttpHost[] httpHostArray = new HttpHost[split.length];
for(int i=0;i<split.length;i++){
String item = split[i];
httpHostArray[i] = new HttpHost(item.split(":")[0], Integer.parseInt(item.split(":")
[1]), "http");
}
return RestClient.builder(httpHostArray).build();
}
}

API

@Api(value = "课程搜索",description = "课程搜索",tags = {"课程搜索"})
public interface EsCourseControllerApi {
@ApiOperation("课程搜索")
public QueryResponseResult<CoursePub> list(int page,int size,
CourseSearchParam courseSearchParam) throws IOException;
}

Service
1)在appliction.yml中配置source_field‘

elasticsearch:
hostlist: 127.0.0.1:9200 #多个结点中间用逗号分隔
course:
index: xc_course
type: doc
source_field:
id,name,grade,mt,st,charge,valid,pic,qq,price,price_old,status,studymodel,teachmode,expires,pub_
time,start_time,end_time

2)service完整代码如下

@Service
public class EsCourseService {
private static final Logger LOGGER = LoggerFactory.getLogger(EsCourseService.class);
@Value("${xuecheng.elasticsearch.course.index}")
private String es_index;
@Value("${xuecheng.elasticsearch.course.type}")
private String es_type;
@Value("${xuecheng.elasticsearch.course.source_field}")
private String source_field;
@Autowired
RestHighLevelClient restHighLevelClient;
public QueryResponseResult<CoursePub> list(int page,int size,CourseSearchParamcourseSearchParam) {
//设置索引
SearchRequest searchRequest = new SearchRequest(es_index);
//设置类型
searchRequest.types(es_type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//source源字段过虑
String[] source_fields = source_field.split(",");
searchSourceBuilder.fetchSource(source_fields, new String[]{});
//关键字
if(StringUtils.isNotEmpty(courseSearchParam.getKeyword())){
//匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder =
QueryBuilders.multiMatchQuery(courseSearchParam.getKeyword(), "name",
"teachplan","description");
//设置匹配占比
multiMatchQueryBuilder.minimumShouldMatch("70%");
//提升另个字段的Boost值
multiMatchQueryBuilder.field("name",10);
boolQueryBuilder.must(multiMatchQueryBuilder);
}
//布尔查询
searchSourceBuilder.query(boolQueryBuilder);
//请求搜索
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("xuecheng search error..{}",e.getMessage());
return new QueryResponseResult(CommonCode.SUCCESS,new QueryResult<CoursePub>());
}
//结果集处理
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
//记录总数
long totalHits = hits.getTotalHits();
//数据列表
List<CoursePub> list = new ArrayList<>();
for (SearchHit hit : searchHits) {
CoursePub coursePub = new CoursePub();
//取出source
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
    //取出名称
String name = (String) sourceAsMap.get("name");
coursePub.setName(name);
//图片
String pic = (String) sourceAsMap.get("pic");
coursePub.setPic(pic);
//价格
Float price = null;
try {
if(sourceAsMap.get("price")!=null ){

price = Float.parseFloat((String) sourceAsMap.get("price"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice(price);
Float price_old = null;
try {
if(sourceAsMap.get("price_old")!=null ){
price_old = Float.parseFloat((String) sourceAsMap.get("price_old"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice_old(price_old);
list.add(coursePub);
}
QueryResult<CoursePub> queryResult = new QueryResult<>();
queryResult.setList(list);
queryResult.setTotal(totalHits);
QueryResponseResult<CoursePub> coursePubQueryResponseResult = new
QueryResponseResult<CoursePub>(CommonCode.SUCCESS,queryResult);
return coursePubQueryResponseResult;
}
}

按分类和难度等级搜索

@Service
public class EsCourseService {
private static final Logger LOGGER = LoggerFactory.getLogger(EsCourseService.class);
@Value("${xuecheng.elasticsearch.course.index}")
private String es_index;
@Value("${xuecheng.elasticsearch.course.type}")
private String es_type;
@Value("${xuecheng.elasticsearch.course.source_field}")
private String source_field;
@Autowired
RestHighLevelClient restHighLevelClient;
public QueryResponseResult<CoursePub> list(int page,int size,CourseSearchParam
courseSearchParam) {
//设置索引
SearchRequest searchRequest = new SearchRequest(es_index);
//设置类型
searchRequest.types(es_type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//source源字段过虑
String[] source_fields = source_field.split(",");
searchSourceBuilder.fetchSource(source_fields, new String[]{});
//关键字
if(StringUtils.isNotEmpty(courseSearchParam.getKeyword())){
//匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder =
QueryBuilders.multiMatchQuery(courseSearchParam.getKeyword(), "name",
"teachplan","description");
//设置匹配占比
multiMatchQueryBuilder.minimumShouldMatch("70%");
//提升另个字段的Boost值
multiMatchQueryBuilder.field("name",10);
boolQueryBuilder.must(multiMatchQueryBuilder);
}
//过虑
if(StringUtils.isNotEmpty(courseSearchParam.getMt())){
boolQueryBuilder.filter(QueryBuilders.termQuery("mt",courseSearchParam.getMt()));
}
if(StringUtils.isNotEmpty(courseSearchParam.getSt())){
boolQueryBuilder.filter(QueryBuilders.termQuery("st",courseSearchParam.getSt()));
}
if(StringUtils.isNotEmpty(courseSearchParam.getGrade())){
boolQueryBuilder.filter(QueryBuilders.termQuery("grade",courseSearchParam.getGrade()));
}
//布尔查询
searchSourceBuilder.query(boolQueryBuilder);
//请求搜索
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("xuecheng search error..{}",e.getMessage());
return new QueryResponseResult(CommonCode.SUCCESS,new QueryResult<CoursePub>());
}
//结果集处理
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
//记录总数
long totalHits = hits.getTotalHits();
//数据列表
List<CoursePub> list = new ArrayList<>();
for (SearchHit hit : searchHits) {
CoursePub coursePub = new CoursePub();
//取出source
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//取出名称
String name = (String) sourceAsMap.get("name");
coursePub.setName(name);
//图片
String pic = (String) sourceAsMap.get("pic");
coursePub.setPic(pic);
//价格
Float price = null;
try {
if(sourceAsMap.get("price")!=null ){
price = Float.parseFloat((String) sourceAsMap.get("price"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice(price);
Float price_old = null;
try {
if(sourceAsMap.get("price_old")!=null ){
price_old = Float.parseFloat((String) sourceAsMap.get("price_old"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice_old(price_old);
list.add(coursePub);
}
QueryResult<CoursePub> queryResult = new QueryResult<>();
queryResult.setList(list);
queryResult.setTotal(totalHits);
QueryResponseResult<CoursePub> coursePubQueryResponseResult = new
QueryResponseResult<CoursePub>(CommonCode.SUCCESS,queryResult);
return coursePubQueryResponseResult;
}
}

分页与高亮

@Service
public class EsCourseService {
private static final Logger LOGGER = LoggerFactory.getLogger(EsCourseService.class);
@Value("${xuecheng.elasticsearch.course.index}")
private String es_index;
@Value("${xuecheng.elasticsearch.course.type}")
private String es_type;
@Value("${xuecheng.elasticsearch.course.source_field}")
private String source_field;
@Autowired
RestHighLevelClient restHighLevelClient;
public QueryResponseResult<CoursePub> list(int page,int size,CourseSearchParam
courseSearchParam) {
//设置索引
SearchRequest searchRequest = new SearchRequest(es_index);
//设置类型
searchRequest.types(es_type);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//source源字段过虑
String[] source_fields = source_field.split(",");
searchSourceBuilder.fetchSource(source_fields, new String[]{});
//关键字
if(StringUtils.isNotEmpty(courseSearchParam.getKeyword())){
//匹配关键字
MultiMatchQueryBuilder multiMatchQueryBuilder =
QueryBuilders.multiMatchQuery(courseSearchParam.getKeyword(), "name",
"teachplan","description");
//设置匹配占比
multiMatchQueryBuilder.minimumShouldMatch("70%");
//提升另个字段的Boost值
multiMatchQueryBuilder.field("name",10);
boolQueryBuilder.must(multiMatchQueryBuilder);
}
//过虑
if(StringUtils.isNotEmpty(courseSearchParam.getMt())){
boolQueryBuilder.filter(QueryBuilders.termQuery("mt",courseSearchParam.getMt()));
}
if(StringUtils.isNotEmpty(courseSearchParam.getSt())){
boolQueryBuilder.filter(QueryBuilders.termQuery("st",courseSearchParam.getSt()));
}
if(StringUtils.isNotEmpty(courseSearchParam.getGrade())){
boolQueryBuilder.filter(QueryBuilders.termQuery("grade",courseSearchParam.getGrade()));
}
//分页
if(page<=0){
page = 1;
}
if(size<=0){
size = 20;
}
int start = (page‐1)*size;
searchSourceBuilder.from(start);
searchSourceBuilder.size(size);
//布尔查询
searchSourceBuilder.query(boolQueryBuilder);
//高亮设置
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.preTags("<font class='eslight'>");
highlightBuilder.postTags("</font>");
//设置高亮字段
highlightBuilder.fields().add(new HighlightBuilder.Field("name"));
searchSourceBuilder.highlighter(highlightBuilder);
//请求搜索
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = null;
try {
searchResponse = restHighLevelClient.search(searchRequest);
} catch (IOException e) {
e.printStackTrace();
LOGGER.error("xuecheng search error..{}",e.getMessage());
return new QueryResponseResult(CommonCode.SUCCESS,new QueryResult<CoursePub>());
}
//结果集处理
SearchHits hits = searchResponse.getHits();
SearchHit[] searchHits = hits.getHits();
//记录总数
long totalHits = hits.getTotalHits();
//数据列表
List<CoursePub> list = new ArrayList<>();
for (SearchHit hit : searchHits) {
CoursePub coursePub = new CoursePub();
//取出source
Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//取出名称
String name = (String) sourceAsMap.get("name");
//取出高亮字段内容
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if(highlightFields!=null){
HighlightField nameField = highlightFields.get("name");
if(nameField!=null){
Text[] fragments = nameField.getFragments();
StringBuffer stringBuffer = new StringBuffer();
for (Text str : fragments) {
stringBuffer.append(str.string());
}
name = stringBuffer.toString();
}
}
coursePub.setName(name);
//图片
String pic = (String) sourceAsMap.get("pic");
coursePub.setPic(pic);
//价格
Float price = null;
try {
if(sourceAsMap.get("price")!=null ){
price = Float.parseFloat((String) sourceAsMap.get("price"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice(price);
Float price_old = null;
try {
if(sourceAsMap.get("price_old")!=null ){
price_old = Float.parseFloat((String) sourceAsMap.get("price_old"));
}
} catch (Exception e) {
e.printStackTrace();
}
coursePub.setPrice_old(price_old);
list.add(coursePub);
}
QueryResult<CoursePub> queryResult = new QueryResult<>();
queryResult.setList(list);
queryResult.setTotal(totalHits);
QueryResponseResult<CoursePub> coursePubQueryResponseResult = new
QueryResponseResult<CoursePub>(CommonCode.SUCCESS,queryResult);
return coursePubQueryResponseResult;
}
}

Controller

@RestController
@RequestMapping("/search/course")
public class EsCourseController implements EsCourseControllerApi {
@Autowired
EsCourseService esCourseService;
@Override
@GetMapping(value="/list/{page}/{size}")
public QueryResponseResult<CoursePub> list(@PathVariable("page") int page,
@PathVariable("size") int size, CourseSearchParam courseSearchParam) throws IOException {
return esCourseService.list(page,size,courseSearchParam);
}
}

猜你喜欢

转载自blog.csdn.net/qq_43843037/article/details/88090112
今日推荐