Spring Data Elasticsearch基礎入門詳解

【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 and pattern 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章