RestClient的索引和文档操作

  • 1. 准备

(1)引入es的RestHighLevelClient依赖


    org.elasticsearch.client
    elasticsearch-rest-high-level-client

(2)因为SpringBoot默认的ES版本是7.6.2,所以需要覆盖默认的ES版本


    1.8
    7.12.1

(3)初始化RestHighLevelClient

将RestHighLevelClient交给你spring容器管理,否则报错:java.net.ConnectException: Timeout connecting to [localhost/127.0.0.1:9200]

    @Bean
    public RestHighLevelClient restHighLevelClient(){
        return new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.217.129:9200")
        ));
//或者
@Getter
@Setter
@Configuration
@ConfigurationProperties(prefix = "elasticsearch")
public class ElasticSearchConfig {
    private String host;
    private int port;

    @Bean
    public RestHighLevelClient client(){
        return new RestHighLevelClient(RestClient.builder(
                new HttpHost(host, port, "http")));
    }
}

#自定义elasticsearch连接配置
elasticsearch:
  host: 192.168.217.129
  port: 9200

2.索引操作

(1)创建Request对象

CreateIndexRequest request = new CreateIndexRequest("索引名");
  • CreateIndexRequest:创建索引库
  • DeleteIndexRequest:删除索引库
  • GetIndexRequest:判断索引库是否存在

(2)准备请求的参数:DSL语句

request.source(source, XContentType.JSON);
  • source为DSL的JSON参数部分,delete时无参

(3)发送请求

client.indices().create(request, RequestOptions.DEFAULT);
//批量添加文档到es
@SpringBootTest
public class Test {

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private RestHighLevelClient client;

    @Test
    public void addTset() throws Exception{
        //分页将数据添加进es
        int i = 1;
        while (true) {
            //远程调用获取item list
            PageDTO pageDTO = itemClient.selectByPage(i, 500);
            List dtoList = pageDTO.getList();
            if (dtoList.isEmpty()){
                break ;
            }
            BulkRequest request = new BulkRequest();
            for (Item item : dtoList) {
                if (item.getStatus()==2){
                    continue;
                }
                ItemDoc itemDoc = new ItemDoc(item);
                request.add(new IndexRequest("hmall")
                        .source(JSON.toJSONString(itemDoc), XContentType.JSON)
                        .id(String.valueOf(item.getId())));
            }
            client.bulk(request, RequestOptions.DEFAULT);
            i++;
        }
    }
}

 3.文档操作

核心方法:根据DSL语句结构去使用API

3.1 增删改查

根据DSL语句写代码,如图:

  • IndexRequest:新增
  • GetRequest:查询(根据id查询)
  • UpdateRequest:修改
  • DeleteRequest:删除
  • BulkRequest:批量删除 

RestClient的索引和文档操作_第1张图片

3.2 匹配查询

(1)发送查询请求

RestClient的索引和文档操作_第2张图片

 这里关键的API有两个,一个是request.source(),其中包含了查询、排序、分页、高亮等所有功能: RestClient的索引和文档操作_第3张图片

另一个是QueryBuilders,其中包含match、term、function_score、bool等各种查询:

RestClient的索引和文档操作_第4张图片

 (2)解析响应

RestClient的索引和文档操作_第5张图片

(3)排序、分页、高亮

搜索结果的排序和分页是与query同级的参数,因此同样是使用request.source()来设置。 RestClient的索引和文档操作_第6张图片

RestClient的索引和文档操作_第7张图片

 (4)高亮结果解析

高亮的结果与查询的文档结果默认是分离的,并不在一起。

因此解析高亮的代码需要额外处理:RestClient的索引和文档操作_第8张图片

(5)算分查询

RestClient的索引和文档操作_第9张图片RestClient的索引和文档操作_第10张图片

 3.3. 代码范例

@Service
public class HotelService extends ServiceImpl implements IHotelService {
    @Autowired
    private RestHighLevelClient client;
    @Override
    public PageResult search(RequestParams params) throws IOException {
        //创建request
        SearchRequest request = new SearchRequest("hotel");

        //1、设置请求参数
        //多字段查询和算分查询
        //注意:多字段查询用boolQuery,boolQuery必须为同一个,不能用多个QueryBuilders.boolQuery(),会导致多字段无法并联查询
        BoolQueryBuilder boolQueryBuilder = basicSearch(params,request);

        //2、传入请求参数
        SearchSourceBuilder query = request.source().query(boolQueryBuilder);

        //3、对查询结果处理:分页、按距离排序、高亮显示
        sortBycondition(params,request);

        //发送请求,解析响应
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        return convertResponse(response);
    }
----------------------------------------------------------------------------------------
    //多字段查询和算分查询
    private BoolQueryBuilder basicSearch(RequestParams params,SearchRequest request) {
        //先创建一个boolQuery
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //搜索为空,查询全部,不为空则匹配查询
        if (StringUtils.isNotBlank(params.getKey())){
            //多字段查询,对匹配结果打分,使用布尔查询
            boolQueryBuilder.must(QueryBuilders.matchQuery("all", params.getKey()));
        }else{
            boolQueryBuilder.must(QueryBuilders.matchAllQuery());
        }
        //城市:多字段查询,不需要算分,不需要分词
        if (params.getCity() != null) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("city", params.getCity()));
        }
        //星级:多字段查询,不需要算分,不需要分词
        if (params.getStarName()!=null){
            boolQueryBuilder.filter(QueryBuilders.termQuery("starName", params.getStarName()));
        }
        //品牌:多字段查询,不需要算分,需要分词
        if (params.getBrand()!=null){
            boolQueryBuilder.filter(QueryBuilders.matchQuery("brand", params.getBrand()));
        }
        //价格:多字段查询,不需要算分,不需要分词
        if (params.getMinPrice()!=null && params.getMaxPrice()!=null  ){
            boolQueryBuilder.filter(QueryBuilders.rangeQuery("price").gte(params.getMinPrice()).lte(params.getMaxPrice()));
        }

        //算分查询与多字段查询并列
        request.source().query(QueryBuilders.functionScoreQuery(
            // 原始查询,相关性算分的查询
                boolQueryBuilder,
            // function score的数组
            new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
                    // 其中的一个function score 元素
                    new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                            // 过滤条件
                            QueryBuilders.termQuery("isAD", true),
                            // 算分函数
                            ScoreFunctionBuilders.weightFactorFunction(10)
                    )
            }));
        return boolQueryBuilder;
    }
----------------------------------------------------------------------------------------
    //对查询结果处理:分页、按距离排序、高亮显示
    private void sortBycondition(RequestParams params, SearchRequest request) {
        //分页
        int page = params.getPage();
        int size = params.getSize();
        request.source().from((page - 1) * size).size(size);
        //按距离查询
        String location = params.getLocation();
        if (StringUtils.isNotBlank(location)){
            request.source().sort(
                    SortBuilders.geoDistanceSort("location",new GeoPoint(location))
                            .order(SortOrder.ASC).unit(DistanceUnit.KILOMETERS));
        }
        //高亮显示:前提是要有全文检索
        if (StringUtils.isNotBlank(params.getKey())) {
            request.source().highlighter(new HighlightBuilder().field("name").requireFieldMatch(false));
        }
    }
----------------------------------------------------------------------------------------
    //处理封装数据,封装基本信息,距离,高亮显示
    private PageResult convertResponse(SearchResponse response) {
        long total = response.getHits().getTotalHits().value;
        SearchHit[] hits = response.getHits().getHits();

        List hotelDocs = new ArrayList<>(hits.length);
        for (SearchHit hit : hits) {
            //获取source
            String string = hit.getSourceAsString();
            //反序列化
            HotelDoc hotelDoc = JSON.parseObject(string, HotelDoc.class);
            //封装距离参数
            Object[] distance = hit.getSortValues();
            if (distance.length>0){
            hotelDoc.setDistance(distance[0]);
            }
            //封装高亮参数
            Map highlightFields = hit.getHighlightFields();
            if (!CollectionUtils.isEmpty(highlightFields)) {
                //根据字段名获取高亮结果
                HighlightField highlightField = highlightFields.get("name");
                if (highlightField != null) {
                // 获取高亮值
                    String name = highlightField.getFragments()[0].string();
                // 覆盖非高亮结果
                    hotelDoc.setName(name);
                }
            }
            hotelDocs.add(hotelDoc);
        }
        return new PageResult(total,hotelDocs);
    }
}

4. 聚合查询

聚合条件与query条件同级别,因此需要使用request.source()来指定聚合条件。

聚合条件的语法:

RestClient的索引和文档操作_第11张图片

聚合的结果也与查询结果不同,API也比较特殊。不过同样是JSON逐层解析:  

RestClient的索引和文档操作_第12张图片

4.1 代码范例

    @Override
    public Map> getFilters(RequestParams params) throws IOException {
        //创建request
        SearchRequest request = new SearchRequest("hotel");
        //1、设置请求参数
        //在每次动态聚合前先多字段查询,保证在前置条件下聚合查询字段
        request.source().query(basicSearch(params,request));
        //不展示hit里面的信息,只展示聚合信息
        request.source().size(0);

        //2、动态聚合
        List city = buildAggregation(request, "city");
        List starName = buildAggregation(request, "starName");
        List brand = buildAggregation(request, "brand");
        List price = buildAggregation(request, "price");

        Map> map = new HashMap<>();
        map.put("city",city);
        map.put("starName",starName);
        map.put("brand",brand);

        return map;
    }

    //聚合查询
    private List buildAggregation(SearchRequest request, String feild) throws IOException {
        //聚合添加
        request.source().aggregation(AggregationBuilders.terms(feild).field(feild).size(100));
        //发送请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        //解析响应结果
       return getAggs(response,feild);
    }

    //解析响应结果
    private List getAggs(SearchResponse response, String feild) {
        //将Aggregations强转成Terms,否则无法调出bucket
        //Terms是Aggregations的子接口,子接口功能更多
        Terms term = response.getAggregations().get(feild);
        List buckets = term.getBuckets();
        //创建一个List装aggs信息
        ArrayList list = new ArrayList<>();
        for (Terms.Bucket bucket : buckets) {
            list.add(bucket.getKeyAsString());
        }
        return list;
    }

5. 自动补全

elasticsearch提供了Completion Suggester查询来实现自动补全功能

对于文档中字段的类型有一些约束:

  • 参与补全查询的字段必须是completion类型。
  • 字段的内容一般是用来补全的多个词条形成的数组。

RestClient的索引和文档操作_第13张图片

自动补全的结果解析的代码如下:  

RestClient的索引和文档操作_第14张图片

 5.1 代码范例

    @Override
    public List getSuggestion(String key) throws IOException {
        SearchRequest request = new SearchRequest("hotel");
        //1、设置请求参数
        request.source().suggest(new SuggestBuilder()
                .addSuggestion("my_suggest", SuggestBuilders
                        .completionSuggestion("suggestion")
                                .prefix(key)   //指定前缀
                                .skipDuplicates(true)
                                .size(10)));
        //2、发出请求
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);

        //3、解析响应
        //强转为CompletionSuggestion接口
        CompletionSuggestion suggestions = response.getSuggest().getSuggestion("my_suggest");
        //获取options
        List options = suggestions.getOptions();
        //封装数据
        ArrayList list = new ArrayList<>();
        for (CompletionSuggestion.Entry.Option option : options) {
            list.add(option.getText().toString());
        }
        return list;
    }

你可能感兴趣的:(elasticsearch,大数据,big,data)