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());
        }
    }

 

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