Spring Boot 与ElasticSearch
一、ElasticSearch 介绍
开源的 ElasticSearch 是目前全文搜索引擎的首选,它是一个分布式搜索服务,提供Restful API,它可以快速地存储、搜索和分析海量数据。底层基于 Lucene,采用多 shard(分片)的方式保证数据安全,并且提供自动 resharding 的功能,github 等大型站点也是采用 ElasticSearch 作为其搜索服务。
ElasticSearch 是面向文档的,它存储整个对象(文档),它使用 JSON 作为文档的序列化格式。一个 ElasticSearch 集群可以包含多个索引,相应的每个索引可以包含多个类型。这些不同的类型存储着多个文档,每个文档又有多个属性。
二、ElasticSearch 快速入门
1、dokcer 安装 ElasticSearch
dokcer pull elasticsearch:6.4.3 # 获取镜像 注意:如果后面整合 spring boot 的话,就要与 spring boot 的版本相对应,我后面创建的 spring boot 项目是 2.1.2 对应的 spring-data-elasticsearch 是 3.1.4,详情参考 springboot 官方:https://github.com/spring-projects/spring-data-elasticsearch 里面的对照表,版本不对应的话,后面用 spring data 使用 ES 的话可能会有问题。
[root@izwz9d74k4cznxtxjeeur9z ~]# docker run -d --name=ES -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms1024M -Xmx1024m" elasticsearch:6.4.3
# 第一个端口是web通信端口,第二个端口是节点通信端口,设置为了单节点模式并规定了运行时最小堆内存为1024M,最大堆内存为1024M(默认情况下,要占将近2个G)
输入 http://服务器地址:9200/,返回 JSON ,运行成功。
2、练习
使用软件 Postman 模拟发送 Restful 请求,练习参考官方文档。
索引雇员文档:第一个业务需求就是存储雇员数据。 这将会以雇员文档的形式存储:一个文档代表一个雇员。存储数据到 Elasticsearch 的行为叫做索引。
发送一个 put 请求,地址:http://x.x.x.x:9200/megacorp/employee/1,内容为:
{
"first_name" : "John",
"last_name" : "Smith",
"age" : 25,
"about" : "I love to go rock climbing",
"interests": [ "sports", "music" ]
}
点击 Send 后,返回响应结果:
将 put 请求变为 get 请求读取下刚索引的雇员文档,返回响应结果如下:
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [
"sports",
"music"
]
}
}
再索引 2 个雇员文档:
PUT /megacorp/employee/2
{
"first_name" : "Jane",
"last_name" : "Smith",
"age" : 32,
"about" : "I like to collect rock albums",
"interests": [ "music" ]
} PUT /megacorp/employee/3
{
"first_name" : "Douglas",
"last_name" : "Fir",
"age" : 35,
"about": "I like to build cabinets",
"interests": [ "forestry" ]
}
Tips:将 HTTP 命令由
PUT改为GET可以用来检索文档,同样的,可以使用DELETE命令来删除文档,以及使用HEAD指令来检查文档是否存在。如果想更新已存在的文档,只需再次PUT。
轻量级搜索:
GET /megacorp/employee/_search #获取所有雇员
返回结果:
{
"took": 59,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 1,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [
"music"
]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 1,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [
"sports",
"music"
]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "3",
"_score": 1,
"_source": {
"first_name": "Douglas",
"last_name": "Fir",
"age": 35,
"about": "I like to build cabinets",
"interests": [
"forestry"
]
}
}
]
}
}
搜索姓氏为
Smith的雇员:GET /megacorp/employee/_search?q=last_name:Smith
表达式搜索:Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。如:搜索姓氏为 Smith 的雇员且年龄大于 30,使用过滤器 filter ,它支持高效地执行一个结构化查询。
GET /megacorp/employee/_search
{
"query" : {
"bool": {
"must": {
"match" : {
"last_name" : "smith"
}
},
"filter": {
"range" : {
"age" : { "gt" : 30 }
}
}
}
}
}
全文搜索:Elasticsearch 默认按照相关性得分排序,即每个文档跟查询的匹配程度。
例:搜索下所有喜欢攀岩(rock climbing)的雇员:
GET /megacorp/employee/_search
{
"query" : {
"match" : {
"about" : "rock climbing"
}
}
}
返回 2 条记录,并按照相关性得分排序。
{
"took": 10,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 2,
"max_score": 0.53484553,
"hits": [
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 0.53484553,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [
"sports",
"music"
]
}
},
{
"_index": "megacorp",
"_type": "employee",
"_id": "2",
"_score": 0.26742277,
"_source": {
"first_name": "Jane",
"last_name": "Smith",
"age": 32,
"about": "I like to collect rock albums",
"interests": [
"music"
]
}
}
]
}
}
短语搜索:它不同于全文搜索,它是精确匹配的。
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
}
}
{
"took": 11,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.53484553,
"hits": [
{
"_index": "megacorp",
"_type": "employee",
"_id": "1",
"_score": 0.53484553,
"_source": {
"first_name": "John",
"last_name": "Smith",
"age": 25,
"about": "I love to go rock climbing",
"interests": [
"sports",
"music"
]
}
}
]
}
}
高亮搜索:自动将匹配到的词语加上高亮标签。
GET /megacorp/employee/_search
{
"query" : {
"match_phrase" : {
"about" : "rock climbing"
}
},
"highlight": {
"fields" : {
"about" : {}
}
}
}
三、Spring Boot 中使用 ElasticSearch
IDEA 通过 Spring Initializr 创建 Spring Boot 项目:
Spring Boot 可以使用下图中标注的方法来使用 ElasticSearch。
使用 jest 方式:
从上图中可知,jest 自动配置类还未生效,需要导入类 JestClient,所以添加 Maven 依赖。
<!-- https://mvnrepository.com/artifact/io.searchbox/jest -->
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.4</version>
</dependency>配置 jest.uris:
spring.elasticsearch.jest.uris=http://x.x.xx.:9200/
创建一个 Java Bean:
package com.yunche.elasticsearch.bean; import io.searchbox.annotations.JestId; /**
* @ClassName: Article
* @Description:
* @author: yunche
* @date: 2019/02/04
*/
public class Article { @JestId //主键
private Integer id;
private String name;
private String author;
private String content; public Integer getId() {
return id;
} @Override
public String toString() {
return "Article{" +
"id=" + id +
", name='" + name + '\'' +
", author='" + author + '\'' +
", content='" + content + '\'' +
'}';
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getAuthor() {
return author;
} public void setAuthor(String author) {
this.author = author;
} public String getContent() {
return content;
} public void setContent(String content) {
this.content = content;
}
}
单元测试:
package com.yunche.elasticsearch; import com.yunche.elasticsearch.bean.Article;
import io.searchbox.client.JestClient;
import io.searchbox.core.Index;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner; import java.io.IOException; @RunWith(SpringRunner.class)
@SpringBootTest
public class ElasticsearchApplicationTests { @Test
public void contextLoads() {
} @Autowired
JestClient jestClient; /**
* 索引一个文档
*/
@Test
public void indexArticle() {
Article article = new Article();
article.setId(1);
article.setAuthor("火星引力");
article.setName("逆天邪神");
article.setContent("掌天毒之珠,承邪神之血,修逆天之力。一代邪神,君临天下。"); //构建一个索引用于索引
Index index = new Index.Builder(article).index("yunche").type("novels").build(); try {
//索引文档
jestClient.execute(index);
} catch (IOException e) {
e.printStackTrace();
}
}
}方法无异常,获取该文档,结果如下:
/**
* 全文搜索
*/
@Test
public void search() { String query = "{\n" +
" \"query\" : {\n" +
" \"match\" : {\n" +
" \"name\" : \"逆天邪神\"\n" +
" }\n" +
" }\n" +
"}";
Search search = new Search.Builder(query).addIndex("yunche").addType("novels").build(); try {
SearchResult result = jestClient.execute(search);
//打印
for (SearchResult.Hit<Article, Void> hit : result.getHits(Article.class)) {
System.out.println(hit.source);
} /*Output:Article{id=1, name=' 逆天邪神 ', author=' 火星引力 ', content=' 掌天毒之珠,承邪神之血,修逆天之力。一代邪神,君临天下。'}*/ } catch (IOException e) {
e.printStackTrace();
}
}
Spring Data 方式:
application.properties:
spring.data.elasticsearch.cluster-name=docker-cluster # 注意填写名字,通过访问 9200 端口返回的 json 数据里面 "cluster_name"节点
spring.data.elasticsearch.cluster-nodes=x.x.x.x:9300
面向接口的方式:
package com.yunche.elasticsearch.repository; import com.yunche.elasticsearch.bean.Anime;
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; /**
* @ClassName: AnimeRepository
* @Description:
* @author: yunche
* @date: 2019/02/04
*/
public interface AnimeRepository extends ElasticsearchRepository<Anime, Integer> {
}
package com.yunche.elasticsearch.bean; import org.springframework.data.elasticsearch.annotations.Document; /**
* @ClassName: Anime
* @Description:
* @author: yunche
* @date: 2019/02/04
*/
//指定索引、类型
@Document(indexName = "yunche", type = "anime")
public class Anime {
private Integer id;
private String name;
private String summary; public Integer getId() {
return id;
} public void setId(Integer id) {
this.id = id;
} public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public String getSummary() {
return summary;
} public void setSummary(String summary) {
this.summary = summary;
}
}@Autowired
AnimeRepository animeRepository; /**
* 以面向接口的方式使用 ES,
* 索引一个动漫文档
*/
@Test
public void test01() {
Anime anime = new Anime();
anime.setId(1);
anime.setName("五等分的花嫁");
anime.setSummary("一直过着贫困生活的高中二年级学生·上杉风太郎,找到了一份条件非常好的家庭教师兼职。然而,要教导的学生居然是同级生!而且还是五胞胎!!虽然都是美少女,但同时也是“将要留级”、“讨厌学习”的问题学生们!最开始的任务就是要取得这些女孩们的信任……!?每天都热闹喧嚣!中野家的五姐妹所带来的可爱度 500%的五个不一样的恋爱喜剧,就此开幕!!"); animeRepository.index(anime);
}
public interface AnimeRepository extends ElasticsearchRepository<Anime, Integer> {
//类似于 JPA 面向接口,只需定义方法不需要实现
List<Anime> findAnimeByNameLike(String name);
}
/**
* 测试下搜索
*/
@Test
public void test02() {
NativeSearchQuery searchQuery = new NativeSearchQuery(QueryBuilders.matchQuery("summary", "五胞胎 美少女"));
for (Anime anime : animeRepository.search(searchQuery)) {
System.out.println(anime.getSummary());
}
} /**
* 模糊查找
*/
@Test
public void test03() {
for (Anime anime : animeRepository.findAnimeByNameLike("五等分")) {
System.out.println(anime.getSummary());
}
}
ElasticsearchTemplate:
@Autowired
public ElasticsearchTemplate template;
/**
* 索引一个 Anime 文档
*/
@Test
public void test04() {
Anime anime = new Anime();
anime.setId(2);
anime.setName("约会大作战");
anime.setSummary("人类遭遇了名为“空间震”的新型灾害。震荡空间、将一切破坏殆尽的这一灾厄,是由于存在于临界的精灵出现这个世界上时而发生的。为了阻止空间震,使人类免受灾厄而必须采取的措施,是使用武力歼灭精灵,或者是——“与其约会,使其娇羞”!让精灵娇羞,再通过“接吻”即可封印其力量——拥有这种能力的高中生·五河士道,为了人类的和平,也为了拯救精灵们——士道展开了和她们之间的“约会”。对士道敞开心扉的精灵·十香、四糸乃、琴里、耶俱矢、夕弦、美九。为了歼灭精灵而行动的“AST”。企图利用精灵的”DEM”。尝试与精灵和平交流的“拉塔托斯克”。以及,需要令其娇羞的新精灵——围绕着这一切,新的战争(约会)开始了——"); IndexQuery indexQuery = new IndexQueryBuilder().withIndexName("yunche").withType("anime").withId(anime.getId().toString()).withObject(anime).build();
template.index(indexQuery);
}
@Test
public void test05() {
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.queryStringQuery("五").field("summary")).build(); for (Anime anime : template.queryForPage(searchQuery, Anime.class)) {
System.out.println(anime.getName());
}
} /*outPut:
五等分的花嫁
约会大作战
*/
四、参考资料
尚硅谷.Spring Boot 高级
Spring Boot 与ElasticSearch的更多相关文章
- Spring Boot整合Elasticsearch
Spring Boot整合Elasticsearch Elasticsearch是一个全文搜索引擎,专门用于处理大型数据集.根据描述,自然而然使用它来存储和搜索应用程序日志.与Logstash和K ...
- spring boot 整合 elasticsearch 5.x
spring boot与elasticsearch集成有两种方式.一种是直接使用elasticsearch.一种是使用data中间件. 本文只指针使用maven集成elasticsearch 5.x, ...
- 【spring boot】【elasticsearch】spring boot整合elasticsearch,启动报错Caused by: java.lang.IllegalStateException: availableProcessors is already set to [8], rejecting [8
spring boot整合elasticsearch, 启动报错: Caused by: java.lang.IllegalStateException: availableProcessors ], ...
- Elasticsearch学习(3) spring boot整合Elasticsearch的原生方式
前面我们已经介绍了spring boot整合Elasticsearch的jpa方式,这种方式虽然简便,但是依旧无法解决我们较为复杂的业务,所以原生的实现方式学习能够解决这些问题,而原生的学习方式也是E ...
- spring boot与ElasticSearch的集成
本文主要介绍Spring boot与ElasticSearch的集成,因为Spring boot的教程以及ElasticSearch的学习其他博客可能更优秀,所以建议再看这篇文章前先学习学习一下Spr ...
- Spring Boot 集成 Elasticsearch 实战
最近有读者问我能不能写下如何使用 Spring Boot 开发 Elasticsearch(以下简称 ES) 相关应用,今天就讲解下如何使用 Spring Boot 结合 ES. 可以在 ES 官方文 ...
- Spring Boot 整合 Elasticsearch,实现 function score query 权重分查询
摘要: 原创出处 www.bysocket.com 「泥瓦匠BYSocket 」欢迎转载,保留摘要,谢谢! 『 预见未来最好的方式就是亲手创造未来 – <史蒂夫·乔布斯传> 』 运行环境: ...
- Spring Boot 整合 elasticsearch
一.简介 我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的 首选.他可以快速的存储.搜索和分析海量数据.Spring Boot通过整合Spring Data E ...
- ElasticSearch入门3: Spring Boot集成ElasticSearch
第一步:创建项目elasticsearch 编写pom文件 <?xml version="1.0" encoding="UTF-8"?> <p ...
- Elasticsearch学习(4) spring boot整合Elasticsearch的聚合操作
之前已将spring boot原生方式介绍了,接下将结介绍的是Elasticsearch聚合操作.聚合操作一般来说是解决一下复杂的业务,比如mysql中的求和和分组,由于博主踩的坑比较多,所以博客可能 ...
随机推荐
- 2016/2/21 JavaScript简介
1,javaScript 是什么? 是脚本语言,需要有宿主文件,它的宿主文件是HTML文件.2,它与Java什么关系? 没有什么直接的关系,Java是Sun公司(被Oracle收购了), netspa ...
- 深入理解JMM(Java内存模型) --(四)volatile
volatile的特性 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别.理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这 ...
- 杂项-Java:JeePlus
ylbtech-杂项-Java:JeePlus 一个集成了代码生成器的java快速开发框架 1. 介绍返回顶部 1. 响应式开发 JeePlus采用了目前极为流行的扁平化响应式的设计风格,UI框架使用 ...
- 如何在BCGControlBar工程的工具栏里面新增下拉列表控件
通常情况下,工具栏里面都是一些按钮和图片,很少可以看到下拉列表控件,但是在某些应用场合,也需要用到下拉列表控件.今天在这里就简单讲解下如何在工具栏里添加下拉列表控件. 添加的过程也比较简单,在CM ...
- Ruby检验变量
更新: 2017/06/12 更新: 2017/06/16 补充.class的输出 更新: 2017/06/23 .include?检验数组/哈希表是否包含目标值 更新: 2017/07/02 b ...
- P1266 速度限制(分层图spfa)
P1266 速度限制 题目描述 在这个繁忙的社会中,我们往往不再去选择最短的道路,而是选择最快的路线.开车时每条道路的限速成为最关键的问题.不幸的是,有一些限速的标志丢失了,因此你无法得知应该开多快. ...
- Java实现短信中提取号码
Description 提取一条短信里所有的电话号码,电话号码之间换行打印,短信的内容由用户输入. Input 第一行有个整数n(1≤n≤1000)表示测试用例的个数.其后的每一行中有一条短信,每一条 ...
- 洛谷 P1414 又是毕业季II(未完成)
题目背景 “叮铃铃铃”,随着高考最后一科结考铃声的敲响,三年青春时光顿时凝固于此刻.毕业的欣喜怎敌那离别的不舍,憧憬着未来仍毋忘逝去的歌.1000多个日夜的欢笑和泪水,全凝聚在毕业晚会上,相信,这一定 ...
- 题解报告:poj 1426 Find The Multiple(bfs、dfs)
Description Given a positive integer n, write a program to find out a nonzero multiple m of n whose ...
- RabbitMQ一:消息队列的认识
1异步处理 场景说明:用户注册后,需要发注册邮件和注册短信.传统的做法有两种1.串行的方式:2.并行方式. (1)串行方式:将注册信息写入数据库成功后,发送注册邮件,再发送注册短信.以上三个任务全部完 ...