热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringBoot:整合Elasticsearch

1,基本概念1.1,下载安装Elasticsearch与Solr类似,同样是一个基于Lucene的开源的分布式搜索引擎。当年由于Luce

1,基本概念


1.1,下载安装


Elasticsearch与Solr类似,同样是一个基于Lucene的开源的分布式搜索引擎。当年由于Lucene的Java API比较难用,于是Shay Banon就开发 出一个叫作Compass的框架来对Lucene进行封装。Compass框架用起来十分方便,后来发现在2009年之后,Compass项目就不更新了。因为Shay Banon用Elasticsearch取代了Compass。由于Compass只是一个Java框架,所以必须掌握Java编程才能使用Compass;而Elasticsearch则是一个独立应用,它提供了RESTful的操作接口,因此不管用什么编程语言, 即使不会编程,也可使用Elasticsearch,只要会用Postman或curl发送请求即可。

(1)登录官网(地址)下载Elasticsearch,版本对照关系(地址)

Elasticsearch启动报错:
warning: ignoring JAVA_HOME=C:\Program Files\Java\jdk1.8.0_191; using bundled JDK。原因是JDK和Elasticsearch版本不对应。warning: usage of JAVA_HOME is deprecated, use ES_JAVA_HOME。原因是 elasticsearch 7系列版本以上都是自带的jdk,可以在es的bin目录下找到elasticsearch-env.bat这个文件,配置es的jdk。官方推荐使用es自带的jdk。

(2)下载后得到Elasticsearch

➢ bin:该目录下包含Elasticsearch的各种工具命令。
➢ config:该目录下包含Elasticsearch的各种配置文件,尤其是elasticsearch.yml和jvm.options两个配置文件很重要,其中elasticsearch.yml用于配置Elasticsearch,jvm.options用于配置JVM的堆内存,垃圾回收机制等选项。
➢ jdk:该目录下包含一份最新的JDK。
➢ lib:该目录下保存Elasticsearch的核心JAR包及依赖的第三方JAR包。
➢ logs:日志目录。
➢ plugins:Elasticsearch的插件目录。

(3)配置环境变量

JAVA_HOME:E:\Java\jdk-1.11
PATH:E:\Elasticsearch

(4)修改elasticsearch.yml文件

cluster.name: my-application #配置集群名
node.name: node-1 #配置节点名
#network.host: 192.168.0.1 #配置Elasticsearch绑定的IP地址
#http.port: 9200 #监听端口

Elasticsearch的集群配置非常简单,在同一个局域网内的多个节点(多个Elasticsearch服务器)上只要指定了相同的 cluster.name,它们都会自动加入同一个集群。因此,一个节点只要设置了cluster.name就能加入集群,成为集群的一部分。

(5)启动Elasticsearch

访问(http://localhost:9200)发送GET请求,输出name,cluster_name就是前面配置的节点名和集群名,也可以看到Elasticsearch的版本信息。则表明Elasticsearch启动成功。


1.2,Elasticsearch安全配置 


如果想就这样使用Elasticsearch,当然也是可以的,但很明显安全性不够,下面为Elasticsearch启用SSL支持,以及配置用户名,密码。

(1)修改config目录下的elasticsearch.yml文件,添加内容。

# ---------------------------------- Security ----------------------------------
xpack.security.enabled: true

(2)启动Elasticsearch,打开新CMD然后设置密码。

Elasticsearch内置了用于不同目的几个用户,故此处要依次为每个用户设置密码,每个密码都要设置两次。

  • elastic:超级用户。
  • kibana:Kibana通过该用户连接Elasticsearch。
  • logstash_system:Logstash将监控信息存储到Elasticsearch中时使用该用户。
  • beats_system:Beats在Elasticsearch中存储监视信息时使用该用户。
  • apm_system:APM服务器在Elasticsearch中存储监视信息时使用该用户。
  • remote_monitoring_user:Metricbeat用户在Elasticsearch中收集和存储监视信息时使用该用户。


1.3,Index操作


【Elastisearch、RDBMS、Solr基本对应关系】

RDBMSElasticsearchSolr
TableIndexCore(Collection)
row(行)Document(文档)Document(文档)
column(列)Field(字段)Field(字段)


(1)添加Index

curl -k -u elastic:123456 -X PUT http://localhost:9200/test

PS:不允许使用大写

(2)查看Index

curl -k -u elastic:123456 http://localhost:9200/_cat/indices

结果以点 (.) 开头的Index是Elasticsearch内置的Index。

(3)删除Index

curl -k -u elastic:123456 -X DELETE http://localhost:9200/test

(4)替换默认分词器:Elasticsearch 内置了一些分词器,但这些分词器对中文支持并不 好,这里可选择使用 IK 分词器来处理中文分词(地址),注意版本对应关系。

elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.16.3/elasticsearch-analysis-ik-7.16.3.zip

安装完成后,IK分词器会被自动安装到Elasticsearch的plugins目录下,还会在config目录下创建一个analysis-ik子目录,用于保存IK分词器的配置文件。为Elasticsearch安装任何插件后都需要重启Elasticsearch服务器来加载IK分词器。然后在当前目录下(命令行提示符“>”前的路径)下创建一个配置文件:

{"settings": {"analysis": {"analyzer": {"default": {"tokenizer": "ik_max_word"}}}}
}

上面文件指定为Index设置默认的中文分词器:ik_max_word,该分词器由IK分词器提供,它还提供了一个名为“ik_smart”的中文分词器。

curl -k -u elastic:123456 -X PUT http://localhost:9200/test -d @test.json -H "Content-Type:application/json"-H:设置Content-Type请求头的值为“application/json”;
-d:用于读取配置文件的内容作为请求数据。

  • 在命令行所在的当前路径下定义一个test.json文件,该JSON文件指定使用ik_max_word分词器,text属性指定要测试分 词的文本内容。

{"analyzer": "ik_max_word","text": "Elasticsearch与Solr类似,同样是一个基于Lucene的开源的分布式搜索引擎。"
}

  • 运行如下命令

curl -k -u elastic:123456 -X POST http://localhost:9200/test/_analyze?pretty=true -d @test.json -H "Content-Type:application/json"{"tokens" : [{"token" : "elasticsearch","start_offset" : 0,"end_offset" : 13,"type" : "ENGLISH","position" : 0},{"token" : "与","start_offset" : 13,"end_offset" : 14,"type" : "CN_CHAR","position" : 1},......]
}

每个词都被称作一个token,每个token都对应如下属性:

  • start_offset:起始位置。
  • end_offset:结束位置。
  • type:类型。
  • position:词的位置。


1.4,文档操作


(1)添加文档:在命令行所在的当前路径下定义一个book.json文件,内容如下:

{"name": "呐喊","description": "生动地塑造了狂人、孔乙己、阿Q等一批不朽的艺术形象,深刻反映了19世纪末到20世纪20年代间中国社会生活的现状,有力揭露和鞭挞了封建旧恶势力,表达了作者渴望变革,为时代呐喊,希望唤醒国民的思想。","price": 35
}

curl -k -u elastic:123456 -X POST http://localhost:9200/test/book/1 -d @book.json -H "Content-Type:application/json"
{"_index":"test","_type":"book","_id":"1","_version":1,"result":"created","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":0,"_primary_term":1}book:就是type
1:就是被添加文档的ID,这个ID其实是字符串,因此也可指定为“abc”

(2)查看Index下所有文档:命令中的pretty=true是一个很常见的参数,用于让Elasticsearch生成格式良好的响应。从该命令可以看出,查看Index下的所有文档,只要在该Index后添加“_search”即可。

curl -k -u elastic:123456 http://localhost:9200/test/_search?pretty=true
{"took" : 717,"timed_out" : false,"_shards" : {"total" : 1,"successful" : 1,"skipped" : 0,"failed" : 0},"hits" : {"total" : {"value" : 1,"relation" : "eq"},"max_score" : 1.0,"hits" : [{"_index" : "test","_type" : "book","_id" : "1","_score" : 1.0,"_source" : {"name" : "呐喊","description" : "生动地塑造了狂人、孔乙己、阿Q等一批不朽的艺术形象,深刻反映了19世纪末到20世纪20年代间中国社会生活的现状,有力揭露和鞭挞了封建旧恶势力,表达了作者渴望变革,为时代呐喊,希望唤醒国民的思想。","price" : 35}}]}
}

(3)查看Index下指定ID的文档:

curl -k -u elastic:123456 http://localhost:9200/test/book/1?pretty=true
{"_index" : "test","_type" : "book","_id" : "1","_version" : 1,"_seq_no" : 0,"_primary_term" : 1,"found" : true,"_source" : {"name" : "呐喊","description" : "生动地塑造了狂人、孔乙己、阿Q等一批不朽的艺术形象,深刻反映了19世纪末到20世纪20年代间中国社会生活的现状,有力揭露和鞭挞了封建旧恶势力,表达了作者渴望变革,为时代呐喊,希望唤醒国民的思想。","price" : 35}
}

(4)删除指定ID的文档:

curl -k -u elastic:123456 -X DELETE http://localhost:9200/test/book/1
{"_index":"test","_type":"book","_id":"1","_version":2,"result":"deleted","_shards":{"total":2,"successful":1,"failed":0},"_seq_no":1,"_primary_term":1}

(5)全文检索:执行全文检索同样是向 Index 后加“_search”的 URL 地址发送请求,只不过需要添加 JSON格式的请求数据而已。

{"query": {"match": {"description": "孔乙己"}}
}

Elasticsearch自己的查询语法(地址),它要求查询参数满足JSON格式,其中query属性的值才是实际的查询参数:

  • match 表明使用普通关键词查询
  • regexp 表示正则表达式查询
  • fuzzy表示模糊查询
  • prefix 表示前缀查询
  • wildcard表示通配符查询
  • range表示范围查询
  • query_string定义查询字符串

curl -k -u elastic:123456 http://localhost:9200/test/_search?pretty=true -d @search.json -H "Content-Type:application/json"

(6)根据查询删除:如果要根据查询条件来删除文档,只要向Index后加“_delete_by_que ry”的URL地址发送POST请求即可。

curl -k -u elastic:123456 -X POST http://localhost:9200/test/_delete_by_query?pretty=true -d @search.json -H "Content-Type:application/json"


2,RESTful客户


2.1,使用RESTful客户端操作Elasticsearch


如果打算使用 Elasticsearch 自带的 RestClient 来操作 Elasticsearch,甚至不需要添加 spring-boot-starter-data-elasticsearch依赖,则直接使用最基本的spring-boot-starter依赖和Elasticsearch提供的RestClient依赖即可。

Elasticsearch官方提供的RestClient分为两种:

  • 高级RestClient(推荐):开发者面向Index,文档等高层次的API编程,因此更加简单,方便。通常建议以高级 RestClient 为主,只有当高级 RestClient 实在搞不定时,才考虑使用低级RestClient。
  • 低级RestClient:开发者直接面向底层 RESTful 接口编程,发送最原始的请求参数,Elasticsearch 服务器也返回最原始的响应,这种方式需要开发者自行处理请求,响应的序列化和反序列化,相当麻烦,但灵活性最好。

Spring Boot只要检测到类路径下有 elasticsearch-rest-high-level-client 依赖(无须使用Spring Data Elasticsearch),Spring Boot 就会在容器中创建一个自动配置的 RestHighLevelClient,它就是Elasticsearch的高级RestClient。如果想使用低级RestClient,只要调用它的 getLowLevelClient() 方法即可返回 ResLowLevelClient,它就是Elasticsearch的低级RestClient。

如果需要对 RestClient 进行定制,则可在容器中部署一个或多个 RestClientBuilderCustomizerBean,该 Bean 的 customize() 方法即可对 RestClientBuilder、HttpAsyncClientBuilder、RequestConfig.Builder进行定制, 这些定制最终将作用于Elasticsearch的RestClient。


当容器中有了自动配置的RestHighLevelClient之后,容器可通过依赖注入将它注入其他任何组件(主要是DAO组件),接下来该组件可通过它的如下方法来操作Elasticsearch索引库:

  • count(CountRequest countRequest,RequestOptions options): 查询符合条件的文档数量。
  • countAsync(CountRequest countRequest,RequestOptions option s,ActionListener listener):以异步方式查询符合条件的文档数量,其中 listener 参数负责处理异步查询的结果。
  • delete(DeleteRequest deleteRequest,RequestOptions options): 根据ID删除文档。
  • deleteAsync(DeleteRequest deleteRequest,RequestOptions option s,ActionListener listener):以异步方式根据ID删除文档,其中listener参数负责处理异步删除的结果。
  • deleteByQuery(DeleteByQueryRequest deleteByQueryRequest,RequestOptions options):删除符合查询条件的文档。
  • deleteByQueryAsync(DeleteByQueryRequest deleteByQueryRequest,RequestOptions options,ActionListener listener):以异步方式删除符合查询条件的文档,其中listener参数负责处理异步删除的结果。
  • exists(GetRequest getRequest,RequestOptions options):判断指定ID对应的文档是否存在。
  • existsAsync(GetRequest getRequest,RequestOptions options,ActionListener listener):以异步方式判断指定ID对应的文档是否存在。
  • get(GetRequest getRequest,RequestOptions options):根据ID 获取文档。
  • getAsync(GetRequest getRequest,RequestOptions options,ActionListener listener):以异步方式根据ID获取文档。
  • index(IndexRequest indexRequest,RequestOptions options):创建索引或文档。
  • indexAsync(IndexRequest indexRequest,RequestOptions options,ActionListener listener):以异步方式创建索引或文档。
  • mget(MultiGetRequest multiGetRequest,RequestOptions option s):根据多个ID获取多个文档。
  • mgetAsync(MultiGetRequest multiGetRequest,RequestOptions options,ActionListener listener):以异步方式根据多个ID获取多个文档。
  • msearch(MultiSearchRequest multiSearchRequest,RequestOptions options):根据多个查询条件返回文档。
  • msearchAsync(MultiSearchRequest multiSearchRequest,RequestOptions options,ActionListener listener):以异步方式根据多个查询条件返回文档。
  • search(SearchRequest searchRequest,RequestOptions option s):查询文档。
  • searchAsync(SearchRequest searchRequest,RequestOptions options,ActionListener listener):以异步方式查询文档。
  • update(UpdateRequest updateRequest , RequestOptions options):根据ID更新文档。
  • updateAsync(UpdateRequest updateRequest,RequestOptions options,ActionListener listener):以异步方式根据ID更新文档。
  • updateByQuery(UpdateByQueryRequest updateByQueryRequest, RequestOptions options):更新符合条件的所有文档。
  • updateByQueryAsync(UpdateByQueryRequest updateByQueryRequest,RequestOptions options,ActionListener listener):以异步方式更新符合条件的所有文档。

此外,它还提供了大量 xxx() 方法来返回对应的 XxxClient,如 asyncSearch() 方法返回AsyncSearchClient,cluster() 方法返回 ClusterClient,eql() 方法返回 EqlClient,indices()方法返回IndicesClient……这些XxxClient又提供了大量的方法来执行相应的操作。


(1)添加 elasticsearch-rest-high-level-client 依赖

org.elasticsearch.clientelasticsearch-rest-high-level-client

(2)修改 application.properties

# 指定Elasticsearch服务器的地址
spring.elasticsearch.rest.uris=http://127.0.0.1:9200
spring.elasticsearch.rest.read-timeout=10s
# 配置用户名和密码
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=123456

(3)Controller

package com.example.springboot.Controller;import org.elasticsearch.Assertions;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import java.io.IOException;@RestController
public class HelloController {@Autowiredprivate RestHighLevelClient restHighClient;@RequestMapping("CreateIndex")public boolean testCreateIndex() throws IOException {// 定义创建Index的设置,和前面test.json文件的内容相同// 设置该Index的默认分词器是ik_max_wordvar json = "{\n" +" \"settings\": {\n" +" \"analysis\": {\n" +" \"analyzer\": {\n" +" \"default\": {\"tokenizer\": \"ik_max_word\"}\n" +" }\n" +" }\n" +" }\n" +"}\n";var indexRequest = new CreateIndexRequest("books").source(json, XContentType.JSON);AcknowledgedResponse resp = restHighClient.indices().create(indexRequest, RequestOptions.DEFAULT);return resp.isAcknowledged();}@RequestMapping("DeleteIndex")public boolean testDeleteIndex(String index) throws IOException {var indexRequest = new DeleteIndexRequest(index);AcknowledgedResponse resp = restHighClient.indices().delete(indexRequest, RequestOptions.DEFAULT);return resp.isAcknowledged();}@RequestMapping("SaveDocument")public void testSaveDocument(String index, Integer id, String name, String description, Double price) throws IOException {IndexRequest request = new IndexRequest(index).id(id + "").source("name", name, "description", description, "price", price);IndexResponse resp = restHighClient.index(request, RequestOptions.DEFAULT);System.out.println(resp);}@RequestMapping("GetDocument")public void testGetDocument(String index, Integer id) throws IOException {var request = new GetRequest(index).id(id + "");GetResponse resp = restHighClient.get(request, RequestOptions.DEFAULT);System.out.println(resp.getSource());}@RequestMapping("Search")public void testSearch(String index, String field, String term) throws IOException {var builder = new SearchSourceBuilder();if (term != null && term.contains("*")) {builder.query(QueryBuilders.wildcardQuery(field, term));} else {builder.query(QueryBuilders.matchQuery(field, term));}var request = new SearchRequest(index).source(builder);SearchResponse resp = restHighClient.search(request, RequestOptions.DEFAULT);SearchHits hits = resp.getHits();hits.forEach(System.out::println);}@RequestMapping("DeleteDocument")public void testDeleteDocument(String index, Integer id) throws IOException {var request = new DeleteRequest(index).id(id + "");DeleteResponse resp = restHighClient.delete(request, RequestOptions.DEFAULT);System.out.println(resp.status());}
}

testSearch()方法测试全文检索功能,该方法对传入的 term 关键 词进行判断,如果该关键词包含星号( * ),就使用通配符查询(Wildc ard Query),否则就使用普通查询。


2.2,使用反应式RESTful客户端操作Elasticsearch


由于Elasticsearch官方并未提供反应式的RestClient,因此Spring Data Elasticsearch额外补充了一个 ReactiveElasticsearchClient,用于提供反应式 API 支持。ReactiveElasticsearchClient 相当于RestHighLevelClient的反应式版本,因此它们二者的功能基本相似。只不过在调用ReactiveElasticsearchClient的方法时无须传入RequestOptions参数,且其方法的返回值都是Flux或Mono (反应式API),因此下面程序使用了blockOptional(),toIterable() 来保证反应式API能执行完成。

ReactiveElasticsearchClient 是基于 WebFlux 的 WebClient 的,因此如果要使用反应式的RestClient,还需要添加Spring WebFlux依赖。

org.springframework.bootspring-boot-starter-data-elasticsearch

org.springframework.bootspring-boot-starter-webflux

# 指定Elasticsearch服务器的地址
spring.data.elasticsearch.client.reactive.endpoints=127.0.0.1:9200
spring.data.elasticsearch.client.reactive.use-ssl=false
spring.elasticsearch.rest.read-timeout=10s
# 配置用户名和密码
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=123456

当容器中有了自动配置的ReactiveElasticsearchClient之后,接下来即可将它依赖注入其他任何组件。

import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.delete.*;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.*;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.admin.indices.create.CreateIndexRequest;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.get.GetResult;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.client.reactive.ReactiveElasticsearchClient;
import org.springframework.web.bind.annotation.*;
import reactor.core.publisher.*;import java.io.IOException;
import java.util.function.Consumer;@RestController
public class HelloController {@Autowiredprivate ReactiveElasticsearchClient reactiveClient;@RequestMapping("CreateIndex")public boolean testCreateIndex() throws IOException {// 定义创建Index的设置,和前面test.json文件的内容相同// 设置该Index的默认分词器是ik_max_wordvar json = "{\n" +" \"settings\": {\n" +" \"analysis\": {\n" +" \"analyzer\": {\n" +" \"default\": {\"tokenizer\": \"ik_max_word\"}\n" +" }\n" +" }\n" +" }\n" +"}\n";CreateIndexRequest indexRequest = new CreateIndexRequest("books").source(json, XContentType.JSON);Mono resp = reactiveClient.indices().createIndex(indexRequest);return resp.blockOptional().isPresent();}@RequestMapping("DeleteIndex")public boolean testDeleteIndex(String index) throws IOException {var indexRequest = new DeleteIndexRequest(index);Mono resp = reactiveClient.indices().deleteIndex(indexRequest);return resp.blockOptional().isPresent();}@RequestMapping("SaveDocument")public void testSaveDocument(String index, Integer id, String name, String description, Double price) throws IOException {IndexRequest request = new IndexRequest(index).id(id + "").source("name", name, "description", description, "price", price);Mono resp = reactiveClient.index(request);resp.blockOptional().ifPresent(System.out::println);}@RequestMapping("GetDocument")public void testGetDocument(String index, Integer id) throws IOException {var request = new GetRequest(index).id(id + "");Mono resp = reactiveClient.get(request);resp.blockOptional().ifPresent(e -> System.out.println(e.getSource()));}@RequestMapping("Search")public void testSearch(String index, String field, String term) throws IOException {var builder = new SearchSourceBuilder();if (term != null && term.contains("*")) {builder.query(QueryBuilders.wildcardQuery(field, term));} else {builder.query(QueryBuilders.matchQuery(field, term));}var request = new SearchRequest(index).source(builder);Flux resp = reactiveClient.search(request);resp.toIterable().forEach(System.out::println);}@RequestMapping("DeleteDocument")public void testDeleteDocument(String index, Integer id) throws IOException {var request = new DeleteRequest(index).id(id + "");Mono resp = reactiveClient.delete(request);resp.blockOptional().ifPresent(e -> System.out.println(e.status()));}
}


3,SpringBoot 整合 Elasticsearch


3.1,Spring Data Elasticsearch


  • 如果Spring Boot在类加载路径下检测到Spring Data Elasticsearch,Spring Boot就会在容器中自动配置一个ElasticsearchRestTemplate(注意不是ElasticsearchTemplate,ElasticsearchTemplate已经过时了)。ElasticsearchRestTemplate底层依赖于容器中自动配置的RestHighLevelClient
  • 如果Spring Boot在类加载路径下同时检测到Spring Data Elasticsearch和Spring WebFlux,Spring Boot就会在容器中自动配置一个ReactiveElasticsearchTemplateReactiveElasticsearchTemplate底层依赖于容器中自动配置的 ReactiveElasticsearchClient。正如 ReactiveElasticsearchClient RestHighLevelClient的反应式版本,ReactiveElasticsearchTemplate则是ElasticsearchRestTemplate的反应式版本。

与RestHighLevelClient、ReactiveElasticsearchClient 相比,ElasticsearchRestTemplate、ReactiveElasticsearchTemplate 能以更加面向对象的方法来操作 Elasticsearch 索引库,这些Xxx Template的方法操作的是实体对象,而Spring Data Elasticsearch会自动将面向实体对象的操作转化为对索引库的操作。


3.2,使用 Elasticsearch 的 Repository


由于Spring Data是高层次的抽象,而Spring Data Elasticsearch只是属于底层的具体实现,因此Spring Data Elasticsearch也提供了与前面Spring Data完全一致的操作。

Spring Data Elasticsearch大致包括如下几方面功能:

  • DAO接口只需继承CrudRepository或ReactiveCrudRepository,Spring Data Elasticsearch能为DAO组件提供实现类。
  • Spring Data Elasticsearch支持方法名关键字查询,只不过Elasticsearch查询都是全文检索查询。
  • Spring Data Elasticsearch同样支持DAO组件添加自定义的查询方法—通过添加额外的接口,并为额外的接口提供实现类,Spring Data Elasticsearch就能将该实现类中的方法“移植”到DAO组件中。

Spring Data Elasticsearch的 Repository 操作的数据类同样使用@Document和@Field注解修饰,其中@Document修饰的实体类被映射到文档, 使用该注解时可指定如下两个常用属性。

  • indexName:指定该实体类被映射到哪个Index。
  • createIndex:指定是否根据实体类创建Index。

@Field修饰的属性则被映射到索引文档的Field,使用该注解时可指定如下常用属性。

  • name:指定该属性被映射到索引文档的哪个Field,如果不指定该属性,则默认基于同名映射。
  • analyzer:指定该Field所使用的分词器。
  • searchAnalyzer:指定对该Field执行搜索时所使用的分词器。


Maven

org.springframework.bootspring-boot-starterorg.springframework.bootspring-boot-starter-data-elasticsearchorg.springframework.bootspring-boot-starter-web

application.properties

# 指定Elasticsearch服务器的地址
spring.elasticsearch.rest.uris=https://127.0.0.1:9200
spring.elasticsearch.rest.read-timeout=10s
# 配置用户名和密码
spring.elasticsearch.rest.username=elastic
spring.elasticsearch.rest.password=123456

Pojo:该 Book 类使用了@Document(index Name="springboot",createIndex=true) 修饰,这说明该类被映射到名为 “books” 的Index,Spring Data Elasticsearch可根据该实体类自动创建 Index—如果该Index不存在的话。

package com.example.springboot.Pojo;import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;// createIndex指定自动创建索引
@Document(indexName = "books", createIndex = true)
public class Book {@Idprivate Integer id;@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String name;@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String description;@Field(analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private Double price;//构造器、setter、getter
}

Dao

package com.example.springboot.Dao;import com.example.springboot.Pojo.Book;
import org.springframework.data.elasticsearch.annotations.Query;
import org.springframework.data.repository.CrudRepository;
import java.util.List;public interface BookDao extends CrudRepository, BookCustomDao {// 方法名关键字查询List findByName(String name);List findByIdIn(List list);List findByPriceBetween(double start, double end);List findByDescriptionMatches(String descPattern);// 使用@Query定义查询语句@Query("{ \"match\": { \"?0\": \"?1\" } } ")
// @Query("{ \"query_string\": { \"query\": \"?0:?1\" } } ")List findByQuery1(String field, String term);
}

package com.example.springboot.Dao;import com.example.springboot.Pojo.Book;
import java.util.List;public interface BookCustomDao {List customQuery1(String name, String description);
}

package com.example.springboot.Dao;import com.example.springboot.Pojo.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.data.elasticsearch.core.SearchHits;
import org.springframework.data.elasticsearch.core.query.Criteria;
import org.springframework.data.elasticsearch.core.query.CriteriaQuery;
import org.springframework.data.elasticsearch.core.query.Query;
import java.util.ArrayList;
import java.util.List;public class BookCustomDaoImpl implements BookCustomDao {&#64;Autowiredprivate ElasticsearchRestTemplate restTemplate;&#64;Overridepublic List customQuery1(String name, String description) {// 以面向对象的方式定义查询语句Criteria criteria &#61; new Criteria("name").is(name).and("description").is(description);// 创建CriteriaQueryQuery query &#61; new CriteriaQuery(criteria);SearchHits hits &#61; restTemplate.search(query, Book.class);List books &#61; new ArrayList<>();hits.forEach(hit -> books.add(hit.getContent()));return books;}
}

Controller

package com.example.springboot.Controller;
import com.example.springboot.Dao.BookDao;
import com.example.springboot.Pojo.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;&#64;RestController
public class HelloController {&#64;Autowiredprivate BookDao bookDao;&#64;RequestMapping("/Save")public void testSave(Integer id, String name, String description, Double price) {var book &#61; new Book(id, name, description, price);bookDao.save(book);}&#64;RequestMapping("/Delete")public void testDelete() {// 删除id为3的Book对象bookDao.deleteById(3);}&#64;RequestMapping("/FindByName")public void testFindByName(String name) {bookDao.findByName(name).forEach(System.out::println);}&#64;RequestMapping("/FindByIdIn")public void testFindByIdIn(Integer id1, Integer id2) {bookDao.findByIdIn(List.of(id1, id2)).forEach(System.out::println);}&#64;RequestMapping("/FindByPriceBetween")public void testFindByPriceBetween(double start, double end) {bookDao.findByPriceBetween(start, end).forEach(System.out::println);}&#64;RequestMapping("/FindByDescriptionMatches")public void testFindByDescriptionMatches(String descPattern) {bookDao.findByDescriptionMatches(descPattern).forEach(System.out::println);}&#64;RequestMapping("/FindByQuery1")public void testFindByQuery1(String field, String term) {bookDao.findByQuery1(field, term).forEach(System.out::println);}&#64;RequestMapping("/CustomQuery1")public void testCustomQuery1(String name, String description) {bookDao.customQuery1(name, description).forEach(System.out::println);}
}


推荐阅读
  • ElasticSerach初探第一篇认识ES+环境搭建+简单MySQL数据同步+SpringBoot整合ES
    一、认识ElasticSearch是一个基于Lucene的开源搜索引擎,通过简单的RESTfulAPI来隐藏Lucene的复杂性。全文搜索,分析系统&# ... [详细]
  • 软件测试行业深度解析:迈向高薪的必经之路
    本文深入探讨了软件测试行业的发展现状及未来趋势,旨在帮助有志于在该领域取得高薪的技术人员明确职业方向和发展路径。 ... [详细]
  • 问题描述现在,不管开发一个多大的系统(至少我现在的部门是这样的),都会带一个日志功能;在实际开发过程中 ... [详细]
  • 深入探讨:Actor模型如何解决并发与分布式计算难题
    在现代软件开发中,高并发和分布式系统的设计面临着诸多挑战。本文基于Akka最新文档,详细探讨了Actor模型如何有效地解决这些挑战,并提供了对并发和分布式计算的新视角。 ... [详细]
  • 本文详细介绍了 Java 网站开发的相关资源和步骤,包括常用网站、开发环境和框架选择。 ... [详细]
  • 本文详细介绍了Java代码分层的基本概念和常见分层模式,特别是MVC模式。同时探讨了不同项目需求下的分层策略,帮助读者更好地理解和应用Java分层思想。 ... [详细]
  • 开发笔记:使用JavaScript解决网页图片拉伸问题
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了使用JavaScript解决网页图片拉伸问题相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文是对《敏捷软件开发:原则、模式与实践》一书的深度解析,书中不仅探讨了敏捷方法的核心理念及其应用,还详细介绍了面向对象设计的原则、设计模式的应用技巧及UML的有效使用。 ... [详细]
  • Docker安全策略与管理
    本文探讨了Docker的安全挑战、核心安全特性及其管理策略,旨在帮助读者深入理解Docker安全机制,并提供实用的安全管理建议。 ... [详细]
  • 2023年,Android开发前景如何?25岁还能转行吗?
    近期,关于Android开发行业的讨论在多个平台上热度不减,许多人担忧其未来发展。本文将探讨当前Android开发市场的现状、薪资水平及职业选择建议。 ... [详细]
  • 本文探讨了如何通过Service Locator模式来简化和优化在B/S架构中的服务命名访问,特别是对于需要频繁访问的服务,如JNDI和XMLNS。该模式通过缓存机制减少了重复查找的成本,并提供了对多种服务的统一访问接口。 ... [详细]
  • 对象存储与块存储、文件存储等对比
    看到一篇文档,讲对象存储,好奇,搜索文章,摘抄,学习记录!背景:传统存储在面对海量非结构化数据时,在存储、分享与容灾上面临很大的挑战,主要表现在以下几个方面:传统存储并非为非结 ... [详细]
  • Java EE 平台集成了多种服务、API 和协议,旨在支持基于 Web 的多层应用程序开发。本文将详细介绍 Java EE 中的 13 种关键技术规范,帮助开发者更好地理解和应用这些技术。 ... [详细]
  • 数字经济浪潮下企业人才需求变化,优质IT培训机构助力技能提升
    随着云计算、大数据、人工智能、区块链和5G等技术的迅猛发展,数字经济已成为推动经济增长的重要动力。据信通院数据,2020年中国数字经济占GDP比重达38.6%,整体规模突破39.2万亿元。本文探讨了企业在数字化转型中对技术人才的需求变化,并介绍了优质IT培训机构如何助力人才培养。 ... [详细]
  • 2020年9月15日,Oracle正式发布了最新的JDK 15版本。本次更新带来了许多新特性,包括隐藏类、EdDSA签名算法、模式匹配、记录类、封闭类和文本块等。 ... [详细]
author-avatar
手机用户2502921663
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有