【1】Spring Data Elasticsearch
官網地址:https://spring.io/projects/spring-data-elasticsearch#learn,最新穩定版本爲3.2.3。
開發文檔地址:https://docs.spring.io/spring-data/elasticsearch/docs/3.2.3.RELEASE/reference/html/#reference
版本標識說明
GA:General Availability,正式發佈的版本,官方推薦使用此版本。在國外都是用GA來說明release版本的。
PRE: 預覽版,內部測試版. 主要是給開發人員和測試人員測試和找BUG用的,不建議使用;
SNAPSHOT: 快照版,可以穩定使用,且仍在繼續改進版本。
Spring Data Elasticsearch 開發文檔地址:點擊查看
版本新特性
Spring Data Elasticsearch 3.2引入了一下新特性:
- Elasticsearch集羣支持基本身份驗證和SSL傳輸。
- 升級至Elasticsearch 6.8.1。
- Reactive programming support with Reactive Elasticsearch Operations and Reactive Elasticsearch Repositories.
- ElasticsearchEntityMapper作爲Jackson對象映射器的替代品。
@Field
中的字段名自定義。- 支持按查詢刪除。
Reactive programming support with Reactive Elasticsearch Operations and Reactive Elasticsearch Repositories.這句話這樣翻譯:響應式的Elasticsearch Operations和響應式的Elasticsearch Repositories一起提供了響應式編程支持。
版本對應說明
Spring Data Release Train | Spring Data Elasticsearch | Elasticsearch | Spring Boot |
---|---|---|---|
Moore | 3.2.x | 6.8.4 | 2.2.x |
Lovelace | 3.1.x | 6.2.2 | 2.1.x |
Kay | 3.0.x | 5.5.0 | 2.0.x |
Ingalls | 2.1.x | 2.4.0 | 1.5.x |
【2】Elasticsearch Clients
Spring data Elasticsearch 可以使用Elasticsearch 客戶端連接到單一節點或者集羣進行操作。
① Transport Client
在前面Java原生操作Elasticsearch我們就使用的是這個客戶端,實話說,確實很難用。
static class Config {
//首先得獲取客戶端
@Bean
Client client() {
Settings settings = Settings.builder()
.put("cluster.name", "elasticsearch")
.build();
TransportClient client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1")
, 9300));
return client;
}
}
// ...具體使用諸如下面代碼
IndexRequest request = new IndexRequest("spring-data", "elasticsearch", randomID())
.source(someObject)
.setRefreshPolicy(IMMEDIATE);
IndexResponse response = client.index(request);
② High Level REST Client(高級REST客戶端)
High Level REST Client現在是Elasticsearch默認客戶端,它替換掉了Transport Client。異步調用在客戶端管理的線程池上操作,並要求在請求完成時通知回調。
引入pom:
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-elasticsearch</artifactId>
<version>3.2.3.RELEASE</version>
</dependency>
配置類:
@Configuration
public class ElasticsearchConfig {
@Bean
RestHighLevelClient client() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("192.168.18.128:9200")
// .connectedTo("192.168.18.128:9300", "localhost:9301")
.build();
RestHighLevelClient restHighLevelClient = RestClients.create(clientConfiguration).rest();
System.out.println(restHighLevelClient);
return restHighLevelClient;
}
}
測試代碼:
@Autowired
RestHighLevelClient highLevelClient;
@Test
public void test() throws IOException {
GetRequest getRequest=new GetRequest("my-blog","_all","1");
RequestOptions option=RequestOptions.DEFAULT;
GetResponse getResponse = highLevelClient.get(getRequest, option);
System.out.println(getResponse.getIndex());
System.out.println(getResponse.toString());
}
控制檯打印:
{
"_index": "my-blog",
"_type": "article",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"id": "1",
"title": "基於Lucene的搜索服務器",
"content": "它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口"
}
}
③ Reactive Client
ReactiveElasticsearchClient 是基於WebClient的非官方驅動程序,使用Elasticsearch 核心項目提供的request/response對象。調用直接在反應堆棧上操作,而不是將異步(線程池綁定)響應包裝爲反應類型。
示例代碼如下:
static class Config {
@Bean
ReactiveElasticsearchClient client() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291")
.build();
return ReactiveRestClients.create(clientConfiguration);
}
}
// ...
Mono<IndexResponse> response = client.index(request ->
request.index("spring-data")
.type("elasticsearch")
.id(randomID())
.source(singletonMap("feature", "reactive-client"))
.setRefreshPolicy(IMMEDIATE);
);
④ Client Configuration
上面三個客戶端對象都是通過ClientConfiguration對象創建出來的,客戶端的行爲可以通過該對象改變,也可以選擇行的進行其他配置,如ssl。
示例代碼:
// optional if Basic Auhtentication is needed
HttpHeaders defaultHeaders = new HttpHeaders();
defaultHeaders.setBasicAuth(USER_NAME, USER_PASS);
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200", "localhost:9291")
.withConnectTimeout(Duration.ofSeconds(5))
.withSocketTimeout(Duration.ofSeconds(3))
.useSsl()
.withDefaultHeaders(defaultHeaders)
.withBasicAuth(username, password)
. // ... other options
.build();
【3】Elasticsearch Object Mapping
Spring Data Elasticsearch允許在通過EntityMapper接口抽象的兩個映射實現之間進行選擇:
-
Jackson Object Mapping
-
Meta Model Object Mapping
① Jackson Object Mapping
基於Jackson2的方法(默認情況下使用)使用帶有spring數據特定模塊的自定義ObjectMapper實例。實際映射的擴展需要通過Jackson註釋(比如@JsonInclude)進行定製。
示例代碼:
@Configuration
public class Config extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
}
}
AbstractElasticsearchConfiguration已經通過ElasticsearchConfigurationSupport定義了一個基於Jackson2的entityMapper。注意這時這些註解不能使用:CustomConversions, @ReadingConverter & @WritingConverter
,並且@Field(name="custom-name")
也不行。
② Meta Model Object Mapping
基於元模型的方法使用域類型信息來讀/寫Elasticsearch。這允許註冊特定域類型映射的轉換器實例。
示例代碼;
@Configuration
public class Config extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest()
}
@Bean
@Override
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
elasticsearchMappingContext(), new DefaultConversionService()
);
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
}
說明:
- 覆蓋掉ElasticsearchConfigurationSupport 默認配置的EntityMapper 並作爲bean對外暴露;
- Use the provided SimpleElasticsearchMappingContext to avoid inconsistencies and provide a GenericConversionService for Converter registration.
- 如果需要,可以設置CustomConversions
③ 對象映射註解
ElasticsearchEntityMapper可以使用元數據來驅動對象到文檔的映射。對象上面可以使用註解進行標識。
-
@Id
: 目標對象主鍵,字段級別註解。 -
@Document
: 類級別註解,標明是個文檔對象,有如下重要屬性:-
indexName: 實體中的索引名字
-
type: the mapping type.如果未設置,則使用類的小寫簡單名稱。
-
shards: 索引分片數量
-
replicas: 索引的副本數
-
refreshIntervall: 索引的刷新間隔。用於創建索引。默認值爲“1s”。
-
indexStoreType:索引存儲類型。用於創建索引。默認值爲“fs”。
-
createIndex: 配置是否在存儲庫引導時創建索引。默認值爲true。
-
versionType: 版本管理的配置,默認值爲 EXTERNAL.
-
-
@Transient
: 默認所有字段都映射到document, 添加該註解的字段可以例外。 -
@PersistenceConstructor
: 從數據庫中實例化對象時,標記要使用的給定構造函數(甚至是受包保護的構造函數)。構造函數參數按名稱映射到檢索到的文檔中的鍵值。 -
@Field
: 應用於字段級別並定義字段的屬性,大多數屬性映射到相應的Elasticsearch Mapping定義:-
name: field映射到Elasticsearch document中的名稱,默認爲字段名。
-
type: 字段類型,可以是如下之一: Text, Integer, Long, Date, Float, Double, Boolean, Object, Auto, Nested, Ip, Attachment, Keyword.
-
format
andpattern
custom definitions for the Date type. -
store: 標記原始字段值是否應存儲在Elasticsearch中,默認值爲false。
-
analyzer, searchAnalyzer, normalizer用於指定自定義分析器和規範化器。
-
copy_to: 拷貝到多個文檔字段的目標字段
-
-
@GeoPoint
:將字段標記爲地理位置數據類型。如果字段是GeoPoint類的實例,則可以忽略。
上述映射元數據註解定義在一個單獨的spring-data-commons 項目中,是技術無關的。
④ 映射規則
映射規則主要有四方面:
-
Type Hints:按照類型映射;
-
Geospatial Types:點和地質點等地理空間類型轉換爲緯度/經緯度對。
-
Collections:對於集合內的值,當涉及類型提示和自定義轉換時,將應用與聚合根相同的映射規則。
-
Maps:同Collections,但是鍵是需要是字符串
-
Custom Conversions
可以使用ElasticsearchCustomConversions 註冊自定義的映射規則,示例代碼如下:
@Configuration
public class Config extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
return RestClients.create(ClientConfiguration.create("localhost:9200")).rest();
}
@Bean
@Override
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(
elasticsearchMappingContext(), new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
@Bean
@Override
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
return new ElasticsearchCustomConversions(
Arrays.asList(new AddressToMap(), new MapToAddress()));
}
@WritingConverter
static class AddressToMap implements Converter<Address, Map<String, Object>> {
@Override
public Map<String, Object> convert(Address source) {
LinkedHashMap<String, Object> target = new LinkedHashMap<>();
target.put("ciudad", source.getCity());
// ...
return target;
}
}
@ReadingConverter
static class MapToAddress implements Converter<Map<String, Object>, Address> {
@Override
public Address convert(Map<String, Object> source) {
// ...
return address;
}
}
}
【4】Elasticsearch Operations
在第一部分描述新特性時,有個特性是Reactive programming support with Reactive Elasticsearch Operations and Reactive Elasticsearch Repositorie
,這裏不能簡單翻譯而要深入瞭解一下什麼是Reactive Elasticsearch Operations、Reactive Elasticsearch Repositorie才能知道如何提供響應式編程支持。
Spring Data Elasticsearch使用兩個接口來定義可以針對Elasticsearch索引調用的操作。分別是ElasticsearchOperations and ReactiveElasticsearchOperations。前者通常用於經典的同步實現,後者用於響應式編程。
接口的默認實現提供如下三方面功能:
- 對域類型的讀/寫映射支持。
- 一個豐富的查詢和條件api。
- 資源管理和異常轉換
① ElasticsearchTemplate
ElasticsearchTemplate是ElasticsearchOperations 接口的一個實現,是基於Transport Client操作的。
示例代碼如下:
@Configuration
public class TransportClientConfig extends ElasticsearchConfigurationSupport {
@Bean
public Client elasticsearchClient() throws UnknownHostException {
Settings settings = Settings.builder().put("cluster.name", "elasticsearch").build();
TransportClient client = new PreBuiltTransportClient(settings);
client.addTransportAddress(new TransportAddress(InetAddress.getByName("127.0.0.1"), 9300));
return client;
}
@Bean(name = {"elasticsearchOperations", "elasticsearchTemplate"})
public ElasticsearchTemplate elasticsearchTemplate() throws UnknownHostException {
return new ElasticsearchTemplate(elasticsearchClient(), entityMapper());
}
// use the ElasticsearchEntityMapper
@Bean
@Override
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
}
② ElasticsearchRestTemplate
ElasticsearchRestTemplate是ElasticsearchOperations 接口的另一個實現,其是基於High Level REST Client.實現的。
配置代碼示例如下:
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration {
@Override
public RestHighLevelClient elasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("192.168.18.128:9200")
.withSocketTimeout(60000)
.withConnectTimeout(60000)
.build();
RestHighLevelClient restHighLevelClient = RestClients.create(clientConfiguration).rest();
System.out.println("RestClientConfig-elasticsearchClient:"+restHighLevelClient);
return restHighLevelClient;
}
// no special bean creation needed
// use the ElasticsearchEntityMapper
@Bean
@Override
public EntityMapper entityMapper() {
ElasticsearchEntityMapper entityMapper = new ElasticsearchEntityMapper(elasticsearchMappingContext(),
new DefaultConversionService());
entityMapper.setConversions(elasticsearchCustomConversions());
return entityMapper;
}
}
③ Spring REST controller中使用ElasticsearchOperations
具體ElasticsearchOperations 使用哪種實現,主要依據上面兩種配置。示例代碼如下:
@RestController
public class TestController {
@Autowired
private ElasticsearchOperations elasticsearchOperations;
// public TestController(ElasticsearchOperations elasticsearchOperations) {
// this.elasticsearchOperations = elasticsearchOperations;
// }
@PostMapping("/person")
public String save(@RequestBody Person person) {
System.out.println("elasticsearchOperations:"+elasticsearchOperations);
IndexQuery indexQuery = new IndexQueryBuilder()
.withId(person.getId().toString())
.withObject(person)
.build();
String documentId = elasticsearchOperations.index(indexQuery);
return documentId;
}
@GetMapping("/person/{id}")
public Person findById(@PathVariable("id") Long id) {
Person person = elasticsearchOperations
.queryForObject(GetQuery.getById(id.toString()), Person.class);
return person;
}
}
Person類實例代碼:
@Document(indexName = "person",type = "person")
public class Person {
@Id
private Integer id;
@Field
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Person{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
}
上面兩個都是ElasticsearchOperations 的實現,下面講解Reactive Elasticsearch Operations實現。
④ Reactive Elasticsearch Operations
ReactiveElasticsearchOperations是Elasticsearch集羣使用ReactiveElasticsearchClient執行高級命令的網關。ReactiveElasticsearchOperations的默認實現是ReactiveElasticsearchTemplate 。
示例代碼如下:
@Configuration
public class Config extends AbstractReactiveElasticsearchConfiguration {
@Bean
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
// ...
}
}
使用ReactiveElasticsearchClient 最簡單方法就是繼承AbstractReactiveElasticsearchConfiguration 並覆蓋reactiveElasticsearchClient方法,然後作爲bean暴露給外部。方法體內部可以使用ReactiveRestClients 定義 Reactive Client或者默認使用DefaultReactiveElasticsearchClient。
如果想對組件有更多自定義控制,可以如下所示進行配置:
@Configuration
public class Config {
@Bean
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
// ...
}
@Bean
public ElasticsearchConverter elasticsearchConverter() {
return new MappingElasticsearchConverter(elasticsearchMappingContext());
}
@Bean
public SimpleElasticsearchMappingContext elasticsearchMappingContext() {
return new SimpleElasticsearchMappingContext();
}
@Bean
public ReactiveElasticsearchOperations reactiveElasticsearchOperations() {
return new ReactiveElasticsearchTemplate(reactiveElasticsearchClient(), elasticsearchConverter());
}
}
⑤ ReactiveElasticsearchTemplate實際使用
實例代碼如下:
@Document(indexName = "marvel", type = "characters")
public class Person {
private @Id String id;
private String name;
private int age;
// Getter/Setter omitted...
}
template.save(new Person("Bruce Banner", 42))
.doOnNext(System.out::println)
.flatMap(person -> template.findById(person.id, Person.class))
.doOnNext(System.out::println)
.flatMap(person -> template.delete(person))
.doOnNext(System.out::println)
.flatMap(id -> template.count(Person.class))
.doOnNext(System.out::println)
.subscribe();
控制檯打印:
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
> Person(id=QjWCWWcBXiLAnp77ksfR, name=Bruce Banner, age=42)
> QjWCWWcBXiLAnp77ksfR
> 0
可以發現相比於RestHighLevelClient 而言ReactiveElasticsearchTemplate主要就是流式編程,可以直接使用Lamda表達式及隱式函數進行操作。
【5】Elasticsearch Repositories
Repository,瞭解jpa的應該知道,Spring Data默認定義了一些接口實現了一些常用的增刪改查方法,你只需要聲明自己接口繼承於Spring Data的接口即可使用那些方法且不用自己實現。當然,你可以自己實現自己定義方法。
參考博文專欄:Spring Data JPA使用實踐詳解
① 查詢方法與查詢策略
Elasticsearch模塊支持所有基本的查詢構建功能,如字符串查詢、本地索查詢、基於條件的查詢或從方法名派生。從方法名派生查詢並不總是足夠的,並且/或者可能導致無法讀取方法名。在這種情況下,可以使用@Query註解。
通常,Elasticsearch的查詢創建機制的工作方式如查詢方法中所述,實例如下:
interface BookRepository extends Repository<Book, String> {
List<Book> findByNameAndPrice(String name, Integer price);
}
等同於如下Elasticsearch json 查詢串:
{ "bool" :
{ "must" :
[
{ "field" : {"name" : "?"} },
{ "field" : {"price" : "?"} }
]
}
}
Elasticsearch 支持的關鍵字與對應的json 查詢列表如下:
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And | findByNameAndPrice | {“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
Or | findByNameOrPrice | {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}} |
Is | findByName | {“bool” : {“must” : {“field” : {“name” : “?”}}}} |
Not | findByNameNot | {“bool” : {“must_not” : {“field” : {“name” : “?”}}}} |
Between | findByPriceBetween | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
LessThanEqual | findByPriceLessThan | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
GreaterThanEqual | findByPriceGreaterThan | “bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}} |
Before | findByPriceBefore | {“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}} |
After | findByPriceAfter | {“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}} |
Like | findByNameLike | {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}} |
StartingWith | findByNameStartingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}} |
EndingWith | findByNameEndingWith | {“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true}}}}} |
Contains/Containing | findByNameContaining | {“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true}}}}} |
In | findByNameIn(Collection<String>names) |
{“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}} |
NotIn | findByNameNotIn(Collection<String>names) |
{“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}} |
Near | findByStoreNear | Not Supported Yet ! |
True | findByAvailableTrue | {“bool” : {“must” : {“field” : {“available” : true}}}} |
False | findByAvailableFalse | {“bool” : {“must” : {“field” : {“available” : false}}}} |
OrderBy | findByAvailableTrueOrderByNameDesc | {“sort” : [{ “name” : {“order” : “desc”} }],“bool” : {“must” : {“field” : {“available” : true}}}} |
② @Query註解
如果想自定義查詢,可以選擇在方法上用註解標識查詢條件,實例如下:
interface BookRepository extends ElasticsearchRepository<Book, String> {
@Query("{\"bool\" : {\"must\" : {\"field\" : {\"name\" : \"?0\"}}}}")
Page<Book> findByName(String name,Pageable pageable);
}
③ 基於註解的配置
Spring Data Elasticsearch repositories using JavaConfig,for example:
@Configuration
@EnableElasticsearchRepositories(
basePackages = "org.springframework.data.elasticsearch.repositories"
)
static class Config {
@Bean
public ElasticsearchOperations elasticsearchTemplate() {
// ...
}
}
class ProductService {
private ProductRepository repository;
public ProductService(ProductRepository repository) {
this.repository = repository;
}
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
}
}
需要說明的是,EnableElasticsearchRepositories註解激活Repository 支持。如果沒有配置基包( base package),則它將使用在任何添加該註解的配置類上。
④ Elasticsearch Repositories using CDI*
Spring Data Elasticsearch repositories同樣支持CDI(上下文依賴注入)能力,實例如下:
class ElasticsearchTemplateProducer {
@Produces
@ApplicationScoped
public ElasticsearchOperations createElasticsearchTemplate() {
// ...
}
}
class ProductService {
private ProductRepository repository;
public Page<Product> findAvailableBookByName(String name, Pageable pageable) {
return repository.findByAvailableTrueAndNameStartingWith(name, pageable);
}
@Inject
public void setRepository(ProductRepository repository) {
this.repository = repository;
}
}
⑤ Spring applicationContext.xml中配置Elasticsearch
Setting up Elasticsearch repositories using Namespace:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
<elasticsearch:repositories base-package="com.acme.repositories" />
</beans>
Transport Client using Namespace:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/elasticsearch
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch-1.0.xsd">
<elasticsearch:transport-client id="client" cluster-nodes="localhost:9300,someip:9300" />
</beans>
Rest Client using Namespace:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:elasticsearch="http://www.springframework.org/schema/data/elasticsearch"
xsi:schemaLocation="http://www.springframework.org/schema/data/elasticsearch
https://www.springframework.org/schema/data/elasticsearch/spring-elasticsearch.xsd
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<elasticsearch:rest-client id="restClient" hosts="http://localhost:9200">
</beans>
【6】Reactive Elasticsearch Repositories
Reactive Elasticsearch repository支持Spring Data Repositories核心接口擴展,通過Reactive Elasticsearch Operations實現(Reactive Client實際執行)。
Reactive Elasticsearch repository是使用Project Reactor作爲其組件來實現響應編程。
有三個接口主要被使用:
-
ReactiveRepository
-
ReactiveCrudRepository
-
ReactiveSortingRepository
① 配置ReactiveRepositoryConfig
@Configuration
@EnableReactiveElasticsearchRepositories
public class ReactiveRepositoryConfig extends AbstractReactiveElasticsearchConfiguration {
@Override
public ReactiveElasticsearchClient reactiveElasticsearchClient() {
ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("192.168.18.128:9200")
.withSocketTimeout(60000)
.withConnectTimeout(60000)
.build();
ReactiveElasticsearchClient reactiveElasticsearchClient = ReactiveRestClients.create(clientConfiguration);
System.out.println("reactiveElasticsearchClient is created:"+reactiveElasticsearchClient);
return reactiveElasticsearchClient;
}
}
② Domain Object
@Document(indexName = "person",type = "person")
public class Person {
@Id
private String id;
private String firstname;
private String lastname;
//...
}
③ 自定義Repository接口繼承自ReactiveSortingRepository
實例代碼如下:
public interface ReactivePersonRepository extends ReactiveSortingRepository<Person, String> {
Flux<Person> findByFirstname(String firstname);
Flux<Person> findByFirstname(Publisher<String> firstname);
Flux<Person> findByFirstnameOrderByLastname(String firstname);
Flux<Person> findByFirstname(String firstname, Sort sort);
Flux<Person> findByFirstname(String firstname, Pageable page);
Mono<Person> findByFirstnameAndLastname(String firstname, String lastname);
Mono<Person> findFirstByLastname(String lastname);
@Query("{ \"bool\" : { \"must\" : { \"term\" : { \"lastname\" : \"?0\" } } } }")
Flux<Person> findByLastname(String lastname);
Mono<Long> countByFirstname(String firstname);
Mono<Boolean> existsByFirstname(String firstname);
Mono<Long> deleteByFirstname(String firstname);
}
可以看到ReactiveSortingRepository又繼承自ReactiveCrudRepository,故而當前ReactivePersonRepository 可以使用CRUD和Sort等方法。
④ 測試代碼
@RestController
public class ReactiveController {
@Autowired
ReactivePersonRepository repository;
@RequestMapping("/testReactive")
public String testReactive() {
Flux<Person> persons = repository.findAll();
System.out.println(persons.blockFirst());
Mono<List<Person>> listMono = persons.collectList();
System.out.println("listMono:"+ listMono);
List<Person> personList = listMono.block();
System.out.println("personList:"+personList);
return persons.toString();
}
}
【7】Miscellaneous Elasticsearch Operation Support
Elasticsearch Operation還提供了許多其他支持,而這些支持不能直接通過repository 接口來使用,它更被推薦在自定義接口實現裏面使用。
① 查詢過濾器
實例如下:
private ElasticsearchTemplate elasticsearchTemplate;
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withFilter(boolFilter().must(termFilter("id", documentId)))
.build();
Page<SampleEntity> sampleEntities =
elasticsearchTemplate.queryForPage(searchQuery,SampleEntity.class);
② 滾動輸出大結果集
Elasticsearch 針對大數據量結果提供了scroll API,ElasticsearchTemplate 有startScroll and continueScroll 方法可以被使用,如下所示:
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withIndices(INDEX_NAME)
.withTypes(TYPE_NAME)
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
ScrolledPage<SampleEntity> scroll = elasticsearchTemplate.startScroll(1000, searchQuery, SampleEntity.class);
String scrollId = scroll.getScrollId();
List<SampleEntity> sampleEntities = new ArrayList<>();
while (scroll.hasContent()) {
sampleEntities.addAll(scroll.getContent());
scrollId = scroll.getScrollId();
scroll = elasticsearchTemplate.continueScroll(scrollId, 1000, SampleEntity.class);
}
elasticsearchTemplate.clearScroll(scrollId);
ElasticsearchTemplate 同樣提供了流方法使用,如下所示:
SearchQuery searchQuery = new NativeSearchQueryBuilder()
.withQuery(matchAllQuery())
.withIndices(INDEX_NAME)
.withTypes(TYPE_NAME)
.withFields("message")
.withPageable(PageRequest.of(0, 10))
.build();
CloseableIterator<SampleEntity> stream = elasticsearchTemplate.stream(searchQuery, SampleEntity.class);
List<SampleEntity> sampleEntities = new ArrayList<>();
while (stream.hasNext()) {
sampleEntities.add(stream.next());
}
相關實例代碼GitHub地址:https://github.com/JanusJ/SpringBoot/tree/master/elasticsearch