文章目录
- 十四、检索
- 1.概论
- 2.搭建环境(docker)
- 3.概念
- 4.向ES端口发送json数据
- 案例
- (1)存储雇员数据
- (2)检索雇员数据
- (3)轻量搜索
- 5.整合ElasticSearch测试
- (1)搭建环境
- (2)ES交互
- <1>.Jest(默认不生效)
- <2>.SpringData ElasticSearch
- 编写一个ElasticSearchRepository
十四、检索
1.概论
我们的应用经常需要添加检索功能,开源的 ElasticSearch 是目前全文搜索引擎的首选。他可以快速的存储、搜索和分析海量数据。Spring Boot通过整合Spring Data ElasticSearch为我们提供了非常便捷的检索功能支持;
Elasticsearch是一个分布式搜索服务,提供Restful API,底层基于Lucene,采用多shard(分片)的方式保证数据安全,并且提供自动resharding的功能,github等大型的站点也是采用了ElasticSearch作为其搜索服务,
Elasticsearch 是 面向文档 的,意味着它存储整个对象或 文档_。Elasticsearch 不仅存储文档,而且 _索引 每个文档的内容使之可以被检索。在 Elasticsearch 中,你 对文档进行索引、检索、排序和过滤–而不是对行列数据。这是一种完全不同的思考数据的方式,也是 Elasticsearch 能支持复杂全文检索的原因。
2.搭建环境(docker)
[root@localhost ~]# docker search elasticsearch
INDEX NAME DESCRIPTION STARS OFFICIAL AUTOMATED
docker.io docker.io/elasticsearch Elasticsearch is a powerful open source se... 3531 [OK]
docker.io docker.io/nshou/elasticsearch-kibana Elasticsearch-6.5.4 Kibana-6.5.4 96 [OK]
docker.io docker.io/itzg/elasticsearch Provides an easily configurable Elasticsea... 67 [OK]
docker.io docker.io/elastichq/elasticsearch-hq Official Docker image for ElasticHQ: Elast... 26 [OK]
docker.io docker.io/kubernetes/fluentd-elasticsearch An image that ingests Docker container log... 25
docker.io docker.io/lmenezes/elasticsearch-kopf elasticsearch kopf 17 [OK]
docker.io docker.io/bitnami/elasticsearch Bitnami Docker Image for Elasticsearch 16 [OK]
docker.io docker.io/taskrabbit/elasticsearch-dump Import and export tools for elasticsearch 15 [OK]
docker.io docker.io/elastic/elasticsearch The Elasticsearch Docker image maintained ... 14
docker.io docker.io/esystemstech/elasticsearch Debian based Elasticsearch packing for Lif... 14
docker.io docker.io/monsantoco/elasticsearch ElasticSearch Docker image 11 [OK]
docker.io docker.io/mesoscloud/elasticsearch [UNMAINTAINED] Elasticsearch 9 [OK]
docker.io docker.io/blacktop/elasticsearch Alpine Linux based Elasticsearch Docker Image 8 [OK]
docker.io docker.io/justwatch/elasticsearch_exporter Elasticsearch stats exporter for Prometheus 8
docker.io docker.io/centerforopenscience/elasticsearch Elasticsearch 4 [OK]
docker.io docker.io/barchart/elasticsearch-aws Elasticsearch AWS node 3
docker.io docker.io/forkdelta/fluentd-elasticsearch fluent/fluentd with fluent-plugin-elastics... 1 [OK]
docker.io docker.io/jetstack/elasticsearch-pet An elasticsearch image for kubernetes PetSets 1 [OK]
docker.io docker.io/phenompeople/elasticsearch Elasticsearch is a powerful open source se... 1 [OK]
docker.io docker.io/18fgsa/elasticsearch Built from https://github.com/docker-libra... 0
docker.io docker.io/axway/elasticsearch-docker-beat "Beat" extension to read logs of container... 0 [OK]
docker.io docker.io/driveclutch/infra-elasticsearch-aws Elasticsearch Docker for use in AWS 0 [OK]
docker.io docker.io/igneoussystems/base-elasticsearch A base image containing 3p tools for elast... 0
docker.io docker.io/igneoussystems/base-elasticsearch-5 Base elasticsearch 5.1.1 container 0
docker.io docker.io/wreulicke/elasticsearch elasticsearch 0 [OK]
[root@localhost ~]# docker pull registry.docker-cn.com/library/elasticsearch
Using default tag: latest
Trying to pull repository registry.docker-cn.com/library/elasticsearch ...
latest: Pulling from registry.docker-cn.com/library/elasticsearch
05d1a5232b46: Pull complete
5cee356eda6b: Pull complete
89d3385f0fd3: Pull complete
65dd87f6620b: Pull complete
78a183a01190: Pull complete
1a4499c85f97: Pull complete
2c9d39b4bfc1: Pull complete
1b1cec2222c9: Pull complete
59ff4ce9df68: Pull complete
1976bc3ee432: Pull complete
5af49e8af381: Pull complete
42c8b75ff7af: Pull complete
7e6902915254: Pull complete
99853874fa54: Pull complete
596fbad6fcff: Pull complete
Digest: sha256:a8081d995ef3443dc6d077093172a5931e02cdb8ffddbf05c67e01d348a9770e
Status: Downloaded newer image for registry.docker-cn.com/library/elasticsearch:latest
[root@localhost ~]# docker images
REPOSITORY TAG IMAGE ID CREATED SIZE
docker.io/rabbitmq 3-management 20c898989d5c 37 hours ago 213 MB
docker.io/tomcat latest 02eaee4dc65c 3 days ago 463 MB
docker.io/redis latest a55fbf438dfd 4 days ago 95 MB
docker.io/mysql latest 7bb2586065cd 4 days ago 477 MB
registry.docker-cn.com/library/elasticsearch latest 5acf0e8da90b 6 months ago 486 MB
[root@localhost ~]# docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name ES01 5acf0e8da90b
c2a8a49519b42a3035bab87d6107464aa5e83e4ae9fea06eed0545f7b7504a55
[root@localhost ~]# docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c2a8a49519b4 5acf0e8da90b "/docker-entrypoin..." 4 seconds ago Up 3 seconds 0.0.0.0:9200->9200/tcp, 0.0.0.0:9300->9300/tcp ES01
6d58aafede2d 20c898989d5c "docker-entrypoint..." 22 hours ago Up 9 hours 4369/tcp, 5671/tcp, 0.0.0.0:5672->5672/tcp, 15671/tcp, 25672/tcp, 0.0.0.0:15672->15672/tcp myrabbitmq
在这里提一句,因为ES默认启动会占用2G的运行内存,所以需要手动去修改最小占用内存
然后我们来到ES对应的Web见面,来检验安装
{
"name" : "h6idySx",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "MCg7KwdRSAmAzVSbuBseAQ",
"version" : {
"number" : "5.6.12",
"build_hash" : "cfe3d9f",
"build_date" : "2018-09-10T20:12:43.732Z",
"build_snapshot" : false,
"lucene_version" : "6.6.1"
},
"tagline" : "You Know, for Search"
}
3.概念
以 员工文档 的形式存储为例:一个文档代表一个员工数据。存储数据到 ElasticSearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 ElasticSearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
类似关系:
索引-数据库
类型-表
文档-表中的记录
属性-列
4.向ES端口发送json数据
为了更加了解ES对于面向文档的存储特性和搜索功能,所以我们借助postman来完成json的数据注入和查询,因为json的特点就是跨平台和轻量级。
首先我们连接到我们的9200端口。
案例
发送成功。然后进行一个官网文档上的小案例:https://www.elastic.co/guide/cn/elasticsearch/guide/current/_finding_your_feet.html
我们受雇于 Megacorp 公司,作为 HR 部门新的 “热爱无人机” (“We love our drones!”)激励项目的一部分,我们的任务是为此创建一个雇员目录。该目录应当能培养雇员认同感及支持实时、高效、动态协作,因此有一些业务需求:
- 支持包含多值标签、数值、以及全文本的数据
- 检索任一雇员的完整信息
- 允许结构化搜索,比如查询 30 岁以上的员工
- 允许简单的全文搜索以及较复杂的短语搜索
- 支持在匹配文档内容中高亮显示搜索片段
- 支持基于数据创建和管理分析仪表盘
(1)存储雇员数据
这将会以 雇员文档 的形式存储:一个文档代表一个雇员。存储数据到 Elasticsearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
一个 Elasticsearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
思路:
- 每个雇员索引一个文档,包含该雇员的所有信息。
- 每个文档都将是 employee 类型 。
- 该类型位于 索引 megacorp 内。
- 该索引保存在我们的 Elasticsearch 集群中。
注意,路径 /megacorp/employee/1 包含了三部分的信息:
megacorp
索引名称
employee
类型名称
1
特定雇员的ID
加入更多的雇员信息,然后再进行检索操作
(2)检索雇员数据
目前我们已经在 Elasticsearch 中存储了一些数据, 接下来就能专注于实现应用的业务需求了。第一个需求是可以检索到单个雇员的数据。
这在 Elasticsearch 中很简单。简单地执行 一个 HTTP GET 请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
将 HTTP 命令由 PUT 改为 GET 可以用来检索文档,同样的,可以使用 DELETE 命令来删除文档,以及使用 HEAD 指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
- 没删之前先HEAD一下,发现status是200
- 发送delete请求的时候,也是200,说明删除成功
- 删除之后再次查看Status变成了404
(3)轻量搜索
一个 GET 是相当简单的,可以直接得到指定的文档。 现在尝试点儿稍微高级的功能,比如一个简单的搜索!
第一个尝试的几乎是最简单的搜索了。我们使用下列请求来搜索所有雇员:
我们像这样输入
http://192.168.243.30:9200/megacorp/employee/_search
选择GET请求,然后点击Send
然后就会相应出所有雇员的信息了
{
"took": 58,
"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
的雇员。为此,我们将使用一个 高亮 搜索,很容易通过命令行完成。这个方法一般涉及到一个 查询字符串 (query-string) 搜索,因为我们通过一个URL参数来传递查询信息给搜索接口:
我们仍然在请求路径中使用 _search 端点,并将查询本身赋值给参数 q= 。返回结果给出了所有的 Smith:
后面等等的细节操作可以去跟随官网的教程去学习
5.整合ElasticSearch测试
(1)搭建环境
配置我们ES的端口号在properties文件中
spring.elasticsearch.jest.uris=http://192.168.243.30:9200
(2)ES交互
SpringBoot默认支持两种技术来和ES交互 :
<1>.Jest(默认不生效)
需要导入Jest的工具包(io.searchbox.client.JestClient)
所以我们来导入Jest相关的maven配置程序,然后将SpringData的maven打上注释
<dependency>
<groupId>io.searchbox</groupId>
<artifactId>jest</artifactId>
<version>5.3.4</version>
</dependency>
由于我们的ES是5.6.12版本,所以Jest的版本也应该是5.X
然后测试程序,为了让测试类方便运行,所以我们先为ES索引一个文档,方便进行操作。
package springboot.elastic.bean;
import io.searchbox.annotations.JestId;
public class Article {
@JestId
private Integer id;
private String author;
private String title;
private String content;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
}
然后首先为我们的ES文档创建一个索引
@Autowired
JestClient jestClient;
@Test
public void contextLoads() {
//1.给ES索引一个文档
Article article = new Article();
article.setAuthor("zz");
article.setId(1);
article.setContent("zz is sb");
article.setTitle("a z");
// 构建一个索引功能
Index build = new Index.Builder(article).index("zzsb").type("news").build();
// save ES.build project.指定索引.指定类型
try {
jestClient.execute(build);
} catch (IOException e) {
e.printStackTrace();
}
}
运行程序,来到对应端口
数据操作成功。
然后使用Jest来进行一下搜索数据的操作
@Test
public void search(){
//查询表达式
String json = "{\n" +
"\t\"query\" : {\n" +
"\t\t\"match\" :{\n" +
"\t\t\t\"content\" : \"hello\"\n" +
"\t\t}\n" +
"\t}\n" +
"}";
// 构建搜索
Search build = new Search.Builder(json).addIndex("zzsb").addType("news").build();
// 执行
try {
SearchResult execute = jestClient.execute(build);
System.out.println(execute.getJsonString());
} catch (IOException e) {
e.printStackTrace();
}
}
然后进行测试
<2>.SpringData ElasticSearch
首先把原来的SpringData的注释消除,然后在配置类中进行配置
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.243.30:9300
然后运行,将会出现版本不适配的问题,说连接拒绝。
解决方法有两个,第一个就是提升SpringBoot的版本,第二就是降低ES的版本。
这里选择第二个,
由于我的ES是2.4.6,所以来到docker重新下载2.4.6版本的ES,操作步骤就省略了。
然后Properties的端口号也改成9301,重新运行,发现成功。
1)Client节点信息clusterNodes;clusterName
2)Elasticsearchtemplate 操作es
3)编写一个ElasticSearchRepository的子接口来操作es
这里将会说明SpringData ES的两种用法
编写一个ElasticSearchRepository
首先创建一个这样的接口:
public interface BookRepository extends ElasticsearchRepository {
}
我们发现他内部会需要两个泛型,都是和我们操作的数据源相关的,所以我们先来创建我们的数据源。
package springboot.elastic.bean;
import org.springframework.data.elasticsearch.annotations.Document;
@Document(indexName = "zzsb",type = "book")
public class Book {
private Integer id;
private String BookName;
private String author;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getBookName() {
return BookName;
}
public void setBookName(String bookName) {
BookName = bookName;
}
public String getAuthor() {
return author;
}
public void setAuthor(String author) {
this.author = author;
}
@Override
public String toString() {
return "Book{" +
"id=" + id +
", BookName='" + BookName + '\'' +
", author='" + author + '\'' +
'}';
}
}
在这里呢,我们的Book是被定义为文档的,所以要加上document的注解,然后声明类型和索引
然后就可以返回我们的接口类,重新进行编写
public interface BookRepository extends ElasticsearchRepository<Book,Integer> {
}
Book是我们要操作的主键,而Integer是主键类型。
来到我们的测试类,进行测试
@Autowired
BookRepository bookRepository;
@Test
public void test(){
Book book = new Book();
bookRepository.index(book);
}
然后运行程序,来到我们定义的9201端口
运行成功
{"took":4,"timed_out":false,"_shards":{"total":5,"successful":5,"failed":0},"hits":{"total":1,"max_score":1.0,"hits":[{"_index":"zzsb","_type":"book","_id":"null","_score":1.0,"_source":{"id":null,"author":null,"bookName":null}}]}}
现在我们已经能成功连接到平台了,下面要做的就是填充数据了。
public void test(){
Book book = new Book();
book.setId(1);
book.setBookName("zzBook");
book.setAuthor("zz");
bookRepository.index(book);
}
重新运行
{"_index":"zzsb","_type":"book","_id":"1","found":false}
运行成功