Elasticsearch 入門實戰(9)--Java API Client 使用二

且行且码發表於2024-07-28

本文繼續上文(Elasticsearch 入門實戰(5)--Java API Client 使用一(Index,Document,Ingest,SQL APIs))介紹 Java API Client,相關的環境及軟體資訊如下:CentOS 7.6.1810、Java 1.8.0_341(客戶端用)、Elasticsearch 8.13.4、elasticsearch-java 8.13.4。

1、Search APIs

1.1、Count API(查詢文件數量)

/**
 * 查詢文件數量
 */
@Test
public void count() throws IOException {
    //查詢該索引的所有文件數量
    CountResponse response = client.count(builder -> builder.index(INDEX_NAME));
    log.info("response={}", response);

    //透過 Lucene 查詢語法指定條件;8.13.4會報錯”contains unrecognized parameter: [q]“,因為 API 提交了請求 "{}",應該時不需要請求體
    //response = client.count(builder -> builder.index(INDEX_NAME).q("name:杜甫"));
    log.info("response={}", response);

    //透過 "Query DSL" 指定條件
    response = client.count(builder -> builder
            .index(INDEX_NAME)
            .query(queryBuilder -> queryBuilder
                    .term(termQueryBuilder -> termQueryBuilder
                            .field("name").value("杜甫")
                    )
            )
    );
    log.info("response={}", response);
}

1.2、Search API(查詢文件)

1.2.1、query

1.2.1.1、term/terms 查詢

/**
 * term/terms 查詢,對輸入內容不做分詞處理
 */
@Test
public void searchTerm() throws IOException {
    SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .term(termQueryBuilder -> termQueryBuilder
                                    .field("name").value("李白")))
                    .sort(sortOptionsBuilder -> sortOptionsBuilder
                            .field(fieldSortBuilder -> fieldSortBuilder
                                    .field("age").order(SortOrder.Asc)))
                    .source(sourceConfigBuilder -> sourceConfigBuilder
                            .filter(sourceFilterBuilder -> sourceFilterBuilder
                                    .includes("age", "name")))
                    .from(0)
                    .size(10)
            , Map.class);
    log.info("response={}", response);

    List<FieldValue> words = new ArrayList<>();
    words.add(new FieldValue.Builder().stringValue("李白").build());
    words.add(new FieldValue.Builder().stringValue("杜甫").build());
    SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .terms(termsQueryBuilder -> termsQueryBuilder
                                    .field("name").terms(termsQueryFieldBuilder -> termsQueryFieldBuilder.value(words))))
                    .source(sourceConfigBuilder -> sourceConfigBuilder
                            .filter(sourceFilterBuilder -> sourceFilterBuilder
                                    .excludes("about")))
                    .from(0)
                    .size(10)
            , Poet.class);
    log.info("response2={}", response2);
}

1.2.1.2、range 查詢

/**
 * range 查詢,範圍查詢
 */
@Test
public void searchRange() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .range(rangeQueryBuilder -> rangeQueryBuilder
                                    .field("age").gte(JsonData.of("20")).lt(JsonData.of("40"))))
            , Poet.class);
    log.info("response={}", response);
}

1.2.1.3、exists 查詢

/**
 * exists 查詢,查詢對應欄位不為空的資料
 */
@Test
public void exists() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME).query(builder -> builder.exists(builder1 -> builder1.field("poems")))
            , Poet.class);
    log.info("response={}", response);
}

1.2.1.4、match 相關查詢

A、match

/**
 * match查詢,對輸入內容先分詞再查詢
 */
@Test
public void searchMatch() throws IOException {
    SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .match(matchQueryBuilder -> matchQueryBuilder
                                    .field("success").query("思想")))
            , Map.class);
    log.info("response={}", response);
}

B、multi_match

/**
 * multi_match 查詢,多個欄位進行匹配。
 */
@Test
public void searchMultiMatch() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .multiMatch(multiMatchQueryBuilder -> multiMatchQueryBuilder
                                    .fields("about", "success").query("思想")))
            , Poet.class);
    log.info("response={}", response);
}

C、match_phrase

/**
 * match_phrase 查詢,類似 match,先把查詢字串分詞生成一個詞列表,查詢包含所有這些詞且這些詞在文件中出現次序一致的資料。
 */
@Test
public void searchMatchPhrase() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .matchPhrase(matchPhraseQueryBuilder -> matchPhraseQueryBuilder
                                    .field("success").query("偉大作家")))
            , Poet.class);
    log.info("response={}", response);
}

D、match_all

/**
 * match_all 查詢,查詢所有文件
 */
@Test
public void searchMatchAll() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .matchAll(matchAllQueryBuilder -> matchAllQueryBuilder))
            , Poet.class);
    log.info("response={}", response);

    //不加請求體,也是一樣的效果,查詢所有文件。
    response = client.search(searchRequestBuilder -> searchRequestBuilder.index(INDEX_NAME), Poet.class);
    log.info("response={}", response);
}

E、match_none

/**
 * match_none 查詢,與 match_all 相反,返回 0 個文件。
 */
@Test
public void searchMatchNone() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .matchNone(matchAllQueryBuilder -> matchAllQueryBuilder))
            , Poet.class);
    log.info("response={}", response);
}

1.2.1.5、query_string 查詢

/**
 * query_string 查詢,可以同時實現多種查詢
 */
@Test
public void searchQueryString() throws IOException {
    //類似 match
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .defaultField("success").query("古典文學")))
            , Poet.class);
    log.info("response={}", response);

    //類似 mulit_match
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .fields("about", "success").query("古典文學")))
            , Poet.class);
    log.info("response={}", response);

    //類似 match_phrase
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .defaultField("success").query("\"文學作家\"")))
            , Poet.class);
    log.info(response.toString());

    //帶運算子查詢,運算子兩邊的詞不再分詞
    //查詢同時包含 ”文學“ 和 ”偉大“ 的文件
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .fields("success").query("文學 AND 偉大")))
            , Poet.class);
    log.info("response={}", response);

    //等同上一個查詢
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .fields("success").query("文學 偉大").defaultOperator(Operator.And)))
            , Poet.class);
    log.info("response={}", response);

    //查詢 name 或 success 欄位包含"文學"和"偉大"這兩個單詞,或者包含"李白"這個單詞的文件。
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                    .fields("name","success").query("(文學 AND 偉大) OR 高度")))
            , Poet.class);
    log.info("response={}", response);
}

1.2.1.6、simple_query_string 查詢

/**
 * simple_query_string 查詢,類似 query_string,主要區別如下:
 * 1、不支援 AND OR NOT,會當做字元處理;使用 + 代替 AND,| 代替 OR,- 代替 NOT
 * 2、會忽略錯誤的語法
 */
@Test
public void searchSimpleQueryString() throws IOException {
    //查詢同時包含 ”文學“ 和 ”偉大“ 的文件
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .simpleQueryString(simpleQueryStringQueryBuilder -> simpleQueryStringQueryBuilder
                                    .fields("success").query("文學 + 偉大")))
            , Poet.class);
    log.info("response={}", response);
}

3.4.7、fuzzy 查詢

/**
 * 模糊查詢
 */
@Test
public void searchFuzzy() throws IOException {
    //全文查詢時使用模糊引數,先分詞再計算模糊選項。
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .match(matchQueryBuilder -> matchQueryBuilder
                                    .field("success").query("思考").fuzziness("1")))
            , Poet.class);
    log.info(response.toString());

    //使用 fuzzy query,對輸入不分詞,直接計算模糊選項。
    SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .fuzzy(fuzzyQueryBuilder ->  fuzzyQueryBuilder
                                    .field("success").fuzziness("1").value("理想")))
            , Poet.class);
    log.info(response2.toString());
}

1.2.1.8、wildcard 查詢

/**
 * wildcard 查詢,類似 SQL 語句中的 like;? 匹配一個字元,* 匹配多個字元
 */
@Test
public void searchWildcard() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
            .index(INDEX_NAME)
            .query(queryBuilder -> queryBuilder.wildcard(wildcardQueryBuilder -> wildcardQueryBuilder
                    .field("name")
                    .wildcard("李*")))
            , Poet.class);
    log.info(response.toString());
}

1.2.1.9、bool 查詢

/**
 * bool 查詢,組合查詢
 */
@Test
public void searchBool() throws IOException {
    //查詢 success 包含 “思想” 且 age 在 [20-40] 之間的文件
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .bool(boolQueryBuilder -> boolQueryBuilder
                                    .must(queryBuilder2 -> queryBuilder2
                                            .match(matchQueryBuilder -> matchQueryBuilder
                                                    .field("success").query("思想"))
                                    )
                                    .must(queryBuilder2 -> queryBuilder2
                                            .range(rangeQueryBuilder -> rangeQueryBuilder
                                                    .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
                                    )
                            )
                    )
            , Poet.class);
    log.info(response.toString());

    //過濾出 success 包含 “思想” 且 age 在 [20-40] 之間的文件,不計算得分
    SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .bool(boolQueryBuilder -> boolQueryBuilder
                                    .filter(queryBuilder2 -> queryBuilder2
                                            .match(matchQueryBuilder -> matchQueryBuilder
                                                    .field("success").query("思想"))
                                    )
                                    .filter(queryBuilder2 -> queryBuilder2
                                            .range(rangeQueryBuilder -> rangeQueryBuilder
                                                    .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
                                    )
                            )
                    )
            , Poet.class);
    log.info(response2.toString());
}

1.2.2、aggs 查詢

/**
 * aggs 查詢,聚合查詢
 */
@Test
public void searchAggs() throws IOException {
    //求和,類似 select sum(age) from poet-index
    SearchResponse response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .aggregations("age_sum", aggregationBuilder -> aggregationBuilder
                            .sum(sumAggregationBuilder -> sumAggregationBuilder
                                    .field("age")))
            , Poet.class);
    log.info(response.toString());

    //類似 select count distinct(age) from poet-index
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .aggregations("age_count", aggregationBuilder -> aggregationBuilder
                            .cardinality(cardinalityAggregationBuilder -> cardinalityAggregationBuilder.field("age")))
            , Poet.class);
    log.info(response.toString());

    //數量、最大、最小、平均、求和
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .aggregations("age_stats", aggregationBuilder -> aggregationBuilder
                            .stats(statsAggregationBuilder -> statsAggregationBuilder
                                    .field("age")))
            , Poet.class);
    log.info(response.toString());

    //select name,count(*) from poet-index group by name
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
                            .terms(termsAggregationBuilder -> termsAggregationBuilder
                                    .field("name")))
            , Map.class);
    log.info(response.toString());

    //select name,age,count(*) from poet-index group by name,age
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
                            .terms(termsAggregationBuilder -> termsAggregationBuilder
                                    .field("name")
                            )
                            .aggregations("age_terms", aggregationBuilder2 -> aggregationBuilder2
                                    .terms(termsAggregationBuilder -> termsAggregationBuilder
                                            .field("age")
                                    ))
                    )
            , Poet.class);
    log.info(response.toString());

    //類似 select avg(age) from poet-index where name='李白'
    response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .bool(boolQueryBuilder -> boolQueryBuilder
                                    .filter(queryBuilder2 -> queryBuilder2
                                            .term(termQueryBuilder -> termQueryBuilder
                                                    .field("name").value("李白")))))
                    .aggregations("ave_age", aggregationBuilder -> aggregationBuilder
                            .avg(averageAggregationBuilder -> averageAggregationBuilder.field("age")))
            , Poet.class);
    log.info(response.toString());
}

1.2.3、suggest 查詢

/**
 * suggest 查詢,推薦查詢
 */
@Test
public void searchSuggest() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .suggest(suggesterBuilder -> suggesterBuilder
                            .suggesters("success_suggest", fieldSuggesterBuilder -> fieldSuggesterBuilder
                                    .text("思考")
                                    .term(termSuggesterBuilder -> termSuggesterBuilder
                                            .field("success")
                                            .suggestMode(SuggestMode.Always)
                                            .minWordLength(2)
                                    )
                            )
                    )
            , Poet.class);
    log.info(response.toString());
}

1.2.4、highlight

/**
 * 高亮顯示
 */
@Test
public void searchHighlight() throws IOException {
    SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                    .index(INDEX_NAME)
                    .query(queryBuilder -> queryBuilder
                            .match(matchQueryBuilder -> matchQueryBuilder
                                    .field("success").query("思想")))
                    .highlight(highlightBuilder -> highlightBuilder
                            .preTags("<span color='red'>")
                            .postTags("</span>")
                            .fields("success", highlightFieldBuilder -> highlightFieldBuilder))
            , Poet.class);
    log.info(response.toString());
}

2、完整程式碼

Elasticsearch 入門實戰(9)--Java API Client 使用二
package com.abc.demo.es;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.SuggestMode;
import co.elastic.clients.elasticsearch._types.query_dsl.Operator;
import co.elastic.clients.elasticsearch.core.*;
import co.elastic.clients.json.JsonData;
import co.elastic.clients.json.jackson.JacksonJsonpMapper;
import co.elastic.clients.transport.ElasticsearchTransport;
import co.elastic.clients.transport.rest_client.RestClientTransport;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
public class ElasticsearchJavaCase2 {
    private static final String INDEX_NAME = "poet-index";

    private ElasticsearchTransport transport;
    private ElasticsearchClient client;

    @Before
    public void before() {
        RestClient restClient = RestClient.builder(
                new HttpHost("10.49.196.10", 9200),
                new HttpHost("10.49.196.11", 9200),
                new HttpHost("10.49.196.12", 9200)).build();
        ObjectMapper objectMapper = new ObjectMapper();
        transport = new RestClientTransport(restClient, new JacksonJsonpMapper(objectMapper));
        client = new ElasticsearchClient(transport);
    }

    @After
    public void after() throws IOException {
        transport.close();
    }

    /**
     * 查詢文件數量
     */
    @Test
    public void count() throws IOException {
        //查詢該索引的所有文件數量
        CountResponse response = client.count(builder -> builder.index(INDEX_NAME));
        log.info("response={}", response);

        //透過 Lucene 查詢語法指定條件;8.13.4會報錯”contains unrecognized parameter: [q]“,因為 API 提交了請求 "{}",應該時不需要請求體
        //response = client.count(builder -> builder.index(INDEX_NAME).q("name:杜甫"));
        log.info("response={}", response);

        //透過 "Query DSL" 指定條件
        response = client.count(builder -> builder
                .index(INDEX_NAME)
                .query(queryBuilder -> queryBuilder
                        .term(termQueryBuilder -> termQueryBuilder
                                .field("name").value("杜甫")
                        )
                )
        );
        log.info("response={}", response);
    }

    /**
     * term/terms查詢,對輸入內容不做分詞處理
     */
    @Test
    public void searchTerm() throws IOException {
        SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .term(termQueryBuilder -> termQueryBuilder
                                        .field("name").value("李白")))
                        .sort(sortOptionsBuilder -> sortOptionsBuilder
                                .field(fieldSortBuilder -> fieldSortBuilder
                                        .field("age").order(SortOrder.Asc)))
                        .source(sourceConfigBuilder -> sourceConfigBuilder
                                .filter(sourceFilterBuilder -> sourceFilterBuilder
                                        .includes("age", "name")))
                        .from(0)
                        .size(10)
                , Map.class);
        log.info("response={}", response);

        List<FieldValue> words = new ArrayList<>();
        words.add(new FieldValue.Builder().stringValue("李白").build());
        words.add(new FieldValue.Builder().stringValue("杜甫").build());
        SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .terms(termsQueryBuilder -> termsQueryBuilder
                                        .field("name").terms(termsQueryFieldBuilder -> termsQueryFieldBuilder.value(words))))
                        .source(sourceConfigBuilder -> sourceConfigBuilder
                                .filter(sourceFilterBuilder -> sourceFilterBuilder
                                        .excludes("about")))
                        .from(0)
                        .size(10)
                , Poet.class);
        log.info("response2={}", response2);
    }

    /**
     * range(範圍)查詢
     */
    @Test
    public void searchRange() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .range(rangeQueryBuilder -> rangeQueryBuilder
                                        .field("age").gte(JsonData.of("20")).lt(JsonData.of("40"))))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * exists 查詢,查詢對應欄位不為空的資料
     */
    @Test
    public void exists() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME).query(builder -> builder.exists(builder1 -> builder1.field("poems")))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * match查詢,對輸入內容先分詞再查詢
     */
    @Test
    public void searchMatch() throws IOException {
        SearchResponse<Map> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .match(matchQueryBuilder -> matchQueryBuilder
                                        .field("success").query("思想")))
                , Map.class);
        log.info("response={}", response);
    }

    /**
     * multi_match 查詢,多個欄位進行匹配。
     */
    @Test
    public void searchMultiMatch() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .multiMatch(multiMatchQueryBuilder -> multiMatchQueryBuilder
                                        .fields("about", "success").query("思想")))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * match_phrase 查詢,類似 match,先把查詢字串分詞生成一個詞列表,查詢包含所有這些詞且這些詞在文件中出現次序一致的資料。
     */
    @Test
    public void searchMatchPhrase() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .matchPhrase(matchPhraseQueryBuilder -> matchPhraseQueryBuilder
                                        .field("success").query("偉大作家")))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * match_all 查詢,查詢所有文件
     */
    @Test
    public void searchMatchAll() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .matchAll(matchAllQueryBuilder -> matchAllQueryBuilder))
                , Poet.class);
        log.info("response={}", response);

        //不加請求體,也是一樣的效果,查詢所有文件。
        response = client.search(searchRequestBuilder -> searchRequestBuilder.index(INDEX_NAME), Poet.class);
        log.info("response={}", response);
    }

    /**
     * match_none 查詢,與 match_all 相反,返回 0 個文件。
     */
    @Test
    public void searchMatchNone() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .matchNone(matchAllQueryBuilder -> matchAllQueryBuilder))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * query_string 查詢,可以同時實現多種查詢
     */
    @Test
    public void searchQueryString() throws IOException {
        //類似 match
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .defaultField("success").query("古典文學")))
                , Poet.class);
        log.info("response={}", response);

        //類似 mulit_match
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .fields("about", "success").query("古典文學")))
                , Poet.class);
        log.info("response={}", response);

        //類似 match_phrase
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .defaultField("success").query("\"文學作家\"")))
                , Poet.class);
        log.info(response.toString());

        //帶運算子查詢,運算子兩邊的詞不再分詞
        //查詢同時包含 ”文學“ 和 ”偉大“ 的文件
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .fields("success").query("文學 AND 偉大")))
                , Poet.class);
        log.info("response={}", response);

        //等同上一個查詢
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .fields("success").query("文學 偉大").defaultOperator(Operator.And)))
                , Poet.class);
        log.info("response={}", response);

        //查詢 name 或 success 欄位包含"文學"和"偉大"這兩個單詞,或者包含"李白"這個單詞的文件。
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .queryString(queryStringQueryBuilder -> queryStringQueryBuilder
                                        .fields("name","success").query("(文學 AND 偉大) OR 高度")))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * simple_query_string 查詢,類似 query_string,主要區別如下:
     * 1、不支援 AND OR NOT,會當做字元處理;使用 + 代替 AND,| 代替 OR,- 代替 NOT
     * 2、會忽略錯誤的語法
     */
    @Test
    public void searchSimpleQueryString() throws IOException {
        //查詢同時包含 ”文學“ 和 ”偉大“ 的文件
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .simpleQueryString(simpleQueryStringQueryBuilder -> simpleQueryStringQueryBuilder
                                        .fields("success").query("文學 + 偉大")))
                , Poet.class);
        log.info("response={}", response);
    }

    /**
     * 模糊查詢
     */
    @Test
    public void searchFuzzy() throws IOException {
        //全文查詢時使用模糊引數,先分詞再計算模糊選項。
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .match(matchQueryBuilder -> matchQueryBuilder
                                        .field("success").query("思考").fuzziness("1")))
                , Poet.class);
        log.info(response.toString());

        //使用 fuzzy query,對輸入不分詞,直接計算模糊選項。
        SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .fuzzy(fuzzyQueryBuilder ->  fuzzyQueryBuilder
                                        .field("success").fuzziness("1").value("理想")))
                , Poet.class);
        log.info(response2.toString());
    }

    /**
     * wildcard 查詢,類似 SQL 語句中的 like;? 匹配一個字元,* 匹配多個字元
     */
    @Test
    public void searchWildcard() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                .index(INDEX_NAME)
                .query(queryBuilder -> queryBuilder.wildcard(wildcardQueryBuilder -> wildcardQueryBuilder
                        .field("name")
                        .wildcard("李*")))
                , Poet.class);
        log.info(response.toString());
    }

    /**
     * bool 查詢,組合查詢
     */
    @Test
    public void searchBool() throws IOException {
        //查詢 success 包含 “思想” 且 age 在 [20-40] 之間的文件
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .bool(boolQueryBuilder -> boolQueryBuilder
                                        .must(queryBuilder2 -> queryBuilder2
                                                .match(matchQueryBuilder -> matchQueryBuilder
                                                        .field("success").query("思想"))
                                        )
                                        .must(queryBuilder2 -> queryBuilder2
                                                .range(rangeQueryBuilder -> rangeQueryBuilder
                                                        .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
                                        )
                                )
                        )
                , Poet.class);
        log.info(response.toString());

        //過濾出 success 包含 “思想” 且 age 在 [20-40] 之間的文件,不計算得分
        SearchResponse<Poet> response2 = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .bool(boolQueryBuilder -> boolQueryBuilder
                                        .filter(queryBuilder2 -> queryBuilder2
                                                .match(matchQueryBuilder -> matchQueryBuilder
                                                        .field("success").query("思想"))
                                        )
                                        .filter(queryBuilder2 -> queryBuilder2
                                                .range(rangeQueryBuilder -> rangeQueryBuilder
                                                        .field("age").gte(JsonData.of("20")).lt(JsonData.of("40")))
                                        )
                                )
                        )
                , Poet.class);
        log.info(response2.toString());
    }

    /**
     * aggs 查詢,聚合查詢
     */
    @Test
    public void searchAggs() throws IOException {
        //求和,類似 select sum(age) from poet-index
        SearchResponse response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .aggregations("age_sum", aggregationBuilder -> aggregationBuilder
                                .sum(sumAggregationBuilder -> sumAggregationBuilder
                                        .field("age")))
                , Poet.class);
        log.info(response.toString());

        //類似 select count distinct(age) from poet-index
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .aggregations("age_count", aggregationBuilder -> aggregationBuilder
                                .cardinality(cardinalityAggregationBuilder -> cardinalityAggregationBuilder.field("age")))
                , Poet.class);
        log.info(response.toString());

        //數量、最大、最小、平均、求和
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .aggregations("age_stats", aggregationBuilder -> aggregationBuilder
                                .stats(statsAggregationBuilder -> statsAggregationBuilder
                                        .field("age")))
                , Poet.class);
        log.info(response.toString());

        //select name,count(*) from poet-index group by name
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field("name")))
                , Map.class);
        log.info(response.toString());

        //select name,age,count(*) from poet-index group by name,age
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .aggregations("name_terms", aggregationBuilder -> aggregationBuilder
                                .terms(termsAggregationBuilder -> termsAggregationBuilder
                                        .field("name")
                                )
                                .aggregations("age_terms", aggregationBuilder2 -> aggregationBuilder2
                                        .terms(termsAggregationBuilder -> termsAggregationBuilder
                                                .field("age")
                                        ))
                        )
                , Poet.class);
        log.info(response.toString());

        //類似 select avg(age) from poet-index where name='李白'
        response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .bool(boolQueryBuilder -> boolQueryBuilder
                                        .filter(queryBuilder2 -> queryBuilder2
                                                .term(termQueryBuilder -> termQueryBuilder
                                                        .field("name").value("李白")))))
                        .aggregations("ave_age", aggregationBuilder -> aggregationBuilder
                                .avg(averageAggregationBuilder -> averageAggregationBuilder.field("age")))
                , Poet.class);
        log.info(response.toString());
    }

    /**
     * suggest 查詢,推薦搜尋
     */
    @Test
    public void searchSuggest() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .suggest(suggesterBuilder -> suggesterBuilder
                                .suggesters("success_suggest", fieldSuggesterBuilder -> fieldSuggesterBuilder
                                        .text("思考")
                                        .term(termSuggesterBuilder -> termSuggesterBuilder
                                                .field("success")
                                                .suggestMode(SuggestMode.Always)
                                                .minWordLength(2)
                                        )
                                )
                        )
                , Poet.class);
        log.info(response.toString());
    }

    /**
     * 高亮顯示
     */
    @Test
    public void searchHighlight() throws IOException {
        SearchResponse<Poet> response = client.search(searchRequestBuilder -> searchRequestBuilder
                        .index(INDEX_NAME)
                        .query(queryBuilder -> queryBuilder
                                .match(matchQueryBuilder -> matchQueryBuilder
                                        .field("success").query("思想藝術")))
                        .highlight(highlightBuilder -> highlightBuilder
                                .preTags("<span color='red'>")
                                .postTags("</span>")
                                .fields("success", highlightFieldBuilder -> highlightFieldBuilder))
                , Poet.class);
        log.info(response.toString());
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    static class Poet {
        private Integer age;
        private String name;
        private String poems;
        private String about;
        /**成就*/
        private String success;
    }
}
ElasticsearchJavaCase2.java

詳細的 Elasticsearch Java API Client 使用說明,請參考官網文件:https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html。

相關文章