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 TrainSpring Data ElasticsearchElasticsearchSpring Boot
Moore3.2.x6.8.42.2.x
Lovelace3.1.x6.2.22.1.x
Kay3.0.x5.5.02.0.x
Ingalls2.1.x2.4.01.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 查询列表如下:

KeywordSampleElasticsearch Query String
AndfindByNameAndPrice{“bool” : {“must” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
OrfindByNameOrPrice{“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“price” : “?”}} ]}}
IsfindByName{“bool” : {“must” : {“field” : {“name” : “?”}}}}
NotfindByNameNot{“bool” : {“must_not” : {“field” : {“name” : “?”}}}}
BetweenfindByPriceBetween{“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
LessThanEqualfindByPriceLessThan{“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
GreaterThanEqualfindByPriceGreaterThan“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
BeforefindByPriceBefore{“bool” : {“must” : {“range” : {“price” : {“from” : null,“to” : ?,“include_lower” : true,“include_upper” : true}}}}}
AfterfindByPriceAfter{“bool” : {“must” : {“range” : {“price” : {“from” : ?,“to” : null,“include_lower” : true,“include_upper” : true}}}}}
LikefindByNameLike{“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}}
StartingWithfindByNameStartingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “?*”,“analyze_wildcard” : true}}}}}
EndingWithfindByNameEndingWith{“bool” : {“must” : {“field” : {“name” : {“query” : “*?”,“analyze_wildcard” : true}}}}}
Contains/ContainingfindByNameContaining{“bool” : {“must” : {“field” : {“name” : {“query” : “?”,“analyze_wildcard” : true}}}}}
InfindByNameIn(Collection<String>names){“bool” : {“must” : {“bool” : {“should” : [ {“field” : {“name” : “?”}}, {“field” : {“name” : “?”}} ]}}}}
NotInfindByNameNotIn(Collection<String>names){“bool” : {“must_not” : {“bool” : {“should” : {“field” : {“name” : “?”}}}}}}
NearfindByStoreNearNot Supported Yet !
TruefindByAvailableTrue{“bool” : {“must” : {“field” : {“available” : true}}}}
FalsefindByAvailableFalse{“bool” : {“must” : {“field” : {“available” : false}}}}
OrderByfindByAvailableTrueOrderByNameDesc{“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

©️2020 CSDN 皮肤主题: 程序猿惹谁了 设计师:上身试试 返回首页