在SpringBoot中使用Elasticsearch

一、SpringBoot模版方式接入(不建議)

其實(shí)一開始是準(zhǔn)備用SpringBoot的模版來直接接入使用的稿湿,也就是以下這樣的接入方式妙黍,也是網(wǎng)上大家都這么說的使用方式匹舞。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-elasticsearch</artifactId>
        </dependency>

但是后面看java api的官方文檔

Deprecated in 7.0.0.

The TransportClient is deprecated in favour of the Java High Level REST Client and will be removed in Elasticsearch 8.0. The migration guide describes all the steps needed to migrate.

再看看模版方式引入的源碼


SpringBoot模版方式引入

直接模版方式的java api調(diào)用方式,后續(xù)官方會不支持了炒瘸,不建議使用,要使用Java High Level REST Client來代替,Elasticsearch 8.0版本后直接移除橘忱,想想還是換人家建議的使用方式吧,免得以后更新?lián)Q代還得做遷移卸奉,也就是我們現(xiàn)在準(zhǔn)備的使用方式钝诚。

二、High Level Java REST Client方式接入

使用High Level Java REST Client進(jìn)行Elasticsearch檢索查詢榄棵,第一步添加依賴

  • org.elasticsearch.client:elasticsearch-rest-client
  • org.elasticsearch:elasticsearch

2.1凝颇、添加依賴

在SpringBoot中的具體添加方式是在pom.xml中:

        <dependency>
            <groupId>org.elasticsearch</groupId>
            <artifactId>elasticsearch</artifactId>
            <version>6.3.2</version>
        </dependency>

        <!-- Java High Level REST Client -->
        <dependency>
            <groupId>org.elasticsearch.client</groupId>
            <artifactId>elasticsearch-rest-high-level-client</artifactId>
            <version>6.3.2</version>
        </dependency>

2.2、添加配置地址

添加依賴之后即可進(jìn)行初始化

RestHighLevelClient client = new RestHighLevelClient(
        RestClient.builder(
                new HttpHost("localhost", 9200, "http")));

這個(gè) client的內(nèi)部會維護(hù)一個(gè)線程池疹鳄,所以在任務(wù)完成后可以通過 client.close()來釋放資源拧略,但是這得看需求,如果需要頻繁進(jìn)行查詢的話尚辑,就直接做成單例辑鲤,避免線程池的不斷創(chuàng)建和釋放也會影響應(yīng)用的性能,在SpringBoot的做法做成單例的話更簡單杠茬。
application.yml配置文件中添加集群地址月褥,我這邊只有一個(gè)弛随,有多個(gè)的可以用逗號分割然后自己解析。

elasticsearch:
  ip: localhost:9200
@Configuration
public class ElasticsearchRestClient {

    /**
     * ES地址,ip:port
     */
    @Value("${elasticsearch.ip}")
    String ipPort;

    @Bean
    public RestClientBuilder restClientBuilder() {

        return RestClient.builder(makeHttpHost(ipPort));
    }


    @Bean(name = "highLevelClient")
    public RestHighLevelClient highLevelClient(@Autowired RestClientBuilder restClientBuilder) {
        restClientBuilder.setMaxRetryTimeoutMillis(60000);
        return new RestHighLevelClient(restClientBuilder);
    }


    private HttpHost makeHttpHost(String s) {
        String[] address = s.split(":");
        String ip = address[0];
        int port = Integer.parseInt(address[1]);
        
        return new HttpHost(ip, port, "http");
    }
}

我們這邊只有一個(gè)地址宁赤,如果有多個(gè)地址舀透,自己做下處理即可。

三决左、Elasticsearch檢索查詢

經(jīng)過上一步驟之后就可以在項(xiàng)目中使用client來進(jìn)行具體的檢索及查詢操作了愕够,具體使用之前先清楚幾個(gè)概念。

3.1 Elasticsearch數(shù)據(jù)結(jié)構(gòu)

在我們這邊的使用場景中佛猛,Elasticsearch是用來存儲各個(gè)端的日志惑芭,在這種場景下,每一條日志就是一個(gè)Document(文檔)继找,我們知道日志中包含了很多信息遂跟,比如上傳時(shí)間,瀏覽器婴渡,ip等等幻锁,每條日志中包含多個(gè)字段信息就是Field(字段),不同的日志可能有不同的類型边臼,比如服務(wù)器日志哄尔,用戶行為日志,這就是Type(類型)柠并,每天的日志分開進(jìn)行存儲是Indice(索引)岭接,可以類比于關(guān)系型數(shù)據(jù)庫比如MySQL。

關(guān)系型數(shù)據(jù)庫 Elasticsearch
Databases(數(shù)據(jù)庫) Indices(索引)
Tables(表) Types(類型)
Rows(行) Documents(文檔)
Columns(列) Fields(字段)

Elasticsearch包含多個(gè)索引(indices)(數(shù)據(jù)庫)堂鲤,每個(gè)索引可以包含多個(gè)類型(types)(表)亿傅,每個(gè)類型包含多個(gè)文檔(documents)(行),每個(gè)文檔包含多個(gè)字段(Fields)(列)瘟栖。

舉個(gè)栗子葵擎,手動添加一條日志,指定indice為customer半哟,type為_doc酬滤,document的id為1。

localhost:9200/customer/_doc/1?pretty
{
    "city": "北京",
    "useragent": "Mobile Safari",
    "sys_version": "Linux armv8l",
    "province": "北京",
    "event_id": "",
    "log_time": 1559191912,
    "session": "343730"
}

然后再查詢一下剛添加的日志寓涨。

GET localhost:9200/customer/_doc/1?pretty
{
    "_index": "customer",
    "_type": "_doc",
    "_id": "1",
    "_version": 3,
    "_seq_no": 2,
    "_primary_term": 1,
    "found": true,
    "_source": {
        "city": "北京",
        "useragent": "Mobile Safari",
        "sys_version": "Linux armv8l",
        "province": "北京",
        "event_id": "",
        "log_time": 1559191912,
        "session": "343730"
    }
}

3.2 Elasticsearch條件查詢

第一步需要初始化SearchRequest盯串,設(shè)置索引(indices)和類型(types),以上面添加的日志為例戒良。

        SearchRequest searchRequest = new SearchRequest();
        searchRequest.indices("customer");
        searchRequest.types("_doc");

然后需要組合查詢條件体捏,主要涉及到=!=>几缭、<這幾個(gè)條件的查詢河泳,需要更復(fù)雜的可以查看官方文檔。

// 條件=
MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("city", "北京");
TermQueryBuilder termQuery = QueryBuilders.termQuery("province", "福建");

// 范圍查詢
RangeQueryBuilder timeFilter = QueryBuilders.rangeQuery("log_time").gt(12345).lt(343750);

構(gòu)建好需要的查詢條件后年栓,需要進(jìn)行組合查詢拆挥,在組合查詢里頭實(shí)現(xiàn)!=條件查詢,需要用到BoolQueryBuilder某抓,BoolQueryBuilder包含4個(gè)方法:

  • must 相當(dāng)于 &(與)條件纸兔。
  • must not 相當(dāng)于~(非)條件。
  • should 相當(dāng)于 | (或)條件否副。
  • filter 類似must汉矿,區(qū)別在于它不參與計(jì)算分值,在不需要用到分值計(jì)算的時(shí)候效率更高备禀。
QueryBuilder totalFilter = QueryBuilders.boolQuery()
                .filter(matchQuery)
                .filter(timeFilter)
                .mustNot(termQuery);

3.3 Elasticsearch分頁查詢

可以設(shè)置每次查詢返回的文檔數(shù)量负甸,如果不設(shè)置的話,默認(rèn)只返回10條hits痹届,這個(gè)數(shù)量可以手動設(shè)置:

sourceBuilder.query(totalFilter).size(100);

單單設(shè)置返回條數(shù)還不滿足需求,因?yàn)槲覀冞@邊是沒有辦法事先確定的打月,所以需要自己來實(shí)現(xiàn)分頁队腐,需要from()方法進(jìn)行輔助。

完整示例代碼如下:

@Service
    public class TestService {
        @Autowired
        RestHighLevelClient highLevelClient;

        private void search(RestHighLevelClient highLevelClient) throws IOException {

            SearchRequest searchRequest = new SearchRequest();
            searchRequest.indices("customer");
            searchRequest.types("_doc");

            // 條件=
            MatchQueryBuilder matchQuery = QueryBuilders.matchQuery("city", "北京");
            TermQueryBuilder termQuery = QueryBuilders.termQuery("province", "福建");
            // 范圍查詢
            RangeQueryBuilder timeFilter = QueryBuilders.rangeQuery("log_time").gt(12345).lt(343750);
            SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

            QueryBuilder totalFilter = QueryBuilders.boolQuery()
                    .filter(matchQuery)
                    .filter(timeFilter)
                    .mustNot(termQuery);

            int size = 200;
            int from = 0;
            long total = 0;

            do {
                try {
                    sourceBuilder.query(totalFilter).from(from).size(size);
                    sourceBuilder.timeout(new TimeValue(60, TimeUnit.SECONDS));
                    searchRequest.source(sourceBuilder);

                    SearchResponse response = highLevelClient.search(searchRequest);
                    SearchHit[] hits = response.getHits().getHits();
                    for (SearchHit hit : hits) {
                        System.out.println(hit.getSourceAsString());
                    }

                    total = response.getHits().totalHits;

                    System.out.println("測試:[" + total + "][" + from + "-" + (from + hits.length) + ")");

                    from += hits.length;

                    // from + size must be less than or equal to: [10000]
                    if (from >= 10000) {
                      System.out.println("測試:超過10000條直接中斷");
                      break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            } while (from < total);
        }
    }

3.4 分頁查詢異常

在分頁的過程中出現(xiàn)了一個(gè)問題是當(dāng)查詢的數(shù)據(jù)超過10000條的時(shí)候報(bào)了異常:

from + size must be less than or equal to: [10000]

這個(gè)問題最快捷的解決方式是增大窗口大小:

curl -XPUT http://127.0.0.1:9200/customer/_settings -d '{ "index" : { "max_result_window" : 500000}}'

但是對應(yīng)增大窗口大小奏篙,會犧牲更多的服務(wù)器的內(nèi)存柴淘、CPU資源,在我們這邊的使用場景下秘通,這樣做是劃不來的为严,因?yàn)槲覀兊哪康氖亲瞿繕?biāo)數(shù)據(jù)的搜索,而不是大規(guī)模的遍歷肺稀,所以我們這邊會直接放棄超過這個(gè)數(shù)量的查詢第股,也就是上面的這段代碼:

 // from + size must be less than or equal to: [10000]
  if (from > 10000) {
     System.out.println("測試:超過10000條直接中斷");
     break;
  }

對于Elasticsearch其實(shí)也是很多地方還不熟悉,感興趣的童鞋可以多多一起交流和指正话原,不然的話后續(xù)也只能在使用過程中來加深理解夕吻。

參考:
1、Elasticsearch: 權(quán)威指南
2繁仁、Elasticsearch: Java API [7.1]
3涉馅、Elasticsearch: Java REST Client [7.1]
4、Elasticsearch查詢——布爾查詢Bool Query
5黄虱、解決ElasticSearch深度分頁機(jī)制中Result window is too large問題

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末稚矿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌晤揣,老刑警劉巖桥爽,帶你破解...
    沈念sama閱讀 216,372評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異碉渡,居然都是意外死亡聚谁,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,368評論 3 392
  • 文/潘曉璐 我一進(jìn)店門滞诺,熙熙樓的掌柜王于貴愁眉苦臉地迎上來形导,“玉大人,你說我怎么就攤上這事习霹《涓” “怎么了?”我有些...
    開封第一講書人閱讀 162,415評論 0 353
  • 文/不壞的土叔 我叫張陵淋叶,是天一觀的道長阎曹。 經(jīng)常有香客問我,道長煞檩,這世上最難降的妖魔是什么处嫌? 我笑而不...
    開封第一講書人閱讀 58,157評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮斟湃,結(jié)果婚禮上熏迹,老公的妹妹穿的比我還像新娘。我一直安慰自己凝赛,他們只是感情好注暗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,171評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著墓猎,像睡著了一般捆昏。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上毙沾,一...
    開封第一講書人閱讀 51,125評論 1 297
  • 那天骗卜,我揣著相機(jī)與錄音,去河邊找鬼左胞。 笑死膨俐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的罩句。 我是一名探鬼主播焚刺,決...
    沈念sama閱讀 40,028評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼门烂!你這毒婦竟也來了乳愉?” 一聲冷哼從身側(cè)響起兄淫,我...
    開封第一講書人閱讀 38,887評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎蔓姚,沒想到半個(gè)月后捕虽,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,310評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡坡脐,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,533評論 2 332
  • 正文 我和宋清朗相戀三年泄私,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片备闲。...
    茶點(diǎn)故事閱讀 39,690評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡晌端,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出恬砂,到底是詐尸還是另有隱情咧纠,我是刑警寧澤,帶...
    沈念sama閱讀 35,411評論 5 343
  • 正文 年R本政府宣布泻骤,位于F島的核電站漆羔,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏狱掂。R本人自食惡果不足惜演痒,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,004評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望趋惨。 院中可真熱鬧嫡霞,春花似錦、人聲如沸希柿。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽曾撤。三九已至,卻和暖如春晕粪,著一層夾襖步出監(jiān)牢的瞬間挤悉,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,812評論 1 268
  • 我被黑心中介騙來泰國打工巫湘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留装悲,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,693評論 2 368
  • 正文 我出身青樓尚氛,卻偏偏與公主長得像诀诊,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子阅嘶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,577評論 2 353