一、Elasticsearch概念
•以 员工文档 的形式存储为例:一个文档代表一个员工数据。存储数据到 ElasticSearch 的行为叫做 索引 ,但在索引一个文档之前,需要确定将文档存储在哪里。
•一个 ElasticSearch 集群可以 包含多个 索引 ,相应的每个索引可以包含多个 类型 。 这些不同的类型存储着多个 文档 ,每个文档又有 多个 属性 。
•类似关系:
–索引-数据库
–类型-表
–文档-表中的记录
–属性-列
更多详细内容见官方文档
二、Elasticsearch入门
2.1、安装Elasticsearch
1、在虚拟机中通过Docker拉取elasticsearch镜像
docker pull elasticsearch
2、运行镜像并使其运行成容器(9200是暴露给web端的,9300是在分布式系统中各个节点交互暴露的接口)
docker run -e ES_JAVA_OPTS="-Xms256m -Xmx256m" -d -p 9200:9200 -p 9300:9300 --name my_es 5acf0e8da90b
3、拷贝配置文件到宿主机(必须保证容器中的ES是启动状态)
从这步开始一定要跟着做,不然后面使用SpringData的接口操作时会报错。详细见ARTS第三周
docker cp my_es:/usr/share/elasticsearch/config/elasticsearch.yml: /usr/share/elasticsearch.yml
4、停止 和 删除原来创建的容器
docker stop elasticsearch
docker rm my_es
5、重新执行创建容器命令(重点:挂载文件)
docker run -di --name=my_es -p 9200:9200 -p 9300:9300 -v /usr/share/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml elasticsearch
6、 修改 /usr/share/elasticsearch.yml 将 transport.host: 0.0.0.0 前的 # 去掉后保存文件退出。 其作用是允许任何ip地址访问 elasticsearch 开发测试阶段可以这么做,生产环境下指定具体的IP
7、重启后发现重启启动失败了(纯宿主机问题),这与我们刚才修改的配置有关,因为elasticsearch在启动的时候会进行一些检查,比如最多打开的文件的个数以及虚拟内存区域数量等等
8、系统调优
(1)修改 /etc/security/limits.conf 追加内容
* soft nofile 65536
* hard nofile 65536
说明:nofile是单个进程允许打开的最大文件个数 soft nofile 是软限制 hard nofile是硬限制 (2)修改 /etc/sysctl.conf 追加内容
vm.max_map_count=655360
说明:限制一个进程可以拥有的VMA(虚拟内存区域)的数量
9、重启虚拟机
2.2、基本操作
1、存储文档
对于雇员目录,我们将做如下操作:
- 每个雇员存储一个文档,包含该雇员的所有信息。
- 每个文档都将是
employee
类型 。 - 该类型位于 索引
megacorp
内。 - 该索引保存在我们的 Elasticsearch 集群中。
实践中这非常简单(尽管看起来有很多步骤),我们可以通过一条命令完成所有这些动作:
PUT /megacorp/employee/1
{"first_name" : "John","last_name" : "Smith","age" : 25,"about" : "I love to go rock climbing","interests": [ "sports", "music" ]
}
注意,路径 /megacorp/employee/1
包含了三部分的信息:
megacorp
索引名称employee
类型名称1
特定雇员的ID
请求体 —— JSON 文档 —— 包含了这位员工的所有详细信息,他的名字叫 John Smith ,今年 25 岁,喜欢攀岩。
很简单!无需进行执行管理任务,如创建一个索引或指定每个属性的数据类型之类的,可以直接只索引一个文档。Elasticsearch 默认地完成其他一切,因此所有必需的管理任务都在后台使用默认设置完成。
进行下一步前,让我们增加更多的员工信息到目录中:
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" ]
}
可以使用Postman软件模拟PUT请求
比如存储员工id为1的文档,下面会响应成功后的信息(上面的其他操作同样)
2、检索文档
目前我们已经在 Elasticsearch 中存储了一些数据, 接下来就能专注于实现应用的业务需求了。第一个需求是可以检索到单个雇员的数据。
这在 Elasticsearch 中很简单。简单地执行 一个 HTTP GET
请求并指定文档的地址——索引库、类型和ID。 使用这三个信息可以返回原始的 JSON 文档:
GET /megacorp/employee/1
返回结果包含了文档的一些元数据,以及 _source
属性,内容是 John Smith 雇员的原始 JSON 文档:
{"_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" ]}
}
将 HTTP 命令由 PUT
改为 GET
可以用来检索文档,同样的,可以使用 DELETE
命令来删除文档,以及使用 HEAD
指令来检查文档是否存在。如果想更新已存在的文档,只需再次 PUT
。 3、轻量搜索
一个 GET
是相当简单的,可以直接得到指定的文档。 现在尝试点儿稍微高级的功能,比如一个简单的搜索!
第一个尝试的几乎是最简单的搜索了。我们使用下列请求来搜索所有雇员:
GET /megacorp/employee/_search
可以看到,我们仍然使用索引库 megacorp
以及类型 employee
,但与指定一个文档 ID 不同,这次使用 _search
。返回结果包括了所有三个文档,放在数组 hits
中。一个搜索默认返回十条结果。
{"took": 6,"timed_out": false,"_shards": { ... },"hits": {"total": 3,"max_score": 1,"hits": [{"_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" ]}},{"_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": "2","_score": 1,"_source": {"first_name": "Jane","last_name": "Smith","age": 32,"about": "I like to collect rock albums","interests": [ "music" ]}}]}
}
注意:返回结果不仅告知匹配了哪些文档,还包含了整个文档本身:显示搜索结果给最终用户所需的全部信息。
接下来,尝试下搜索姓氏为 Smith
的雇员。为此,我们将使用一个 高亮 搜索,很容易通过命令行完成。这个方法一般涉及到一个 查询字符串 (query-string) 搜索,因为我们通过一个URL参数来传递查询信息给搜索接口:
GET /megacorp/employee/_search?q=last_name:Smith
我们仍然在请求路径中使用 _search
端点,并将查询本身赋值给参数 q=
。返回结果给出了所有的 Smith:
{..."hits": {"total": 2,"max_score": 0.30685282,"hits": [{..."_source": {"first_name": "John","last_name": "Smith","age": 25,"about": "I love to go rock climbing","interests": [ "sports", "music" ]}},{..."_source": {"first_name": "Jane","last_name": "Smith","age": 32,"about": "I like to collect rock albums","interests": [ "music" ]}}]}
}
4、 使用查询表达式搜索
Query-string 搜索通过命令非常方便地进行临时性的即席搜索 ,但它有自身的局限性(参见 href="https://www.elastic.co/guide/cn/elasticsearch/guide/current/search-lite.html">轻量 搜索)。Elasticsearch 提供一个丰富灵活的查询语言叫做 查询表达式 , 它支持构建更加复杂和健壮的查询。
领域特定语言 (DSL), 指定了使用一个 JSON 请求。我们可以像这样重写之前的查询所有 Smith 的搜索 :
PUT /megacorp/employee/_search
{"query" : {"match" : {"last_name" : "Smith"}}
}
返回结果与之前的查询一样,但还是可以看到有一些变化。其中之一是,不再使用 query-string 参数,而是一个请求体替代。这个请求使用 JSON 构造,并使用了一个 match
查询(属于查询类型之一,后续将会了解)。
5、更复杂的搜索
现在尝试下更复杂的搜索。 同样搜索姓氏为 Smith 的雇员,但这次我们只需要年龄大于 30 的。查询需要稍作调整,使用过滤器 filter ,它支持高效地执行一个结构化查询。
PUT /megacorp/employee/_search
{"query" : {"bool": {"must": {"match" : {"last_name" : "smith" }},"filter": {"range" : {"age" : { "gt" : 30 } }}}}
}
目前无需太多担心语法问题,后续会更详细地介绍。只需明确我们添加了一个 过滤器 用于执行一个范围查询,并复用之前的 match
查询。现在结果只返回了一个雇员,叫 Jane Smith,32 岁。
{..."hits": {"total": 1,"max_score": 0.30685282,"hits": [{..."_source": {"first_name": "Jane","last_name": "Smith","age": 32,"about": "I like to collect rock albums","interests": [ "music" ]}}]}
}
更多详细内容见官方文档
三、SpringBoot整合Elasticsearch
3.1、简介
SpringBoot默认支持两种技术来和ES交互
1、Jest(默认不生效)需要导入Jest工具包(具体文档可去Github搜索)
2、SpringBoot提供的API:继承SpringData的接口和ElasticsearchTempldate组件
需要指定cluster-name和cluster-nodes,这种方法可能会报超时错误,就是ES版本不合适
如果版本不适配:
1)、升级Springboot版本
2)、安装对应版本ElasticSearch
spring data elasticsearch(spring-data-elasticsearch包版本)elasticsearch(ES版本)3.2.x6.5.03.1.x6.2.23.0.x5.5.02.1.x2.4.02.0.x2.2.01.3.x1.5.2
3.2、JestClient操作ES
maven依赖
<!-- 引入操作ElasticSearch的API --><dependency><groupId>io.searchbox</groupId><artifactId>jest</artifactId><version>5.3.3</version></dependency>
- Article.java
@Data
public class Article {@JestIdprivate Integer id;private String author;private String title;private String content;}
- application.properties
spring.elasticsearch.jest.uris=http://192.168.25.157:9200
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot11ElasticsearchApplicationTests {@Autowiredprivate JestClient jestClient;@Testpublic void testIndex () throws IOException {Article article = new Article();article.setId(1);article.setAuthor("Yang");article.setTitle("好消息");article.setContent("Hello World");Index index = new Index.Builder(article).index("ahead").type("article").build();jestClient.execute(index);}@Testpublic void testSearch() throws IOException {String json = "{n" +" "query" : {n" +" "match" : {n" +" "content" : "hello"n" +" }n" +" }n" +"}";Search search = new Search.Builder(json).addIndex("ahead").addType("article").build();SearchResult result = jestClient.execute(search);//转换为对象Article article = result.getSourceAsObject(Article.class, true);//获取响应的所有信息JsonObject jsonObject = result.getJsonObject();System.out.println(article);System.out.println(jsonObject);}}
- 先执行存储文档方法没问题,然后再执行检索方法,结果正确
更多详细信息参考官方文档
3.3、SpringData的接口操作ES
- maven依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>
- Book.java
@Document(indexName = "ahead", type = "book")
@Data
public class Book { private Integer id; private String name; private String author;
}
- BookRepository.java
public interface BookRepository extends ElasticsearchRepository<Book, Integer> {/*** 按照SpringData所定义的规范来创建方法底层就会帮我们实现* 某个方法对应什么操作具体可参考官方文档* 通过书名模糊查询* @param name* @return*/List<Book> findByNameLike(String name);
}
- application.properties
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.cluster-nodes=192.168.25.157:9300
cluster-name可通过访问ip:9200
获取
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot11ElasticsearchApplicationTests {@Autowiredprivate BookRepository bookRepository;@Testpublic void testRepositoryIndex() {Book book = new Book();book.setId(1);book.setName("西游记");book.setAuthor("吴承恩");bookRepository.index(book);}@Testpublic void testRepositorySearch() {List<Book> book = bookRepository.findByNameLike("游");System.out.println(book);}}
- 先执行存储方法没问题,然后执行检索方法,结果正确。
3.4、ElasticsearchTemplate操作ES
用的还是上面的环境
- 测试类
@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot11ElasticsearchApplicationTests {@Autowiredprivate ElasticsearchTemplate elasticsearchTemplate;@Testpublic void testElasticsearchTemplateIndex() {Book book = new Book();book.setId(2);book.setName("三国演义");book.setAuthor("罗贯中");IndexQuery indexQuery = new IndexQueryBuilder().withObject(book).build();elasticsearchTemplate.index(indexQuery);}@Testpublic void testElasticsearchTemplateSearch() {SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(new MatchQueryBuilder("name", "国")).build();List<Book> books = elasticsearchTemplate.queryForList(searchQuery,Book.class);System.out.println(books);}
}
- 先执行存储文档方法成功后,再执行检索方法结果正确。
更多详细信息参考官方文档