ElasticSearch-7.x RestHighLevelClient 常用API

  关于 ElasticSearch 的一些基础概念可以参考我之前基于6.x总结的:https://www.cnblogs.com/wuzhenzhao/category/1767990.html

  本文主要总结在Java 中如何应用 RestHighLevelClient 。测试方法基于 main 进行。

1、引入依赖:

 1      <dependency>
 2             <groupId>org.elasticsearch</groupId>
 3             <artifactId>elasticsearch</artifactId>
 4         </dependency>
 5         <dependency>
 6             <groupId>org.elasticsearch.client</groupId>
 7             <artifactId>elasticsearch-rest-client</artifactId>
 8         </dependency>
 9         <dependency>
10             <groupId>org.elasticsearch.client</groupId>
11             <artifactId>elasticsearch-rest-high-level-client</artifactId>
12         </dependency>

2、初始化连接 RestHighLevelClient

private final static RequestOptions options = RequestOptions.DEFAULT;
    
    public static RestHighLevelClient restHighLevelClient() {
        //ElasticSearch 连接地址地址
        HttpHost[] httpHosts = getElasticSearchHttpHosts("127.0.0.1:9200");

        RestClientBuilder restClientBuilder = RestClient.builder(httpHosts).setRequestConfigCallback(requestConfigBuilder -> {
            //设置连接超时时间
            requestConfigBuilder.setConnectTimeout(1000);
            requestConfigBuilder.setSocketTimeout(30000);
            //连接的超时时间
            requestConfigBuilder.setConnectionRequestTimeout(500);
            return requestConfigBuilder;
        }).setHttpClientConfigCallback(httpClientBuilder -> {
            httpClientBuilder.disableAuthCaching();
            httpClientBuilder.setKeepAliveStrategy((response, context) -> 60 * 1000);
            //设置账密
            return getHttpAsyncClientBuilder(httpClientBuilder, "username", "password");
        });
        return new RestHighLevelClient(restClientBuilder);
    }


    /**
     * ElasticSearch 连接地址
     * 多个逗号分隔
     * 示例:127.0.0.1:9201,127.0.0.1:9202,127.0.0.1:9203
     */
    private static HttpHost[] getElasticSearchHttpHosts(String address) {
        String[] hosts = address.split(",");
        HttpHost[] httpHosts = new HttpHost[hosts.length];
        for (int i = 0; i < httpHosts.length; i++) {
            String host = hosts[i];
            host = host.replaceAll("http://", "").replaceAll("https://", "");
            httpHosts[i] = new HttpHost(host.split(":")[0], Integer.parseInt(host.split(":")[1]), "http");
        }
        return httpHosts;
    }

    private static HttpAsyncClientBuilder getHttpAsyncClientBuilder(HttpAsyncClientBuilder httpClientBuilder, String username, String password) {
        if (StringUtils.isBlank(username) || StringUtils.isEmpty(password)) {
            return httpClientBuilder;
        }
        //账密设置
        CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
        //es账号密码(一般使用,用户elastic)
        credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username, password));
        httpClientBuilder.setDefaultCredentialsProvider(credentialsProvider);
        return httpClientBuilder;
    }

 3、创建索引 --(数据库中的建表,最好是将必要的字段类型定义完整)

public static boolean createIndex(RestHighLevelClient client, String indexName) {
        try {
            if (!checkIndex(client, indexName)) {
                CreateIndexRequest request = new CreateIndexRequest(indexName);
                //mapping 主要是定于字段的类型,若无定义则ES会自动映射
                request.mapping("{\"properties\":{\"eventId\":{\"type\":\"keyword\"}}}", XContentType.JSON);
                client.indices().create(request, options);
                return Boolean.TRUE;
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

4、判断索引是否存在

public static boolean checkIndex(RestHighLevelClient client, String index) {
        try {
            return client.indices().exists(new GetIndexRequest(index), options);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

5、删除索引 -- 删除表

public static boolean deleteIndex(RestHighLevelClient client, String indexName) {
        try {
            if (checkIndex(client, indexName)) {
                DeleteIndexRequest request = new DeleteIndexRequest(indexName);
                AcknowledgedResponse response = client.indices().delete(request, options);
                return response.isAcknowledged();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

6、插入单条数据

public static boolean insert(RestHighLevelClient client, String indexName, String dataJson) {
        try {
            //ID 可以自己指定,无非是个唯一键,关联业务上的唯一键也可
            IndexRequest source = new IndexRequest(indexName, "_doc").id("1")
                    .source(dataJson, XContentType.JSON);
            client.index(source, options);
            return Boolean.TRUE;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

7、批量写入数据

public static boolean batchInsert(RestHighLevelClient client, String indexName, List<EventInfo> dataJsons) {
        try {
            BulkRequest request = new BulkRequest();
            for (EventInfo data : dataJsons) {
                request.add(new IndexRequest(indexName, "_doc").id(data.getEventId())
                        .opType("create").source(JSONObject.toJSONString(data), XContentType.JSON));
            }
            client.bulk(request, options);
            return Boolean.TRUE;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

8、修改数据

public static void update(RestHighLevelClient client, String indexName, String id) throws IOException {
        //根据 id 来修改
        UpdateRequest updateRequest = new UpdateRequest(indexName, id);
        Map<String, Object> kvs = new HashMap<>();
        kvs.put("eventAdress", "杭州");
        updateRequest.doc(kvs);
        updateRequest.timeout(TimeValue.timeValueSeconds(1));
        updateRequest.setRefreshPolicy(WriteRequest.RefreshPolicy.WAIT_UNTIL);
        client.update(updateRequest, RequestOptions.DEFAULT);
    }

9、条件修改

public static void updateQuery(RestHighLevelClient client, String indexName, String id) throws IOException {
        UpdateByQueryRequest updateByQueryRequest = new UpdateByQueryRequest();
        updateByQueryRequest.indices(indexName);

        updateByQueryRequest.setQuery(new TermQueryBuilder("id", id));
        ////设置要修改的内容可以多个值多个用;隔开
        updateByQueryRequest.setScript(new Script(ScriptType.INLINE,
                "painless",
                "eventAdress='杭州市'", Collections.emptyMap()));
        client.updateByQuery(updateByQueryRequest, RequestOptions.DEFAULT);
    }

10、批量修改

private static void batchUpdate(RestHighLevelClient client, String indexName) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout("1m"); // 设置超时时间
        List<UpdateRequest> updateRequests=new ArrayList<>();

        //更新的数据
        List<EventInfo> params=new ArrayList<>();
        params.add(new EventInfo());

        params.forEach(e->{
            //获取id
            UpdateRequest updateRequest = new UpdateRequest();
            updateRequest.index(indexName);
            //更新的id
            updateRequest.id(e.getEventId());
            //更新的数据
            Map<String,Object> map=new HashMap<>();
            map.put("eventAdress","杭州市");

            updateRequest.doc(map);
            bulkRequest.add(updateRequest);
        });
        // 执行批量更新
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);

        // 检查结果
        if (bulkResponse.hasFailures()) {
            // 处理失败的情况
            System.out.println("Bulk update failed for: " + bulkResponse.buildFailureMessage());
        } else {
            System.out.println("Bulk update succeeded");
        }
    }

 11、删除数据

public static boolean deleteByQuery(RestHighLevelClient client, String indexName, String key, String value) {
        try {
            //DeleteRequest  根据id删除
//            DeleteRequest request = new DeleteRequest(indexName, "id");
            //BulkRequest  批量删除,参考批量修改一般建议是1000-5000个文档,如果你的文档很大,
            // 可以适当减少队列,大小建议是5-15MB,默认不能超过100M,可以在es的配置文件(即$ES_HOME下的config下的elasticsearch.yml)中。
            //条件删除
            DeleteByQueryRequest deleteRequest = new DeleteByQueryRequest(indexName);
            deleteRequest.setQuery(new TermQueryBuilder(key, value));
            client.deleteByQuery(deleteRequest, options);
            return Boolean.TRUE;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Boolean.FALSE;
    }

12、总数查询

public static Long count(RestHighLevelClient client, String indexName) {
        // 指定创建时间
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        queryBuilder.must(QueryBuilders.termQuery("createTime", "2024-04-19T20:23:26"));

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(queryBuilder);

        CountRequest countRequest = new CountRequest(indexName);
        countRequest.source(sourceBuilder);
        try {
            CountResponse countResponse = client.count(countRequest, options);
            return countResponse.getCount();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return 0L;
    }

13、简单分页查询

public static List<EventInfo> page(RestHighLevelClient client, String indexName, Integer offset, Integer size) {
        // 查询条件,指定时间并过滤指定字段值
        SearchRequest searchRequest = new SearchRequest(indexName);

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        //构建查询条件
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
//        boolQueryBuilder.must(QueryBuilders.termsQuery("eventId", Arrays.asList("SJ999995191", "SJ999995193", "SJ999995194", "SJ999995195")));

        boolQueryBuilder.must(QueryBuilders.termsQuery("eventType", Arrays.asList("cem.hygiene_health1", "cem.city_management35")));
//        boolQueryBuilder.must(QueryBuilders.termsQuery("regionCode", Arrays.asList("65411ca27f3946538545ab2deaa8162d")));
        sourceBuilder.query(boolQueryBuilder);
        //offset 从 0 开始
        sourceBuilder.from(offset);
        sourceBuilder.size(size);
        sourceBuilder.sort("createTime", SortOrder.DESC);
        searchRequest.source(sourceBuilder);

        try {
            SearchResponse searchResp = client.search(searchRequest, options);
            List<EventInfo> data = new ArrayList<>();
            SearchHit[] searchHitArr = searchResp.getHits().getHits();
            for (SearchHit searchHit : searchHitArr) {
                String sourceAsString = searchHit.getSourceAsString();
                data.add(JSONObject.parseObject(sourceAsString, EventInfo.class));
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

14、滚动查询

 private static void scrollQuery(RestHighLevelClient restHighLevelClient, String indexName) throws IOException {

        //1. 创建SearchRequest
        SearchRequest request = new SearchRequest(indexName);


        //2. 指定scroll信息!
        request.scroll(TimeValue.timeValueMinutes(1L));//指定生存时间为1m,1分钟

        //3. 指定查询条件
        SearchSourceBuilder builder = new SearchSourceBuilder();
        builder.size(2);//每页4条数据
//        builder.sort("age", SortOrder.DESC);//按年龄排序
//        builder.fetchSource(new String[]{"userName", "age"}, null);//只返回userName和age两个字段
        builder.query(QueryBuilders.matchAllQuery());

        request.source(builder);

        //4. 获取返回结果scrollId,source
        SearchResponse resp = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        String scrollId = resp.getScrollId();
        System.out.println("----------首页---------" + scrollId);
        for (SearchHit hit : resp.getHits().getHits()) {
            System.out.println(hit.getSourceAsMap());
        }
        //模拟下一页
        while (true) {
            //5. 循环 - 创建SearchScrollRequest
            SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);//根据前面得到的scorll_id去指定

            //6. 指定scrollId的生存时间!
            scrollRequest.scroll(TimeValue.timeValueMinutes(1L));

            //7. 执行查询获取返回结果
            SearchResponse scrollResp = restHighLevelClient.scroll(scrollRequest, RequestOptions.DEFAULT);

            //8. 判断是否查询到了数据,输出
            SearchHit[] hits = scrollResp.getHits().getHits();

            if (hits != null && hits.length > 0) {
                System.out.println("----------下一页---------");
                for (SearchHit hit : hits) {
                    System.out.println(hit.getSourceAsMap());
                }
            } else {
                //9. 判断没有查询到数据-退出循环
                System.out.println("----------结束---------");
                break;
            }
        }

        //10. 创建CLearScrollRequest
        ClearScrollRequest clearScrollRequest = new ClearScrollRequest();

        //11. 指定ScrollId
        clearScrollRequest.addScrollId(scrollId);

        //12. 删除ScrollId
        ClearScrollResponse clearScrollResponse = restHighLevelClient.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);

        //13. 输出结果
        System.out.println("删除scroll:" + clearScrollResponse.isSucceeded());
        restHighLevelClient.close();

    }

15、分页查询的常见条件匹配查询

public static List<EventInfo> pageSerach(RestHighLevelClient client, String indexName) {

        //1.创建 SearchRequest搜索请求
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);//指定要查询的索引

        //2.创建 SearchSourceBuilder条件构造。builder模式这里就先不简写了
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
        searchSourceBuilder.query(matchAllQueryBuilder);
        //分页参数
        searchSourceBuilder.from(0);
        searchSourceBuilder.size(100);

        //createTime倒序
        searchSourceBuilder.sort("createTime", SortOrder.DESC);

        //数据过滤---指定需要返回或者排除的字段
//        String[] includes = {
//                "eventId", "createTime","eventAddress"};
//        String[] excludes = {
//        };
//        searchSourceBuilder.fetchSource(includes, excludes);


        //match 查找match在匹配时会对所查找的关键词进行分词,然后按分词匹配查找。
//        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("eventAddress", "13001");
//        matchQueryBuilder.operator(Operator.OR);
//        searchSourceBuilder.query(matchQueryBuilder);

        //多字段查询multi_match
//        MultiMatchQueryBuilder multiMatchQuery = QueryBuilders.multiMatchQuery("处理流程","eventAddress", "dutyName");
//        multiMatchQuery.operator(Operator.OR);
//        searchSourceBuilder.query(multiMatchQuery);


        //Term 查找精确查询Term
//        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("eventAddress.keyword", "浙江省");
//        searchSourceBuilder.query(termQueryBuilder);

        //Range 查找 范围查询range
//        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("createTime");
//        rangeQueryBuilder.gte("2024-03-20T10:07:43");
//        rangeQueryBuilder.lt("2024-03-20T22:07:43");
//        searchSourceBuilder.query(rangeQueryBuilder);


        //多个id 查找
//        IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery();
//        idsQueryBuilder.addIds("2", "5", "1111");
//        searchSourceBuilder.query(idsQueryBuilder);

        //WildcardQueryBuilder 查找
//        WildcardQueryBuilder wildcardQueryBuilder = QueryBuilders.wildcardQuery("dutyName.keyword", "*道及门前*");
//        searchSourceBuilder.query(wildcardQueryBuilder);

        //3.将 SearchSourceBuilder 添加到 SearchRequest中
        searchRequest.source(searchSourceBuilder);

        //4.执行查询
        try {
            SearchResponse searchResp = client.search(searchRequest, options);
            List<EventInfo> data = new ArrayList<>();
            SearchHit[] searchHitArr = searchResp.getHits().getHits();
            for (SearchHit searchHit : searchHitArr) {
                String sourceAsString = searchHit.getSourceAsString();
                data.add(JSONObject.parseObject(sourceAsString, EventInfo.class));
            }
            return data;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

16、分组、半径搜索

public static List<EventInfo> group(RestHighLevelClient client, String indexName) {

        //1.创建 SearchRequest搜索请求
        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices(indexName);//指定要查询的索引

        //2.创建 SearchSourceBuilder条件构造。builder模式这里就先不简写了
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 构建查询
        // size(0) :不需要 查询的结果 , 只需要分组的结果
        searchSourceBuilder.size(0) ;
        searchSourceBuilder.query(QueryBuilders.matchAllQuery()) ;
//        // 构建Agg
//        TermsAggregationBuilder eventTypeAgg = AggregationBuilders.terms("group_by_eventType").field("eventType.keyword");
//        // agg结合进 searchSourceBuilder
//        searchSourceBuilder.aggregation(eventTypeAgg);

        // group  --1
//        TermsAggregationBuilder oneBuilder = AggregationBuilders.terms("one").field("eventType.keyword");
//        TermsAggregationBuilder twoBuilder = AggregationBuilders.terms("two").field("regionCode.keyword");
//        oneBuilder.subAggregation(twoBuilder);
//        searchSourceBuilder.aggregation(oneBuilder);

        // group  --2这种方式查出来的数据更扁平化,容易被接受
        // script{
        //"key": "cem.hygiene_health1-split-root00000000",
        //"doc_count": 5
//        Script script = new Script(ScriptType.INLINE, "groovy",
//                "doc['flowCode.keyword'].value+'-split-'+doc['stepExecuteId'].value", new HashMap<>());
//        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("result").script(script);



        // 设置GeoDistanceQueryBuilder
        //          "location":{ --字段
        //            "type": "geo_point"
        //          }

            //        "location":{ --字段
            //            "lat": "39.6"
            //            "lon": "118"
            //          }
//        GeoDistanceQueryBuilder geoDistanceQueryBuilder = QueryBuilders
//                .geoDistanceQuery("location")
//                .point(centerLat, centerLon) //原点
//                .distance(distance, DistanceUnit.KILOMETERS); // 设置半径和单位
//
//        searchSourceBuilder.query(geoDistanceQueryBuilder);
//
//        // 可以添加排序,按距离排序
//        searchSourceBuilder.sort(new GeoDistanceSortBuilder("location", centerLat, centerLon)
//                .unit(DistanceUnit.KILOMETERS)
//                .order(SortOrder.ASC));




        //3.将 SearchSourceBuilder 添加到 SearchRequest中
        searchRequest.source(searchSourceBuilder);
        List<EventInfo> data = new ArrayList<>();

        //4.执行查询  ---以上条件决定了返回的值,需要修改
        try {
            SearchResponse searchResp = client.search(searchRequest, options);
            //3.处理返回结果
            Aggregations aggregations = searchResp.getAggregations();

            //他是按照Terms 进行聚合的 所以取出来也需要 Terms 获取运行结果
            Terms colorAggRes = aggregations.get("group_by_eventType");
            List<? extends Terms.Bucket> colorBuckets = colorAggRes.getBuckets();

            // 遍历结果
            for (Terms.Bucket colorBucket : colorBuckets) {
                System.out.println("key: " + colorBucket.getKeyAsString());
                System.out.println("docCount: " + colorBucket.getDocCount());
                System.out.println("=================");

            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return data;
    }

 17、数据关联(数据库链表)---Elasticsearch 处理表关联关系是比较复杂的,此处总结一下 对象类型(宽表)、嵌套类型、父子关联关系。

  对象类型(宽表):其实就是将关联信息放到主表里,索引建立语句如下:

{
    "mappings": {
        "properties": {
            "picture": {
                "type": "text"
            },
            "time": {
                "type": "date"
            },
            "color": {
                "properties": {
                    "red": {
                        "type": "text"
                    },
                    "green": {
                        "type": "text"
                    },
                    "yellow": {
                        "type": "keyword"
                    }
                }
            }
        }
    }
}

  这样得形式就使得一个索引的宽度非常大,若关联的字段过多,降低了可读性。

  包含数组对象的类型。

{
    "mappings": {
        "properties": {
            "color": {
                "properties": {
                    "first_color": {
                        "type": "keyword"
                    },
                    "last_color": {
                        "type": "keyword"
                    }
                }
            },
            "picture": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            }
        }
    }
}

  存储时,内部对象的边界并没有考虑在内,JSON格式被处理成扁平式键值对的结构。当对多个字段进行查询时,导致出现不满足条件的搜索结果。

  嵌套类型(Nested Object):

  Nested 是 ElasticSearch 处理关联关系时的一种范式化设计的数据模型,在索引时他们被存放到两个不同Lucene文档中,在查询的时候 join出来,将根父文档带出来。

    • 允许嵌套对象中的数据被独⽴索引
    • 分成两个文档去存储,类似数据库的分表存放
    • 关键词 “type”: “nested”
{
    "mappings": {
        "properties": {
            "actors": {
                "type": "nested",
                "properties": {
                    "first_name": {
                        "type": "keyword"
                    },
                    "last_name": {
                        "type": "keyword"
                    }
                }
            },
            "title": {
                "type": "text",
                "fields": {
                    "keyword": {
                        "type": "keyword",
                        "ignore_above": 256
                    }
                }
            }
        }
    }
}

  nested 有个不好用的缺点就是,我们对嵌套文档进行操作的时候,本来是打算想要更新其中一个字段的数据,但是他会进行覆盖处理,对整个文档进行更新。

  以上两种方式不是很推荐,重点关注以下 的父子索引。

  父子关联关系

  要想使用父子文档,需要我们在设置mapping的时候将数据类型设置成为 join。如下是创建索引语句,索引名称自定义即可,例如一个人(People)分别拥有多辆车(Car)和多套房(House) ,索引名称:people_index

put /people_index
{ "mappings": { "properties": { "join_relation": { ---自定义该属性名称,用于标记父子关系 "type": "join", "relations": { "people": ["car","house"],
      "house":"room" ---可定义多层嵌套关系 } }, "intro": { "type": "text" }, "name": { "type": "keyword" } } } }

  这个文档mapping,语义是join_relation为父子文档类型的,并且如果 join_relation 的值为 people,那么为父文档,如果 child_relation 的值为 car、house,那么为子文档,这个值是随你自己自定义的。接下来看例子:

  先索引父文档 :

{
  "intro":"This is a good man",
  "name":"Tom",
    "join_relation":{
    "name":"people"
  }
}

  RestHighLevelClient实现:

public void addParent( RestHighLevelClient client,String indexName) {
        //indexName ==event_index
        String id = UUID.randomUUID().toString();
        People people = new People();
        people.setIntro("This is a good man");
        people.setName("Tom");
        JoinRelation joinField = new JoinRelation();
        joinField.setName("people");
        people.setJoin_relation(joinField);

        String source = JSONObject.toJSONString(people);
        IndexRequest indexRequest = new IndexRequest(indexName).id(id).source(source, XContentType.JSON)
                .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);

        try {
            IndexResponse out = client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
        }

    }

  这里的 join_relation 是我们在之前mapping中定义的父子文档之间的关联唯一标识字段,这里因为是父文档,所以这里需要将 join_relation 的 name 字段设置成为people。

  索引子文档:在索引子文档的时候,需要显示的指定子文档id,并且还需要指定父文档的id,告诉ElasticSearch 你这个文档的父文档是谁。

  • 指定子文档id
  • 指定父文档id,通过关键字 routing
  • 关联关系唯一标识设置为mapping 中子文档的标识
people_index/_doc?routing=parentId
{
  "brand": "aodi",
  "color": "red",
  "join_relation": {
    "name": "car",
    "parent": "iWCdeo4BvcUvbZKKjvHF"
  }
}

  同样的方法添加 house、room相关记录。RestHighLevelClient实现:

public void addCar( RestHighLevelClient client,String indexName) {
        //indexName ==event_index
        String id = UUID.randomUUID().toString();
        Car car = new Car();
        car.setBrand("aodi");
        car.setColor("red");
        JoinRelation joinField = new JoinRelation();
        joinField.setName("car");
        car.setJoin_relation(joinField);

        String source = JSONObject.toJSONString(car);
        IndexRequest indexRequest = new IndexRequest(indexName).id(id).source(source, XContentType.JSON)
                .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);

        try {
            IndexResponse out = client.index(indexRequest, RequestOptions.DEFAULT);
        } catch (IOException e) {
        }

    }

  查询父子文档

POST people_index/_search
{
  "query": {
    "match_all": {}
  }
}

  Parent Id 查询

  Parent Id 查询是ElasticSearch 提供的一种专门查询父子文档的API,这种查询是为了满足类似将一个父文档下的所有的子文档全部查询出来的场景。语法:POST 索引名/_search + 查询体(关键词:“parent_id”)

  • type 子文档的唯一标识
  • id 父文档的id
{
  "query": {
    "parent_id": {
      "type": "car",
      "id": "iGCceY4BvcUvbZKKT_ER"
    }
  }
}

  这个语法就是查询  iGCceY4BvcUvbZKKT_ER 下的所有子文档,注意这里不会将父文档带出来。

  RestHighLevelClient实现:

public void testParentId(RestHighLevelClient client, String indexName) throws IOException {
        SearchRequest search= new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        ParentIdQueryBuilder build = JoinQueryBuilders.parentId("parent","iGCceY4BvcUvbZKKT_ER");
        searchSourceBuilder.query(build);
        search.source(searchSourceBuilder);
        SearchResponse response=client.search(search, RequestOptions.DEFAULT);
        response.getHits().forEach(hi ->{
            System.out.println(hi.getSourceAsString());
        });

    }

  Has Child 查询

  Has Child 查询是根据查询条件将符合条件的子文档查询出来然后再根据父文档id将所有的父文档查询出来。语法:POST 索引名/_search + 查询体(关键词:“has_child”)

  • type 子文档的唯一标识
  • query 查询语法,这里的查询条件是针对子文档的字段的。
{
  "query": {
    "has_child": {
      "type": "car",
      "query": {
        "match": {
          "color": "red"
        }
      }
    }
  }
}

  RestHighLevelClient实现:

public void testHasChild(RestHighLevelClient client, String indexName) throws IOException {
        SearchRequest search= new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        HasChildQueryBuilder build = JoinQueryBuilders.hasChildQuery(
                "car",QueryBuilders.matchQuery("color", "red"), ScoreMode.None);
        searchSourceBuilder.query(build);
        search.source(searchSourceBuilder);
        SearchResponse response=client.search(search, RequestOptions.DEFAULT);
        response.getHits().forEach(hi ->{
            System.out.println(hi.getSourceAsString());
        });

    }

  Has Parent 查询

  Has Parent 查询正好与子查询相反,会将父文档的查询条件找出父文档并且再去找出所有的子文档。语法:POST 索引名/_search + 查询体(关键词:“has_parent”)

  • parent_type 子文档的唯一标识
  • query 查询语法,这里的查询条件是针对父文档的字段的。
{
  "query": {
    "has_parent": {
      "parent_type": "people",
      "query": {
        "match": {
          "name": "Tom"
        }
      }
    }
  }
}

  RestHighLevelClient实现:

public void testHasParent(RestHighLevelClient client, String indexName) throws IOException {
        SearchRequest search= new SearchRequest(indexName);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        HasParentQueryBuilder build = JoinQueryBuilders.hasParentQuery(
                "people",QueryBuilders.matchQuery("name", "Tome"), false);
        searchSourceBuilder.query(build);
        search.source(searchSourceBuilder);
        SearchResponse response=client.search(search, RequestOptions.DEFAULT);
        response.getHits().forEach(hi ->{
            System.out.println(hi.getSourceAsString());
        });

    }

   聚合查询(关联查询)---根据父文档条件+子文档条件,返回一父多子多孙的所有文档,然后程序取进行组装。

public static void searchParentChildAggregation(RestHighLevelClient client, String indexName) throws IOException {
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
        //联car表
        queryBuilder.must(JoinQueryBuilders
                .hasChildQuery("car", QueryBuilders.matchQuery("color", "red"), ScoreMode.None)
                .innerHit(new InnerHitBuilder("car")));
        //联house表
//        queryBuilder.should(JoinQueryBuilders
//                .hasChildQuery("house", QueryBuilders.matchAllQuery(), ScoreMode.None)
//                .innerHit(new InnerHitBuilder("house")));

        queryBuilder.should(JoinQueryBuilders
                .hasChildQuery("house", JoinQueryBuilders
                        //联room表
                        .hasChildQuery("room", QueryBuilders.matchAllQuery(), ScoreMode.None)
                        .innerHit(new InnerHitBuilder("room")), ScoreMode.None)
                .innerHit(new InnerHitBuilder("house")));

        //主表 的条件
        queryBuilder.must(QueryBuilders.termQuery("name","Tom"));

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.query(queryBuilder);

        SearchRequest searchRequest = new SearchRequest(indexName);
        searchRequest.source(sourceBuilder);

        SearchResponse searchResponse = client.search(searchRequest,options);

        for (SearchHit hit : searchResponse.getHits().getHits()) {
            System.out.println(hit.getSourceAsString());
        }
    }

 

posted @ 2021-07-19 16:57  吴振照  阅读(1220)  评论(0编辑  收藏  举报