我们进入elasticsearch的官方文档,可以看到一个与客户端相关的东西
进入,可以看到有很多客户端,我们这里使用Java restful风格的客户端。
发现7.17版本里面只有一个高级客户端
查看一个低级版本,里面还有一个低级版本
可以看到低级客户端里面东西很少
我们这里使用高级客户端,至于新版本里面为什么抛弃低级客户端,不去探究。展开高级客户端,发现里面有个getting start和一大堆API
展开getting start
高级Java REST客户端依赖于以下构件及其传递依赖项:
初始化阶段,我们创建RestHighLevelClient
实例需要一个REST低级客户端构建器;如果是集群,可以构建多个HttpHost
实例,使用完毕,调用close()
关闭,释放资源。
更多的内容,感兴趣可以自行查看。
Spring Data for Elasticsearch is part of the umbrella Spring Data project which aims to provide a familiar and consistent Spring-based programming model for for new datastores while retaining store-specific features and capabilities. The Spring Data Elasticsearch project provides integration with the Elasticsearch search engine.
Spring Data ElasticSearch是Spring对原生JAVA操作Elasticsearch封装之后的产物。它通过对原生API的封装,使得JAVA程序员可以简单的对Elasticsearch进行操作。
我们先创建一个空的maven项目
然后在这个空项目下创建子模块
创建模块时,我们勾选了NoSQL的starter data elasticsearch,自动给我们导入了依赖。
我们进入spring-boot-starter-data-elasticsearch
进入spring-data-elasticsearch
,在这里,我们看到了它导入了elasticsearch的高级客户端依赖,也就是我们前面说的原生依赖
打开 maven导入的依赖目录,可以看到spring-data-elasticsearch 2.7.2
导入的7.17.4
的client
。
注意,我们前面(就是上一篇)使用的elasticsearch
是7.6.2
,这里我们把它改成7.6.2
,保持版本一致。在pom中改成自己想要的版本即可。但修改elasticsearch版本,像我上面2.7.2
的Springboot改不了7.6.2
的elasticsearch。
查看elasticsearch、springboot、Spring data三者版本对应
https://docs.spring.io/spring-data/elasticsearch/docs/current/reference/html/#preface.requirements
修改springboot和elasticsearch的版本。
修改成功。
我们在这里提前导入fastjson,后续需要。
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.1.41version>
dependency>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<groupId>com.goodwingroupId>
<artifactId>es-apiartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>es-apiname>
<description>Demo project for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
<project.build.sourceEncoding>UTF-8project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8project.reporting.outputEncoding>
<spring-boot.version>2.3.7.RELEASEspring-boot.version>
<elasticsearch.version>7.6.2elasticsearch.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.1.41version>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-devtoolsartifactId>
<scope>runtimescope>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-configuration-processorartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<optional>trueoptional>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
<exclusions>
<exclusion>
<groupId>org.junit.vintagegroupId>
<artifactId>junit-vintage-engineartifactId>
exclusion>
exclusions>
dependency>
dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-dependenciesartifactId>
<version>${spring-boot.version}version>
<type>pomtype>
<scope>importscope>
dependency>
dependencies>
dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.pluginsgroupId>
<artifactId>maven-compiler-pluginartifactId>
<version>3.8.1version>
<configuration>
<source>1.8source>
<target>1.8target>
<encoding>UTF-8encoding>
configuration>
plugin>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
<version>2.3.7.RELEASEversion>
<configuration>
<mainClass>com.goodwin.EsApiApplicationmainClass>
configuration>
<executions>
<execution>
<id>repackageid>
<goals>
<goal>repackagegoal>
goals>
execution>
executions>
plugin>
plugins>
build>
project>
RestHighLevelClient
在第一节初始化已经见过,我们后续api
操作中需要用RestHighLevelClient
实例,我们将RestHighLevelClient
注入到Spring容器中
package com.goodwin.config;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* @author goodwin
*/
@Configuration
public class EsConfig {
@Bean
public RestHighLevelClient restHighLevelClient(){
RestHighLevelClient client = new RestHighLevelClient(
RestClient.builder(
new HttpHost("127.0.0.1", 9200, "http")
)
);
return client;
}
}
@Autowired
@Qualifier("restHighLevelClient")
RestHighLevelClient restHighLevelClient;
RestHighLevelClient.indices()
提供一个用于访问Indices
API的IndicesClient
。
public final IndicesClient indices() {
return indicesClient;
}
使用IndicesClient
实例,通过下图API可以看到,可以用于创建、删除索引等。
使用IndicesClient
的create
API创建索引。
public CreateIndexResponse create(CreateIndexRequest createIndexRequest,
RequestOptions options) throws IOException {
return restHighLevelClient.performRequestAndParseEntity(createIndexRequest, IndicesRequestConverters::createIndex, options,
CreateIndexResponse::fromXContent, emptySet());
}
所以创建索引还需要createIndexRequest
和RequestOptions
实例。
在这里创建一个people
索引。
@Test
void testCreateIndex() throws IOException {
CreateIndexRequest indexRequest = new CreateIndexRequest("people");
CreateIndexResponse createIndexResponse =
restHighLevelClient.indices().create(indexRequest, RequestOptions.DEFAULT);
System.out.println(createIndexResponse.isAcknowledged()); // 查看是否创建成功
System.out.println(createIndexResponse); // 查看返回对象
restHighLevelClient.close();
}
输出
true
org.elasticsearch.client.indices.CreateIndexResponse@c4f5c12e
根据创建索引的套路,这里直接给出判断索引是否存在的示例,有兴趣的可以自行阅读源码。
@Test
void testIndexIsExists() throws IOException {
boolean exists = restHighLevelClient.indices().exists(
new GetIndexRequest("people"),
RequestOptions.DEFAULT);
System.out.println(exists);
restHighLevelClient.close();
}
输出
true
@Test
void testSetIndex() throws IOException {
// 创建请求对象
PutMappingRequest request = new PutMappingRequest("people");
request.setTimeout(new TimeValue(20, TimeUnit.SECONDS));
// 设置mapping
String mappingSource = "{ " +
" \"properties\":{ " +
" \"id\":{ " +
" \"type\":\"integer\", " +
" \"store\":true, " +
" \"index\":true" +
" }," +
" \"name\":{ " +
" \"type\":\"text\", " +
" \"store\":true, " +
" \"index\":true," +
" \"analyzer\":\"ik_smart\"" +
" " +
" }," +
" \"description\":{" +
" \"type\":\"text\", " +
" \"store\":true, " +
" \"index\":true," +
" \"analyzer\":\"ik_smart\"" +
" }," +
" \"hobby\":{" +
" \"type\":\"text\", " +
" \"store\":true, " +
" \"index\":true," +
" \"analyzer\":\"ik_smart\"," +
" \"fields\" : {" +
" \"keyword\" : {" +
" \"type\" : \"text\"" +
" }" +
" }" +
" }" +
" }" +
"}";
request.source( mappingSource, XContentType.JSON);
// 发送请求
AcknowledgedResponse response = restHighLevelClient.indices().putMapping(request,RequestOptions.DEFAULT);
// 输出返回结果
System.out.println(response.isAcknowledged());
// 关闭客户端
restHighLevelClient.close();
}
true
@Test
void testDeleteIndex() throws IOException {
AcknowledgedResponse response = restHighLevelClient.indices().delete(
new DeleteIndexRequest("people"),
RequestOptions.DEFAULT);
System.out.println(response.isAcknowledged());
restHighLevelClient.close();
}
输出
true
@Data
@NoArgsConstructor
@AllArgsConstructor
public class People {
private int id;
private String name;
private String description;
private List<String> hobby;
}
下面java
代码相当于
PUT /people/_doc/1
{
"id":1001,
"name":"邓紫棋",
"description":"一位创作型歌手,一个天才美女。",
"hobby":["唱歌","创作"]
}
@Test
void testAddDocument() throws IOException {
// 创建一个People实例
People people = new People(
1001,
"邓紫棋",
"一位创作型歌手,一个天才美女。",
Arrays.asList("唱歌", "创作"));
//实例对应的json字符串
String jsonString = JSON.toJSONString(people);
//创建请求
IndexRequest request = new IndexRequest("people");
//制定规则 PUT /people/_doc/1
//设置id
request.id("1");
//设置超时
request.timeout("1s");
//设置数据源和内容类型
request.source(jsonString,XContentType.JSON);
//客户端发送请求,获取响应的结果
IndexResponse response = restHighLevelClient.index(request, RequestOptions.DEFAULT);
//获取建立索引的状态信息
System.out.println(response.status());
//查看返回内容
System.out.println(response.toString());
restHighLevelClient.close();
}
输出
CREATED
IndexResponse[index=people,type=_doc,id=1,version=1,result=created,seqNo=0,primaryTerm=1,shards={"total":2,"successful":1,"failed":0}]
@Test
void testIsExistDocument() throws IOException {
GetRequest request = new GetRequest("people", "1");
//不获取返回的 _source的上下文了
FetchSourceContext fetchSourceContext = new FetchSourceContext(false);
//允许设置这个请求的FetchSourceContext,控制是否以及如何返回_source。
request.fetchSourceContext(fetchSourceContext);
//显式指定将返回的存储字段。默认情况下,将返回_source字段。
request.storedFields("_none_");
boolean exists = restHighLevelClient.exists(request, RequestOptions.DEFAULT);
System.out.println(exists);
restHighLevelClient.close();
}
true
@Test
void testUpdateDocument() throws IOException {
UpdateRequest updateRequest = new UpdateRequest("people","1");
People people = new People();
people.setId(1002);
updateRequest.doc(JSON.toJSONString(people), XContentType.JSON);
UpdateResponse update = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
System.out.println(update.status());
restHighLevelClient.close();
}
OK
以上相当于,不修改的部分不会丢失。
POST /people/_doc/1/_update
{
"doc":{
"id":1002
}
}
@Test
void testDeleteDocument() throws IOException {
DeleteRequest request = new DeleteRequest("people","1");
request.timeout("1s");
DeleteResponse response = restHighLevelClient.delete(request, RequestOptions.DEFAULT);
System.out.println(response.status());
restHighLevelClient.close();
}
OK
前面文档的新增,使用IndexRequest
实例的source
方法设置数据源和内容类型,查看源码可以发现在IndexRequest
重载了很多source
方法。
但它们最终都走向调用
public IndexRequest source(BytesReference source, XContentType xContentType) {
this.source = Objects.requireNonNull(source);
this.contentType = Objects.requireNonNull(xContentType);
return this;
}
可以看到,如果我们企图多次调用source
方法来批量添加数据,是不可行的,this.source
和this.contentType
只会指向最后一次设置的数据。修改亦是如此。
查看RestHighLevelClient
源码,发现可以使用bulk
API执行批量请求。
上述两个方法都需要我们传入一个BulkRequest
对象,在这个类中,重载了很多add
方法,它们的参数是XXXRequest
,很明显,我们只要批量add
上各种Request
就可以进行批量操作。
下面进行批量添加
@Test
void testBulkAddDocument() throws IOException {
BulkRequest bulkRequest = new BulkRequest("people");
ArrayList<People> list = new ArrayList<>();
list.add(new People(1001,
"邓紫棋",
"一位创作型歌手,一个天才美女。",
Arrays.asList("唱歌", "创作")));
list.add(new People(1002,
"蔡徐坤",
"一个穿背带裤的两年半练习生,是一个人气艺人。",
Arrays.asList("唱","跳","rap","篮球")));
list.add(new People(1003,
"小黑子",
"自诩是ikun,是一类自称是蔡徐坤粉丝的群体,包括俊男美女。",
Arrays.asList("香精煎鱼","香翅捞饭","油饼","荔枝","美食","卤醋鸡脚")));
list.add(new People(1004,
"Gloria",
"邓紫棋在新专辑《启示录》MV中的虚拟人物,同时也是邓紫棋爸爸给她取得英文名字。",
Collections.singletonList("未知")));
for (People people : list) {
bulkRequest.add(
new IndexRequest("people") //加入一个Request
.id(Integer.toString(people.getId())) //没有设置id 会自定生成一个随机id
.source(JSON.toJSONString(people),XContentType.JSON)//设置数据
);
}
BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
System.out.println(bulk.status());
restHighLevelClient.close();
}
OK
SearchSourceBuilder
是一个搜索源构建器,允许轻松构建搜索源,用来设置各种搜索条件。使用SearchRequest
中的source(SearchSourceBuilder sourceBuilder)
设置搜索请求的源。
SearchSourceBuilder
中的一些API
:
SearchSourceBuilder.query(QueryBuilder query)
为此请求设置搜索构建器,QueryBuilder
有非常多的实现类XXXQueryBuilder
。
这里我们能看到很多在上一篇讲过的,例如bool
多条件查询、match
模糊匹配、term/terms
精确查询等有关的构建器。
SearchSourceBuilder.highlighter(HighlightBuilder highlightBuilder)
指定高亮构建器
SearchSourceBuilder.fetchSource(···)
指示响应是否应该包含每个命中的存储的_source
更多方法自行阅读源码。
@Test
void testSearchDocument() throws IOException {
//创建搜索源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//多条件查询构建器
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//创建两个模糊匹配构建器
MatchQueryBuilder matchQueryBuilder1 = new MatchQueryBuilder("name","邓紫棋");
MatchQueryBuilder matchQueryBuilder2 = new MatchQueryBuilder("description","邓紫棋");
//将两个MatchQueryBuilder实例丢到boolQueryBuilder中
boolQueryBuilder.should(matchQueryBuilder1);
boolQueryBuilder.should(matchQueryBuilder2);
//设置搜索源构建器的搜索构建器
searchSourceBuilder.query(boolQueryBuilder);
//设置高亮
HighlightBuilder highlightBuilder = new HighlightBuilder();
//添加字段以高亮显示
highlightBuilder.field("name");
highlightBuilder.field("description");
//设置用于高亮显示的开头和结尾标记。
highlightBuilder.preTags("");
highlightBuilder.postTags("");
//设置搜索源构建器的高亮构建器
searchSourceBuilder.highlighter(highlightBuilder);
//设置超时
searchSourceBuilder.timeout(new TimeValue(2,TimeUnit.SECONDS));
SearchRequest request = new SearchRequest("people");
request.source(searchSourceBuilder);
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//打印执行状态
System.out.println(response.status());
//获得结果
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
System.out.println(hit.getSourceAsMap());
//获取高亮
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//输出高亮
for (Map.Entry<String, HighlightField> stringHighlightFieldEntry : highlightFields.entrySet()) {
System.out.println(stringHighlightFieldEntry);
}
}
restHighLevelClient.close();
}
OK
{name=Gloria, description=邓紫棋在新专辑《启示录》MV中的虚拟人物,同时也是邓紫棋爸爸给她取得英文名字。, id=1004, hobby=[未知]}
description=[description], fragments[[<em>邓紫棋</em>在新专辑《启示录》MV中的虚拟人物,同时也是<em>邓紫棋</em>爸爸给她取得英文名字。]]
{name=邓紫棋, description=一位创作型歌手,一个天才美女。, id=1001, hobby=[唱歌, 创作]}
name=[name], fragments[[<em>邓紫棋</em>]]
以上相当于在kibana执行以下语句
GET /people/_search
{
"query": {
"bool": {
"should": [
{
"match": {
"name": "邓紫棋"
}
},
{
"match": {
"description": "邓紫棋"
}
}
]
}
},
"highlight": {
"pre_tags": "",
"post_tags": "",
"fields": {
"description": {},
"name":{}
}
}
}
更多复杂搜索请各位自行探究,(¦3[▓▓] 晚安。