<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity5</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.59</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.0</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<excludes>
<exclude>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
# ServerProperties
server.port=8080
server.servlet.context-path=/es
# ThymeleafProperties
spring.thymeleaf.cache=false
# DataSourceProperties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/es?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
spring.datasource.hikari.maximum-pool-size=15
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.idle-timeout=30000
# MybatisProperties
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.boot.es.pojo
mybatis.configuration.useGeneratedKeys=true
mybatis.configuration.mapUnderscoreToCamelCase=true
# ElasticsearchProperties
# spring.data.elasticsearch.cluster-name=my-application
# spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
elasticSearch.url=101.35.83.200:9200
package com.boot.es.config;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.elasticsearch.client.ClientConfiguration;
import org.springframework.data.elasticsearch.client.RestClients;
@Configuration
public class EsConfig{
@Value("${elasticSearch.url}")
private String esUrl;
//localhost:9200 写在配置文件中就可以了 配置es客户端
@Bean
RestHighLevelClient client() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo(esUrl)//elasticsearch地址
.build();
return RestClients.create(clientConfiguration).rest();
}
}
package com.nowcoder.community.dao.elasticsearch;
import com.nowcoder.community.entity.DiscussPost;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
import org.springframework.stereotype.Repository;
/*
ElasticsearchRepository<DiscussPost, Integer>
DiscussPost:接口要处理的实体类
Integer:实体类中的主键是什么类型
ElasticsearchRepository:父接口,其中已经事先定义好了对es服务器访问的增删改查各种方法。Spring会给它自动做一个实现,我们直接去调就可以了。
*/
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {
}
package com.boot.es.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import java.util.Date;
@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "elastic-test", /*type = "_doc",*/ shards = 6, replicas = 3)
public class DiscussPost {
@Id
private int id;
@Field(type = FieldType.Integer)
private int userId;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String title;
@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")
private String content;
@Field(type = FieldType.Integer)
private int type;
@Field(type = FieldType.Integer)
private int status;
@Field(type = FieldType.Date)
private Date createTime;
@Field(type = FieldType.Integer)
private int commentCount;
@Field(type = FieldType.Double)
private double score;
}
package com.nowcoder.community.dao;
import com.nowcoder.community.entity.DiscussPost;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
import java.util.List;
@Mapper
@Repository
public interface DiscussPostMapper {
List<DiscussPost> selectDiscussPosts(int userId, int offset, int limit);
// @Param注解用于给参数取别名,
// 如果只有一个参数,并且在<if>里使用,则必须加别名.
int selectDiscussPostRows(@Param("userId") int userId);
int insertDiscussPost(DiscussPost discussPost);
DiscussPost selectDiscussPostById(int id);
int updateCommentCount(int id, int commentCount);
}
mapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.boot.es.dao.DiscussPostMapper">
<sql id="selectFields">
id, user_id, title, content, type, status, create_time, comment_count, score
</sql>
<sql id="insertFields">
user_id, title, content, type, status, create_time, comment_count, score
</sql>
<select id="selectDiscussPosts" resultType="DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where status != 2
<if test="userId!=0">
and user_id = #{userId}
</if>
order by type desc, create_time desc
limit #{offset}, #{limit}
</select>
<select id="selectDiscussPostRows" resultType="int">
select count(id)
from discuss_post
where status != 2
<if test="userId!=0">
and user_id = #{userId}
</if>
</select>
<insert id="insertDiscussPost" parameterType="DiscussPost" keyProperty="id">
insert into discuss_post(<include refid="insertFields"></include>)
values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
</insert>
<select id="selectDiscussPostById" resultType="DiscussPost">
select <include refid="selectFields"></include>
from discuss_post
where id = #{id}
</select>
<update id="updateCommentCount">
update discuss_post set comment_count = #{commentCount} where id = #{id}
</update>
</mapper>
CREATE DATABASE /*!32312 IF NOT EXISTS*/`es` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `es`;
/*Table structure for table `discuss_post` */
DROP TABLE IF EXISTS `discuss_post`;
CREATE TABLE `discuss_post` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` varchar(45) DEFAULT NULL COMMENT '哪个用户发布的',
`title` varchar(100) DEFAULT NULL,
`content` text,
`type` int(11) DEFAULT NULL COMMENT '0-普通; 1-置顶;',
`status` int(11) DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;',
`create_time` timestamp NULL DEFAULT NULL,
`comment_count` int(11) DEFAULT NULL,
`score` double DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `index_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=281 DEFAULT CHARSET=utf8;
/*Data for the table `discuss_post` */
insert into `discuss_post`(`id`,`user_id`,`title`,`content`,`type`,`status`,`create_time`,`comment_count`,`score`) values (109,'101','因特网求职暖春计划','今年的就业形势,确实不容乐观。过了个年,仿佛跳水一般,整个讨论区哀鸿遍野!19届真的没人要了吗?!18届被优化真的没有出路了吗?!大家的“哀嚎”与“悲惨遭遇”牵动了每日潜伏于讨论区的牛客小哥哥小姐姐们的心,于是牛客决定:是时候为大家做点什么了!为了帮助大家度过“寒冬”,牛客网特别联合60+家企业,开启互联网求职暖春计划,面向18届&19届,拯救0 offer!',0,0,'2019-04-04 11:53:36',0,0),(275,'11','我是管理员','我是管理员,你们都老实点!',1,1,'2019-05-16 18:58:44',12,1751.2900346113624),(276,'149','新人报道','新人报道,请多关照!',0,0,'2019-05-17 15:50:18',6,1751.806179973984),(277,'149','Spring Cache','Spring Cache RedisCacheManager',0,0,'2019-05-17 17:06:54',38,1752.5797835966168),(280,'149','事务','事务的4个特性,包括原子性、一致性、隔离性、持久性。',0,0,'2019-05-20 17:41:30',16,1755.2095150145426);
package org.example;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.boot.es.App;
import com.boot.es.dao.DiscussPostMapper;
import com.boot.es.dao.DiscussPostRepository;
import com.boot.es.pojo.DiscussPost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortBuilders;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.ContextConfiguration;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@SpringBootTest
@ContextConfiguration(classes = App.class)
public class ElasticsearchTests {
@Autowired
private DiscussPostMapper discussMapper;
@Autowired
private DiscussPostRepository discussRepository;
@Qualifier("client")
@Autowired
private RestHighLevelClient restHighLevelClient;
//判断某id的文档(数据库中的行)是否存在
@Test
public void testExist(){
boolean exists =discussRepository.existsById(1);
System.out.println(exists);
}
//一次保存一条数据
@Test
public void testInsert() {
//把id为241的DiscussPost的对象保存到elastic-test索引(es的索引相当于数据库的表)
/**
* 不能插入空数据 报错
*/
DiscussPost save = discussRepository.save(discussMapper.selectDiscussPostById(109));
System.out.println(save.getContent());
}
//一次保存多条数据
@Test
public void testInsertList() {
//把id为101的用户发的前100条帖子(List<DiscussPost>)存入es的discusspost索引(es的索引相当于数据库的表)
Iterable<DiscussPost> discussPosts = discussRepository.saveAll(discussMapper.selectDiscussPosts(149, 0, 100));
}
//通过覆盖原内容,来修改一条数据
@Test
public void testUpdate() {
DiscussPost post = discussMapper.selectDiscussPostById(109);
post.setContent("我是新人,使劲灌水。");
post.setTitle(null);//es中的title会设为null
discussRepository.save(post);
}
//修改一条数据
//覆盖es里的原内容 与 修改es中的内容 的区别:String类型的title被设为null,覆盖的话,会把es里的该对象的title也设为null;UpdateRequest,修改后该对象的title不变
@Test
void testUpdateDocument() throws IOException{
UpdateRequest request = new UpdateRequest("elastic-test", "109");
request.timeout("1s");
DiscussPost post = discussMapper.selectDiscussPostById(230);
post.setContent("我是新人,使劲灌水.");
post.setTitle(null);//es中的title会保存原内容不变
request.doc(JSON.toJSONString(post), XContentType.JSON);
UpdateResponse updateResponse = restHighLevelClient.update(request, RequestOptions.DEFAULT);
System.out.println(updateResponse.status());
}
//删除一条数据和删除所有数据
@Test
public void testDelete() {
//通过索引id删除
discussRepository.deleteById(109);//删除一条数据
//discussRepository.deleteAll();//删除所有数据
}
//不带高亮的查询
@Test
public void noHighlightQuery() throws IOException {
SearchRequest searchRequest = new SearchRequest("elastic-test");//discusspost是索引名,就是表名
//构建搜索条件
// SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
// .query(QueryBuilders.multiMatchQuery("互联网寒冬","title","content"))
// .sort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
// .sort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
// .sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
// .from(0)
// .size(10);
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
//在discusspost索引的title和content字段中都查询“互联网寒冬”
.query(QueryBuilders.multiMatchQuery("新人", "title", "content"))
// matchQuery是模糊查询,会对key进行分词:searchSourceBuilder.query(QueryBuilders.matchQuery(key,value));
// termQuery是精准查询:searchSourceBuilder.query(QueryBuilders.termQuery(key,value));
.sort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.sort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
//一个可选项,用于控制允许搜索的时间:searchSourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
.from(0)// 指定从哪条开始查询
.size(10);// 需要查出的总记录条数
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSONObject.toJSON(searchResponse));
List<DiscussPost> list = new LinkedList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
DiscussPost discussPost = JSONObject.parseObject(hit.getSourceAsString(), DiscussPost.class);
System.out.println(discussPost);
list.add(discussPost);
}
}
//带高亮的查询
@Test
public void highlightQuery() throws Exception{
SearchRequest searchRequest = new SearchRequest("elastic-test");//discusspost是索引名,就是表名
//高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.field("title");
highlightBuilder.field("content");
highlightBuilder.requireFieldMatch(false);
highlightBuilder.preTags("");
highlightBuilder.postTags("");
//构建搜索条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder()
.query(QueryBuilders.multiMatchQuery("新人", "title", "content"))
.sort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
.sort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
.sort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
.from(0)// 指定从哪条开始查询
.size(10)// 需要查出的总记录条数
.highlighter(highlightBuilder);//高亮
searchRequest.source(searchSourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println(JSONObject.toJSON(searchResponse));
List<DiscussPost> list = new LinkedList<>();
for (SearchHit hit : searchResponse.getHits().getHits()) {
DiscussPost discussPost = JSONObject.parseObject(hit.getSourceAsString(), DiscussPost.class);
// 处理高亮显示的结果
HighlightField titleField = hit.getHighlightFields().get("title");
if (titleField != null) {
discussPost.setTitle(titleField.getFragments()[0].toString());
}
HighlightField contentField = hit.getHighlightFields().get("content");
if (contentField != null) {
discussPost.setContent(contentField.getFragments()[0].toString());
}
System.out.println(discussPost);
list.add(discussPost);
}
}
}
完成springboot 整合 7.x的基本操作。