热门标签 | 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);}
}


推荐阅读
  • 开发笔记:spring boot项目打成war包部署到服务器的步骤与注意事项
    本文介绍了将spring boot项目打成war包并部署到服务器的步骤与注意事项。通过本文的学习,读者可以了解到如何将spring boot项目打包成war包,并成功地部署到服务器上。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • 使用Spring AOP实现切面编程的步骤和注意事项
    本文介绍了使用Spring AOP实现切面编程的步骤和注意事项。首先解释了@EnableAspectJAutoProxy、@Aspect、@Pointcut等注解的作用,并介绍了实现AOP功能的方法。然后详细介绍了创建切面、编写测试代码的过程,并展示了测试结果。接着讲解了关于环绕通知的使用方法,并修改了FirstTangent类以添加环绕通知方法。最后介绍了利用AOP拦截注解的方法,只需修改全局切入点即可实现。使用Spring AOP进行切面编程可以方便地实现对代码的增强和拦截。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • GetWindowLong函数
    今天在看一个代码里头写了GetWindowLong(hwnd,0),我当时就有点费解,靠,上网搜索函数原型说明,死活找不到第 ... [详细]
  • Win10下游戏不能全屏的解决方法及兼容游戏列表
    本文介绍了Win10下游戏不能全屏的解决方法,包括修改注册表默认值和查看兼容游戏列表。同时提供了部分已经支持Win10的热门游戏列表,帮助玩家解决游戏不能全屏的问题。 ... [详细]
  • JVM 学习总结(三)——对象存活判定算法的两种实现
    本文介绍了垃圾收集器在回收堆内存前确定对象存活的两种算法:引用计数算法和可达性分析算法。引用计数算法通过计数器判定对象是否存活,虽然简单高效,但无法解决循环引用的问题;可达性分析算法通过判断对象是否可达来确定存活对象,是主流的Java虚拟机内存管理算法。 ... [详细]
  • 本文介绍了在使用Python中的aiohttp模块模拟服务器时出现的连接失败问题,并提供了相应的解决方法。文章中详细说明了出错的代码以及相关的软件版本和环境信息,同时也提到了相关的警告信息和函数的替代方案。通过阅读本文,读者可以了解到如何解决Python连接服务器失败的问题,并对aiohttp模块有更深入的了解。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • 深入理解Kafka服务端请求队列中请求的处理
    本文深入分析了Kafka服务端请求队列中请求的处理过程,详细介绍了请求的封装和放入请求队列的过程,以及处理请求的线程池的创建和容量设置。通过场景分析、图示说明和源码分析,帮助读者更好地理解Kafka服务端的工作原理。 ... [详细]
  • 本文介绍了如何清除Eclipse中SVN用户的设置。首先需要查看使用的SVN接口,然后根据接口类型找到相应的目录并删除相关文件。最后使用SVN更新或提交来应用更改。 ... [详细]
  • Imtryingtofigureoutawaytogeneratetorrentfilesfromabucket,usingtheAWSSDKforGo.我正 ... [详细]
  • 本文介绍了在CentOS上安装Python2.7.2的详细步骤,包括下载、解压、编译和安装等操作。同时提供了一些注意事项,以及测试安装是否成功的方法。 ... [详细]
  • 纠正网上的错误:自定义一个类叫java.lang.System/String的方法
    本文纠正了网上关于自定义一个类叫java.lang.System/String的错误答案,并详细解释了为什么这种方法是错误的。作者指出,虽然双亲委托机制确实可以阻止自定义的System类被加载,但通过自定义一个特殊的类加载器,可以绕过双亲委托机制,达到自定义System类的目的。作者呼吁读者对网上的内容持怀疑态度,并带着问题来阅读文章。 ... [详细]
  • Servlet多用户登录时HttpSession会话信息覆盖问题的解决方案
    本文讨论了在Servlet多用户登录时可能出现的HttpSession会话信息覆盖问题,并提供了解决方案。通过分析JSESSIONID的作用机制和编码方式,我们可以得出每个HttpSession对象都是通过客户端发送的唯一JSESSIONID来识别的,因此无需担心会话信息被覆盖的问题。需要注意的是,本文讨论的是多个客户端级别上的多用户登录,而非同一个浏览器级别上的多用户登录。 ... [详细]
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社区 版权所有