ElasticSearch学习日志(1)

1,765 阅读10分钟

下载

  • 访问 https://www.elastic.co/cn/downloads/elasticsearch

本文档编写时最新版本为7.8

  • 根据需要选择下载方式
    image20200619141527902.png

安装

  • 解压压缩包
  • 进入解压目录 运行./bin/elasticsearch 启动Elastic,默认9200端口
  • 执行命令curl localhost:9200 请求端口返回说明信息说明启动成功
$: curl localhost:9200
{
  "name" : "dfcdd082ff4b",
  "cluster_name" : "docker-cluster",
  "cluster_uuid" : "qq-dpNhmTpu3pGhE10GJuA",
  "version" : {
    "number" : "7.8.0",
    "build_flavor" : "default",
    "build_type" : "docker",
    "build_hash" : "757314695644ea9a1dc2fecd26d1a43856725e65",
    "build_date" : "2020-06-14T19:35:50.234439Z",
    "build_snapshot" : false,
    "lucene_version" : "8.5.1",
    "minimum_wire_compatibility_version" : "6.8.0",
    "minimum_index_compatibility_version" : "6.0.0-beta1"
  },
  "tagline" : "You Know, for Search"
}
  • 关闭Elastic,修改./config/elasticsearch.yml
  • network.host的注释放开,修改为0.0.0.0 允许任何人的访问,线上环境需要设置为具体IP

安装插件

版本Elastic选择对应的版本

ES默认的分词器对中文并不友好,例如“疫情”在使用默认分词器会拆分成"疫"与”情“, 而不是”疫情“。所以需要添加分词器(Analyzer)

  • 中文分词器 IK
bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip
  • 拼音分词器PINYIN
bin/belasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.8.0/elasticsearch-analysis-ik-7.8.0.zip
  • 同义词插件

学习中,想到的场景可以通过NLP做语义分析,构建同义词库,然后通过分词器找关联

基本概念

Node和Cluster

Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。

单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)

Index

Elastic 会索引所有字段,经过处理后写入一个反向索引(Inverted Index)。查找数据的时候,直接查找该索引。

所以,Elastic 数据管理的顶层单位就叫做 Index(索引)。它是单个数据库的同义词。每个 Index (即数据库)的名字必须是小写。

下面的命令可以查看当前节点的所有 Index。

$ curl -X GET 'http://localhost:9200/_cat/indices?v'

Document

Index 里面单条的记录称为 Document(文档)。许多条 Document 构成了一个 Index。

Document 使用 JSON 格式表示,下面是一个例子。

{
  "user": "张三",
  "title": "工程师",
  "desc": "数据库管理"
}

同一个 Index 里面的 Document,不要求有相同的结构(scheme),但是最好保持相同,这样有利于提高搜索效率。

Type

Document 可以分组,比如weather这个 Index 里面,可以按城市分组(北京和上海),也可以按气候分组(晴天和雨天)。这种分组就叫做 Type,它是虚拟的逻辑分组,用来过滤 Document。

不同的 Type 应该有相似的结构(schema),举例来说,id字段不能在这个组是字符串,在另一个组是数值。这是与关系型数据库的表的一个区别。性质完全不同的数据(比如productslogs)应该存成两个 Index,而不是一个 Index 里面的两个 Type(虽然可以做到)。

下面的命令可以列出每个 Index 所包含的 Type。

$ curl 'localhost:9200/_mapping?pretty=true'

根据规划,Elastic 6.x 版只允许每个 Index 包含一个 Type,7.x 版将会彻底移除 Type。

Spring整合

transport使用tcp, restclient使用http。但是 8之后的版本将会去除transport

所以建议根据实际情况选择使用

JavaAPI

引入transport

<dependency>
	<groupId>org.elasticsearch.client</groupId>
	<artifactId>transport</artifactId>
	<version>${elasticsearch.version}</version>
</dependency>

实例化TransportClient

Settings settings = Settings.builder()
	.put("cluster.name", "elasticsearch")
	.build();
TransportClient client = new PreBuiltTransportClient(settings);
/*
 * ES的TCP端口为9300,而不是一般使用的9200
 * 这里只配置了一个节点的地址然添加进去,也可以配置多个从节点
 */
InetSocketTransportAddress node = new InetSocketTransportAddress(
InetAddress.getByName("localhost"),9300);
client.addTransportAddress(node);

Spring-Data-ElasticSearch

引入Spring-Data-ElasticSearch

Spring提供的依赖是基于transport,官方建议使用 Hight Level Rest Client

<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-elasticsearch</artifactId>
</dependency>

定义Document

@Document(indexName = "book")
class Book {
    @Id
    private Long id;

    @Field(searchAnalyzer = "ik_smart", analyzer = "ik_smart")
    private String name;
}
@Repository
public interface BookRepository extends ElasticsearchCrudRepository<Book, String> {
	
}

启用Repositories扫描

@EnableElasticsearchRepositories(basePackages="xxx.xxx.xx")

Rest Client/High Level Rest Client(推荐)

引入RestHightLevelClient

RestHightLevelClient是基于RestClient的高级封装

<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
    <version>${elasticsearch.client.version}</version>
</dependency>

配置对应的bean

/**
 * ElasticSearch配置类
 * 引入对应依赖,且上下文没有配置RestClientBuilder时触发
 *
 * @author shadow
 * @date 2020-06-22 23:20:49
 */
@Configuration
@ConditionalOnClass(RestClientBuilder.class)
@ConditionalOnMissingBean(RestClientBuilder.class)
public class ElasticSearchAutoConfiguration {

    @Autowired
    private RestClientProperties restClientProperties;

    /**
     * RestClientBuilder
     * @return {@link RestClientBuilder}
     */
    @Bean
    public RestClientBuilder restClientBuilder() {
        List<String> urls = restClientProperties.getUris();
        List<HttpHost> httpHosts = new ArrayList<>(urls.size());
        for(String uri: urls) {
            httpHosts.add(HttpHost.create(uri));
        }
        return RestClient.builder(httpHosts.toArray(new HttpHost[urls.size()]));
    }

    /**
     * RestClient
     * @return {@link RestClient}
     */
    @Bean
    public RestClient restClient() {
        return restClientBuilder().build();
    }

    /**
     * RestHighLevelClient
     * @return {@link RestHighLevelClient}
     */
    @Bean
    public RestHighLevelClient restHighLevelClient() {
        return new RestHighLevelClient(restClientBuilder());
    }
}

创建索引

PUT /xxxxx
{
    "mappings": {
        "properties": {
            "id": {
                "type": "long"
            },
            "content": {
                "type": "text",
                "analyzer": "ik_smart"
            },
            "content": {
            		"type": "text",
            		"analyzer": "ik_max_word",
            		"search_analyzer": "ik_smart"
            }
        }
    },
    "settings": {
        "number_of_replicas": 0,
        "index": {
            "analysis.analyzer.default.type": "ik_max_word",
            "analysis.search_analyzer.default.type": "ik_smart"
        }
    }
}

常用操作

以下都是按照High Level Rest Client编写

创建索引

CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
return restHighLevelClient.indices()
	.create(createIndexRequest, RequestOptions.DEFAULT)
	.index();

增加数据

每条数据都建议赋予一个独立的ID, 方便后续操作

IndexRequest indexRequest = new IndexRequest(index);
indexRequest.id(source.getId());
indexRequest.source(source); // {"id": "123456", "content": "xxxxxx"}

更新数据

IndexRequest indexRequest = new IndexRequest(index);
indexRequest.id(source.getId();
indexRequest.source(source);// {"id": "123456", "content": "yyyyyyy"}

删除数据

DeleteRequest deleteRequest = new DeleteRequest(index);
deleteRequest.id(id);
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);

查询数据

TermQuery

模糊查询,完整匹配输入。

类似于正则的 /x/g

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.termQuery(field, value));
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

MatchQuery

模糊查询,简单描述与term的区别是会对输入进行分词, 比如上面引入的中文分词,同义词等等。macth也就是一个部分匹配的模糊查询,相对比较宽松

类似于正则的 /.*(x|y).*/g

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchQuery(field, value));
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

MatchPhraseQuery

同MatchQuery,会对输入做分词,但是需要结果中也包含所有的分词,并且顺序要求一致

类似于正则的/.*x.*y.*z/g , 并且对结果也做一次校验

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.matchPhraseQuery(field, value));
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

QueryStringQuery

与match类似,但是不需要指定属性。会在所有字段中搜索,范围更广

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder.query(QueryBuilders.queryStringQuery(value));
SearchRequest searchRequest = new SearchRequest();
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

BoolQuery

与或非查询,对应Lucene的BooleanQuery

类似于正则的 /.*(a|b)((c)(d|e)).*/g

SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 添加条件
boolQueryBuilder.must().add(QueryBuilders.matchQuery(key, value));
boolQueryBuilder.should().add(QueryBuilders.matchQuery(key, value));

sourceBuilder.query(boolQueryBuilder);
SearchRequest searchRequest = new SearchRequest(index);
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

删除索引

注意,删除索引后,索引下的所有数据一并清除

DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);

接口定义

接口类

/**
 * 搜索服务接口
 *
 * @param <E> 实现类泛型
 * @author shadow
 * @version 0.0.1
 * @date 2020-02-21
 * @since 0.0.1
 */
public interface SearchService<E> {

    /**
     * 索引是否存在
     *
     * @param index 索引
     * @return true/false
     * @throws IOException IO
     */
    boolean exists(String index) throws IOException;

    /**
     * 索引是否存在
     *
     * @param indices 多索引
     * @return true/false
     * @throws IOException IO
     */
    boolean exists(String... indices) throws IOException;

    /**
     * 索引
     *
     * @param index 索引
     * @return 索引
     * @throws IOException IO
     */
    String index(String index) throws IOException;

    /**
     * 索引
     *
     * @param index       索引
     * @param mappingPath {@link com.hephaestus.search.configuration.ElasticSearchMappingConfig:prefix} 拼接
     * @return 索引
     * @throws IOException IO
     */
    String index(String index, String mappingPath) throws IOException;

    /**
     * 多索引
     *
     * @param indices 多索引
     * @return 索引组
     * @throws IOException IO
     */
    String[] index(String... indices) throws IOException;

    /**
     * 添加文档
     *
     * @param index  索引
     * @param source 源
     * @return 索引
     * @throws IOException IO
     */
    String document(String index, Object source) throws IOException;

    /**
     * 添加文档
     *
     * @param indices 多索引
     * @return 索引组
     * @throws IOException IO
     */
    String[] document(Map<String, BaseEntity> indices) throws IOException;

    /**
     * 删除索引
     *
     * @param index 索引
     * @return true/false
     * @throws IOException IO
     */
    Boolean delete(String index) throws IOException;

    /**
     * 删除多索引
     *
     * @param indices 多索引
     * @return true/false
     * @throws IOException IO
     */
    Boolean delete(String... indices) throws IOException;

    /**
     * 删除
     *
     * @param index 索引
     * @param id    id
     * @return true/false
     * @throws IOException IO
     */
    Boolean delete(String index, String id) throws IOException;

    /**
     * 条件删除
     *
     * @param index 索引
     * @param key   属性名
     * @param value 属性值
     * @return 删除个数
     * @throws IOException IO
     */
    Long queryDelete(String index, String key, String value) throws IOException;

    /**
     * 搜索
     *
     * @param index 索引
     * @param field 属性名
     * @param value 值
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    SearchHit[] search(String index, String field, String value) throws IOException;

    /**
     * 搜索
     *
     * @param index  索引
     * @param option 条件对象
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    SearchHit[] search(String index, Object option) throws IOException;

    /**
     * 搜索
     *
     * @param index   下标
     * @param options 多条件数组
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    SearchHit[] search(String index, Object[] options) throws IOException;

    /**
     * 搜索-根据索引
     *
     * @param index 索引
     * @param field 属性名
     * @param value 值
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> searchByIndex(String index, String field, String value) throws IOException {
        SearchHit[] hits = search(index, field, value);
        return toList(hits);
    }

    /**
     * 搜索-根据索引
     *
     * @param index  索引
     * @param option 条件对象
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> searchByIndex(String index, Object option) throws IOException {
        SearchHit[] hits = search(index, option);
        return toList(hits);
    }

    /**
     * 搜索-根据索引
     *
     * @param index   索引
     * @param options 条件组
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> searchByIndex(String index, Object[] options) throws IOException {
        SearchHit[] hits = search(index, options);
        return toList(hits);
    }

    /**
     * 搜索
     *
     * @param field 属性名
     * @param value 值
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> search(String field, String value) throws IOException {
        return searchByIndex(null, field, value);
    }

    /**
     * 搜索
     *
     * @param option 条件对象
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> search(Object option) throws IOException {
        return searchByIndex(null, option);
    }

    /**
     * 搜索
     *
     * @param options 条件组
     * @return {@link List<String>}
     * @throws IOException IO
     */
    default List<String> search(Object[] options) throws IOException {
        return toList(search(null, options));
    }

    /**
     * 搜索
     *
     * @param options 条件组
     * @param clazz   类
     * @param <T>     泛型
     * @return {@link List<T>}
     * @throws IOException IO
     */
    default <T> List<T> search(Object[] options, Class<T> clazz) throws IOException {
        return toList(search(options), clazz);
    }

    /**
     * 搜索
     *
     * @param index   索引
     * @param options 条件组
     * @param clazz   类
     * @param <T>     泛型
     * @return {@link List<T>}
     * @throws IOException IO
     */
    default <T> List<T> search(String index, Object[] options, Class<T> clazz) throws IOException {
        return toList(searchByIndex(index, options), clazz);
    }

    /**
     * 搜索
     *
     * @param option 条件对象
     * @param clazz  类
     * @param <T>    泛型
     * @return {@link List<T>}
     * @throws IOException IO
     */
    default <T> List<T> search(Object option, Class<T> clazz) throws IOException {
        return toList(searchByIndex(null, option), clazz);
    }

    /**
     * 搜索
     *
     * @param field 属性名
     * @param value 值
     * @param clazz 类
     * @param <T>   泛型
     * @return {@link List<T>}
     * @throws IOException IO
     */
    default <T> List<T> search(String field, String value, Class<T> clazz) throws IOException {
        List<String> hits = search(field, value);
        return toList(hits, clazz);
    }

    /**
     * 搜索
     *
     * @param index 索引
     * @param field 属性名
     * @param value 值
     * @param clazz 类
     * @param <T>   泛型
     * @return {@link List<T>}
     * @throws IOException IO
     */
    default <T> List<T> search(String index, String field, String value, Class<T> clazz) throws IOException {
        List<String> hits = searchByIndex(index, field, value);
        return toList(hits, clazz);
    }

    /**
     * 转换为List
     *
     * @param hits {@link List<SearchHit>}
     * @return {@link List<String>} json字符串
     */
    default List<String> toList(SearchHit[] hits) {
        List<String> results = new ArrayList<>(hits.length);
        Arrays.stream(hits).forEach(hit -> results.add(hit.getSourceAsString()));
        return results;
    }

    /**
     * 转换为List
     *
     * @param hits  {@link List<SearchHit>}
     * @param clazz 类
     * @param <T>   泛型
     * @return {@link List<T>} 泛型转换
     */
    default <T> List<T> toList(List<String> hits, Class<T> clazz) {
        List<T> results = new ArrayList<>(hits.size());
        hits.forEach(hit -> results.add(JsonUtils.toObject(hit, clazz)));
        return results;
    }


    /**
     * 获取搜索实例
     *
     * @return <E> 实现类
     */
    E getOrigin();
}

接口实现

/**
 * 搜索服务实现类
 * 基于ElasticSearch RestHighLevelClient实现
 *
 * @author shadow
 * @version 0.0.1
 * @date 2020-02-21
 * @since 0.0.1
 *
 * <p>
 * 压制ali-p3c规范, 不以impl结尾
 */
@SuppressWarnings("AlibabaServiceOrDaoClassShouldEndWithImpl")
@Service
public class ElasticSearchService implements SearchService<RestHighLevelClient> {

    private static final Logger LOGGER = LoggerFactory.getLogger(ElasticSearchService.class);

    /** 默认ID属性 */
    private static final String FIELD_ID = "id";
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ElasticSearchMappingConfig elasticSearchMappingConfig;

    /**
     * 索引是否存在
     *
     * @param index 索引
     * @return true/false
     */
    @Override
    public boolean exists(String index) throws IOException {
        return exists(new String[]{index});
    }

    /**
     * 索引是否存在
     *
     * @param indices 多索引
     * @return true/false
     * @throws IOException IO
     */
    @Override
    public boolean exists(String... indices) throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest(indices);
        return restHighLevelClient.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
    }

    /**
     * 索引
     *
     * @param index 索引
     * @return 索引
     * @throws IOException IO
     */
    @Override
    public String index(String index) throws IOException {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        return restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT).index();
    }

    /**
     * 索引
     *
     * @param index       索引
     * @param mappingPath {@link ElasticSearchMappingConfig:prefix} 拼接
     * @return 索引
     * @throws IOException IO
     */
    @Override
    public String index(String index, String mappingPath) throws IOException {
        CreateIndexRequest createIndexRequest = new CreateIndexRequest(index);
        File resource = ResourceUtils.getFile(elasticSearchMappingConfig.getPrefix() + mappingPath);
        String source = JSONUtil.readJSON(resource, StandardCharsets.UTF_8).toStringPretty();
        createIndexRequest.source(source, XContentType.JSON);
        return restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT).index();
    }

    /**
     * 多索引
     *
     * @param indices 多索引
     * @return 索引组
     * @throws IOException IO
     */
    @Override
    public String[] index(String... indices) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        Arrays.stream(indices).forEach(index -> {
            IndexRequest indexRequest = new IndexRequest(index);
            bulkRequest.add(indexRequest);
        });
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        List<String> result = new ArrayList<>(indices.length);
        Arrays.stream(bulkResponse.getItems()).forEach(response -> result.add(response.getIndex()));
        return result.toArray(new String[indices.length]);
    }

    /**
     * 添加文档
     *
     * @param index  索引
     * @param source 源
     * @return 索引
     * @throws IOException 1
     */
    @Override
    public String document(String index, Object source) throws IOException {
        IndexRequest indexRequest = new IndexRequest(index);
        Object id = BeanUtil.getProperty(source, FIELD_ID);
        if (id != null) {
            indexRequest.id(id.toString());
        }
        indexRequest.source(BeanUtil.beanToMap(source));
        return restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT).getIndex();
    }

    /**
     * 添加文档
     *
     * @param indices 多索引
     * @return 索引组
     * @throws IOException IO
     */
    @Override
    public String[] document(Map<String, BaseEntity> indices) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        indices.forEach((index, value) -> {
            IndexRequest indexRequest = new IndexRequest(index);
            indexRequest.source(value);
            indexRequest.id(BeanUtil.getProperty(value, FIELD_ID).toString());
            bulkRequest.add(indexRequest);
        });
        BulkResponse bulkResponse = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        List<String> result = new ArrayList<>(indices.size());
        Arrays.stream(bulkResponse.getItems()).forEach(response -> result.add(response.getIndex()));
        return result.toArray(new String[indices.size()]);
    }

    /**
     * 删除
     *
     * @param index 索引
     * @param id    id
     * @return true/false
     * @throws IOException IO
     */
    @Override
    public Boolean delete(String index, String id) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(index);
        deleteRequest.id(id);
        DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
        return deleteResponse.getId().equals(id);
    }

    /**
     * 删除索引
     *
     * @param index 索引
     * @return true/false
     * @throws IOException IO
     */
    @Override
    public Boolean delete(String index) throws IOException {
        DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest(index);
        AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(deleteIndexRequest, RequestOptions.DEFAULT);
        return acknowledgedResponse.isAcknowledged();
    }

    /**
     * @param indices 多索引
     * @return true/false
     * @throws IOException IO
     */
    @Override
    public Boolean delete(String... indices) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        for (String index : indices) {
            DeleteRequest deleteRequest = new DeleteRequest(index);
            bulkRequest.add(deleteRequest);
        }
        BulkResponse bulkItemResponses = restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
        return bulkItemResponses.hasFailures();
    }

    /**
     * 条件删除
     *
     * @param index 索引
     * @param key   属性名
     * @param value 属性值
     * @return 删除个数
     * @throws IOException IO
     */
    @Override
    public Long queryDelete(String index, String key, String value) throws IOException {
        DeleteByQueryRequest deleteByQueryRequest = new DeleteByQueryRequest(index);
        deleteByQueryRequest.setQuery(new TermQueryBuilder(key, value));
        BulkByScrollResponse bulkByScrollResponse = restHighLevelClient.deleteByQuery(deleteByQueryRequest, RequestOptions.DEFAULT);
        return bulkByScrollResponse.getTotal();
    }

    /**
     * 搜索
     *
     * @param index 索引
     * @param field 属性名
     * @param value 值
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    @Override
    public SearchHit[] search(String index, String field, String value) throws IOException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(QueryBuilders.matchQuery(field, value));
        SearchRequest searchRequest = index == null ? new SearchRequest() : new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
        return searchResponse.getHits().getHits();
    }

    /**
     * 搜索
     *
     * @param index   索引
     * @param option 条件对象
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    @Override
    public SearchHit[] search(String index, Object option) throws IOException {
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        BeanUtil.beanToMap(option).forEach((key, value) -> {
            if (value != null) {
                boolQueryBuilder.must().add(QueryBuilders.matchQuery(key, value));
            }
        });
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = index == null ? new SearchRequest() : new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        return searchResponse.getHits().getHits();
    }

    /**
     * 搜索
     * @param index 下标
     * @param options 多条件数组
     * @return {@link SearchHit }
     * @throws IOException IO
     */
    @Override
    public SearchHit[] search(String index, Object[] options) throws IOException{
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        // 数组forEach循环,然后将对象转为Map,null值跳过
        Arrays.stream(options).forEach(option -> BeanUtil.beanToMap(option).forEach((key, value) -> {
            if (value != null) {
                boolQueryBuilder.must().add(QueryBuilders.matchQuery(key, value));
            }
        }));
        sourceBuilder.query(boolQueryBuilder);
        SearchRequest searchRequest = index == null ? new SearchRequest() : new SearchRequest(index);
        searchRequest.source(sourceBuilder);
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        return searchResponse.getHits().getHits();
    }
    /**
     * 获取搜索实例
     *
     * @return T 搜索实例
     */
    @Override
    public RestHighLevelClient getOrigin() {
        return restHighLevelClient;
    }

}

可视化工具

Elastic HD

下载地址

参考文献

中文文档

中文文档基于ElasticSearch2.x版本,可能部分内容已经过时,建议阅读较新的原文版本

Elastic产品文档

全文搜索引擎 Elasticsearch 入门教程-阮一峰