ElasticSearch 教程(七) RestHighLevelClient 通用操作

項目中使用到ElasticSearch作爲搜索引擎。而ES的環境搭建自然是十分簡單,且本身就適應於分佈式環境,因此這塊就不多贅述。而其本身特性和查詢語句這篇博文不會介紹,如果有機會會深入介紹。

​ 所以這篇博文主要還是介紹Java客戶端中如何使用查詢搜索引擎中的數據。而使用的Java客戶端是官方新推出的RestHighLevelClient,使用Http連接查詢結果。但是網上相關資料較少,只有官網的api介紹。所以本文以一個小demo介紹RestHighLevelClient的使用。
 

項目依賴:

 

dependencies {

    // https://mvnrepository.com/artifact/org.elasticsearch.client/elasticsearch-rest-high-level-client
    compile group: 'org.elasticsearch.client',
    name: 'elasticsearch-rest-high-level-client', 
    version: '5.6.2'
}

一般配置Java Client

// Java客戶端生成工廠
public class ESClientFactory {

    private static final String HOST = "127.0.0.1";
    private static final int PORT = 9200;
    private static final String SCHEMA = "http";
    private static final int CONNECT_TIME_OUT = 1000;
    private static final int SOCKET_TIME_OUT = 30000;
    private static final int CONNECTION_REQUEST_TIME_OUT = 500;

    private static final int MAX_CONNECT_NUM = 100;
    private static final int MAX_CONNECT_PER_ROUTE = 100;

    private static HttpHost HTTP_HOST = new HttpHost(HOST,PORT,SCHEMA);
    private static boolean uniqueConnectTimeConfig = false;
    private static boolean uniqueConnectNumConfig = true;
    private static RestClientBuilder builder;
    private static RestClient restClient;
    private static RestHighLevelClient restHighLevelClient;

    static {
        init();
    }

    public static void init(){
        builder = RestClient.builder(HTTP_HOST);
        if(uniqueConnectTimeConfig){
            setConnectTimeOutConfig();
        }
        if(uniqueConnectNumConfig){
            setMutiConnectConfig();
        }
        restClient = builder.build();
        restHighLevelClient = new RestHighLevelClient(restClient);
    }

    // 主要關於異步httpclient的連接延時配置
    public static void setConnectTimeOutConfig(){
        builder.setRequestConfigCallback(new RequestConfigCallback() {

            @Override
            public Builder customizeRequestConfig(Builder requestConfigBuilder) {
                requestConfigBuilder.setConnectTimeout(CONNECT_TIME_OUT);
                requestConfigBuilder.setSocketTimeout(SOCKET_TIME_OUT);
                requestConfigBuilder.setConnectionRequestTimeout(CONNECTION_REQUEST_TIME_OUT);
                return requestConfigBuilder;
            }
        });
    }
    // 主要關於異步httpclient的連接數配置
    public static void setMutiConnectConfig(){
        builder.setHttpClientConfigCallback(new HttpClientConfigCallback() {

            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                httpClientBuilder.setMaxConnTotal(MAX_CONNECT_NUM);
                httpClientBuilder.setMaxConnPerRoute(MAX_CONNECT_PER_ROUTE);
                return httpClientBuilder;
            }
        });
    }

    public static RestClient getClient(){
        return restClient;
    }

    public static RestHighLevelClient getHighLevelClient(){
        return restHighLevelClient;
    }

    public static void close() {
        if (restClient != null) {
            try {
                restClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


}

 

 
public class ESClientSpringFactory {

    public static int CONNECT_TIMEOUT_MILLIS = 1000;
    public static int SOCKET_TIMEOUT_MILLIS = 30000;
    public static int CONNECTION_REQUEST_TIMEOUT_MILLIS = 500;
    public static int MAX_CONN_PER_ROUTE = 10;
    public static int MAX_CONN_TOTAL = 30;

    private static HttpHost HTTP_HOST;
    private RestClientBuilder builder;
    private RestClient restClient;
    private RestHighLevelClient restHighLevelClient;

    private static ESClientSpringFactory esClientSpringFactory = new ESClientSpringFactory();

    private ESClientSpringFactory(){}

    public static ESClientSpringFactory build(HttpHost httpHost,
            Integer maxConnectNum, Integer maxConnectPerRoute){
        HTTP_HOST = httpHost;
        MAX_CONN_TOTAL = maxConnectNum;
        MAX_CONN_PER_ROUTE = maxConnectPerRoute;
        return  esClientSpringFactory;
    }

    public static ESClientSpringFactory build(HttpHost httpHost,Integer connectTimeOut, Integer socketTimeOut,
            Integer connectionRequestTime,Integer maxConnectNum, Integer maxConnectPerRoute){
        HTTP_HOST = httpHost;
        CONNECT_TIMEOUT_MILLIS = connectTimeOut;
        SOCKET_TIMEOUT_MILLIS = socketTimeOut;
        CONNECTION_REQUEST_TIMEOUT_MILLIS = connectionRequestTime;
        MAX_CONN_TOTAL = maxConnectNum;
        MAX_CONN_PER_ROUTE = maxConnectPerRoute;
        return  esClientSpringFactory;
    }


    public void init(){
        builder = RestClient.builder(HTTP_HOST);
        setConnectTimeOutConfig();
        setMutiConnectConfig();
        restClient = builder.build();
        restHighLevelClient = new RestHighLevelClient(restClient);
        System.out.println("init factory");
    }
    // 配置連接時間延時
    public void setConnectTimeOutConfig(){
        builder.setRequestConfigCallback(new RequestConfigCallback() {

            @Override
            public Builder customizeRequestConfig(Builder requestConfigBuilder) {
                requestConfigBuilder.setConnectTimeout(CONNECT_TIMEOUT_MILLIS);
                requestConfigBuilder.setSocketTimeout(SOCKET_TIMEOUT_MILLIS);
                requestConfigBuilder.setConnectionRequestTimeout(CONNECTION_REQUEST_TIMEOUT_MILLIS);
                return requestConfigBuilder;
            }
        });
    }
    // 使用異步httpclient時設置併發連接數
    public void setMutiConnectConfig(){
        builder.setHttpClientConfigCallback(new HttpClientConfigCallback() {

            @Override
            public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
                httpClientBuilder.setMaxConnTotal(MAX_CONN_TOTAL);
                httpClientBuilder.setMaxConnPerRoute(MAX_CONN_PER_ROUTE);
                return httpClientBuilder;
            }
        });
    }

    public RestClient getClient(){
        return restClient;
    }

    public RestHighLevelClient getRhlClient(){
        return restHighLevelClient;
    }

    public void close() {
        if (restClient != null) {
            try {
                restClient.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        System.out.println("close client");
    }


}

兩種配置方法從本質上都是對client進行配置,且達到相同目的。

​ 由於Client配置爲單例模式,在Spring中的生命週期隨着容器開始結束而開始結束。在定義bean創建和銷燬方法後會自動關閉連接。

​ 但是使用一般Java配置時,需要手動關閉。如果在web項目中,可以使用監聽器,隨着項目的生命週期手動調用開啓關閉。

客戶端演示
接下來就是最簡單的幾個demo,校驗這種客戶端的有效性,同時也爲大家試驗如何使用這種Java客戶端:

數據準備
首先準備要操作的數據:創建一個index爲demo,type爲demo的新聞索引。

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

/PUT http://{{host}}:{{port}}/demo

{

    "mappings":{

        "demo":{

            "properties":{

                "title":{

                    "type":"text"

                },

                "tag":{

                    "type":"keyword"

                },

                "publishTime":{

                    "type":"date"

                }

            }

        }

    }

}

  

插入數據
API格式

/POST http://{{host}}:{{port}}/demo/demo/
{
"title":"中國產小型無人機的“對手”來了,俄微型攔截導彈便宜量又多",
"tag":"軍事",
"publishTime":"2018-01-24T23:59:30Z"
}

Java Client

 

public class News {

    private String title;
    private String tag;
    private String publishTime;


    public News() {
        super();
    }
    public News(String title, String tag, String publishTime) {
        super();
        this.title = title;
        this.tag = tag;
        this.publishTime = publishTime;
    }
    public String getTitle() {
        return title;
    }
    public void setTitle(String title) {
        this.title = title;
    }
    public String getTag() {
        return tag;
    }
    public void setTag(String tag) {
        this.tag = tag;
    }
    public String getPublishTime() {
        return publishTime;
    }
    public void setPublishTime(String publishTime) {
        this.publishTime = publishTime;
    }
}

 

 

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:spring/spring-context.xml")
public class FreeClientTest {

    private String index;

    private String type;

    @Autowired
    private RestHighLevelClient rhlClient;

    @Before
    public void prepare() {
        index = "demo";
        type = "demo";
    }

    @Test
    public void addTest() {
        IndexRequest indexRequest = new IndexRequest(index, type);
        News news = new News();
        news.setTitle("中國產小型無人機的“對手”來了,俄微型攔截導彈便宜量又多");
        news.setTag("軍事");
        news.setPublishTime("2018-01-24T23:59:30Z");
        String source = JsonUtil.toString(news);
        indexRequest.source(source, XContentType.JSON);
        try {
            rhlClient.index(indexRequest);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

 

兩種方式均可插入數據。如果有大量數據這樣插入未免效率太低,接下來看一看批量插入數據。

批量插入數據

API格式

1

2

3

4

5

6

7

8

9

/POST http://{{host}}:{{port}}/_bulk

{"index":{"_index":"demo","_type":"demo"}}

{"title":"中印邊防軍於拉達克舉行會晤 強調維護邊境和平","tag":"軍事","publishTime":"2018-01-27T08:34:00Z"}

{"index":{"_index":"demo","_type":"demo"}}

{"title":"費德勒收鄭泫退賽禮 進決賽戰西里奇","tag":"體育","publishTime":"2018-01-26T14:34:00Z"}

{"index":{"_index":"demo","_type":"demo"}}

{"title":"歐文否認拿動手術威脅騎士 興奮全明星聯手詹皇","tag":"體育","publishTime":"2018-01-26T08:34:00Z"}

{"index":{"_index":"demo","_type":"demo"}}

{"title":"皇馬官方通告拉莫斯伊斯科傷情 將缺陣西甲關鍵戰","tag":"體育","publishTime":"2018-01-26T20:34:00Z"}

  Java Client

@RunWith(SpringJUnit4ClassRunner.class)

@ContextConfiguration("classpath:spring/spring-context.xml")
public class FreeClientTest {

    private String index;

    private String type;

    @Autowired
    private RestHighLevelClient rhlClient;

    @Before
    public void prepare() {
        index = "demo";
        type = "demo";
    }


    @Test
    public void batchAddTest() {
        BulkRequest bulkRequest = new BulkRequest();
        List<IndexRequest> requests = generateRequests();
        for (IndexRequest indexRequest : requests) {
            bulkRequest.add(indexRequest);
        }
        try {
            rhlClient.bulk(bulkRequest);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

    public List<IndexRequest> generateRequests(){
        List<IndexRequest> requests = new ArrayList<>();
        requests.add(generateNewsRequest("中印邊防軍於拉達克舉行會晤 強調維護邊境和平", "軍事", "2018-01-27T08:34:00Z"));
        requests.add(generateNewsRequest("費德勒收鄭泫退賽禮 進決賽戰西里奇", "體育", "2018-01-26T14:34:00Z"));
        requests.add(generateNewsRequest("歐文否認拿動手術威脅騎士 興奮全明星聯手詹皇", "體育", "2018-01-26T08:34:00Z"));
        requests.add(generateNewsRequest("皇馬官方通告拉莫斯伊斯科傷情 將缺陣西甲關鍵戰", "體育", "2018-01-26T20:34:00Z"));
        return requests;
    }

    public IndexRequest generateNewsRequest(String title, String tag, String publishTime){
        IndexRequest indexRequest = new IndexRequest(index, type);
        News news = new News();
        news.setTitle(title);
        news.setTag(tag);
        news.setPublishTime(publishTime);
        String source = JsonUtil.toString(news);
        indexRequest.source(source, XContentType.JSON);
        return indexRequest;
    }

}

 

無論通過哪種方式,現在ES中已插入五條文檔數據。那麼現在就可以通過多種多樣的查詢方式獲得需要的數據了。

查詢數據

查詢目標:2018年1月26日早八點到晚八點關於費德勒的前十條體育新聞的標題

API 格式

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

/POST http://{{host}}:{{port}}/demo/demo/_search

{

    "from":"0",

    "size":"10",

    "_source":["title"],

    "query":{

        "bool":{

            "must":{

                "match":{

                    "title":"費德勒"

                }

            },

            "must":{

                "term":{"tag":"體育"}

            },

            "must":{

                "range":{

                    "publishTime":{

                        "gte":"2018-01-26T08:00:00Z",

                        "lte":"2018-01-26T20:00:00Z"

                    }

                }

            }

        }

    }

 

}

  Java Client

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-context.xml")
public class FreeClientTest {

    private String index;

    private String type;

    @Autowired
    private RestHighLevelClient rhlClient;

    @Before
    public void prepare() {
        index = "demo";
        type = "demo";
    }


    @Test
    public void queryTest(){
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        sourceBuilder.from(0);
        sourceBuilder.size(10);
        sourceBuilder.fetchSource(new String[]{"title"}, new String[]{});
        MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("title", "費德勒");
        TermQueryBuilder termQueryBuilder = QueryBuilders.termQuery("tag", "體育");
        RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime");
        rangeQueryBuilder.gte("2018-01-26T08:00:00Z");
        rangeQueryBuilder.lte("2018-01-26T20:00:00Z");
        BoolQueryBuilder boolBuilder = QueryBuilders.boolQuery();
        boolBuilder.must(matchQueryBuilder);
        boolBuilder.must(termQueryBuilder);
        boolBuilder.must(rangeQueryBuilder);
        sourceBuilder.query(boolBuilder);
        SearchRequest searchRequest = new SearchRequest(index);
        searchRequest.types(type);
        searchRequest.source(sourceBuilder);
        try {
            SearchResponse response = rhlClient.search(searchRequest);
            System.out.println(response);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

 

更新文檔

如果插入了錯誤的數據,想要更改或者在文檔中新增新的數據,那麼就需要更新文檔了。

演示 將費德勒的新聞的tag更改爲網球類型:

API格式

1

2

3

4

5

6

/POST http://{{host}}:{{port}}/demo/demo/AWE1fnSx00f4t28WJ4D6/_update

{

    "doc":{

        "tag":"網球"

    }

}

  Java Client

 

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring/spring-context.xml")
public class FreeClientTest {

    private String index;

    private String type;

    private String id;

    @Autowired
    private RestHighLevelClient rhlClient;

    @Before
    public void prepare() {
        index = "demo";
        type = "demo";
        id = "AWE1fnSx00f4t28WJ4D6";
    }


    @Test
    public void updateTest(){
        UpdateRequest updateRequest = new UpdateRequest(index, type, id);
        Map<String, String> map = new HashMap<>();
        map.put("tag", "網球");
        updateRequest.doc(map);
        try {
            rhlClient.update(updateRequest);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

 

以上介紹了最簡單的doc文檔更改

ID方式刪除

API格式

/DELETE http://{{host}}:{{port}}/delete_demo/demo/AWExGSdW00f4t28WAPen
  • Java 客戶端

 

public class ElkDaoTest extends BaseTest{

    @Autowired
    private RestHighLevelClient rhlClient;

    private String index;

    private String type;

    private String id;

    @Before
    public void prepare(){
        index = "delete_demo";
        type = "demo";
        id = "AWExGSdW00f4t28WAPeo";
    }

    @Test
    public void delete(){
        DeleteRequest deleteRequest = new DeleteRequest(index,type,id);
        DeleteResponse response = null;
        try {
            response = rhlClient.delete(deleteRequest);
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println(response);
    }
}

 

同樣刪除成功。

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