Elasticsearch安裝?+?CRUD?+?查詢實戰(zhàn)指南
一、Elasticsearch 是什么??
Elasticsearch(簡稱 ES) 是基于 Lucene 的分布式全文搜索引擎,支持近實時搜索、海量數(shù)據(jù)存儲、高可用集群、RESTful API,廣泛用于日志分析、商品搜索、數(shù)據(jù)可視化等場景。?
在搜索時為什么我們要用 Elasticsearch,而不是 MySQL 模糊匹配?
在實際業(yè)務中,商品搜索、日志檢索、全文搜索 這些場景,MySQL 根本扛不住。
1. MySQL 的like '%關(guān)鍵詞%'為什么慢?
- 無法走索引,必須全表掃描
- 數(shù)據(jù)量越大,速度越慢
- 不支持分詞,不支持相關(guān)性打分
- 不支持高亮、同義詞、拼音搜索
簡單說:數(shù)據(jù)量一大,like 查詢直接拖垮數(shù)據(jù)庫。
2. Elasticsearch 為什么快?—— 倒排索引原理
ES 快的核心原因只有一個:它用了 倒排索引(Inverted Index)。
我用最簡單的方式講清楚:
正常數(shù)據(jù)庫(正排索引)
文檔1 → 我喜歡華為手機
文檔2 → 華為手機信號好
文檔3 → 蘋果手機也不錯
倒排索引(關(guān)鍵詞 → 文檔)
華為 → 文檔1、文檔2
手機 → 文檔1、文檔2、文檔3
蘋果 → 文檔3 喜歡 → 文檔1
當你搜索:“華為”
ES 不需要掃全表,直接查倒排索引:華為 → 文檔 1、文檔 2直接返回,毫秒級響應。
3. 倒排索引三大優(yōu)勢
- 檢索極快:不需要全表掃描
- 支持分詞:把一句話切成詞語
- 相關(guān)性打分:最匹配的排前面
MySQL 適合存數(shù)據(jù),Elasticsearch 適合搜數(shù)據(jù)。
MySQL 用 B+ 樹,適合精確查詢。
Elasticsearch 用倒排索引,適合全文檢索。
Elasticsearch核心概念?
- 索引(Index):對應 MySQL 的數(shù)據(jù)庫?
- 文檔(Document):對應 MySQL 的行數(shù)據(jù)?
- 映射(Mapping):對應 MySQL 的表結(jié)構(gòu)?
- 分片 / 副本:保證分布式存儲與高可用
文檔數(shù)據(jù)會被序列化為json格式后存儲在elasticsearch
我們統(tǒng)一的把mysql與elasticsearch的概念做一下對比

Kibana是elastic公司提供的用于操作Elasticsearch的可視化控制臺。它的功能非常強大,包括:
- 對Elasticsearch數(shù)據(jù)的搜索、展示
- 對Elasticsearch數(shù)據(jù)的統(tǒng)計、聚合,并形成圖形化報表、圖形
- 對Elasticsearch的集群狀態(tài)監(jiān)控
- 它還提供了一個開發(fā)控制臺(DevTools),在其中對Elasticsearch的Restful的API接口提供了語法提示
elasticsearch安裝
通過下面的Docker命令即可安裝單機版本的elasticsearch:
docker run -d \ --name es \ -e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \ -e "discovery.type=single-node" \ -v es-data:/usr/share/elasticsearch/data \ -v es-plugins:/usr/share/elasticsearch/plugins \ --privileged \ --network hm-net \ -p 9200:9200 \ -p 9300:9300 \ elasticsearch:7.12.1
訪問9200端口,即可看到響應的Elasticsearch服務的基本信息:

安裝kibana
docker run -d \ --name kibana \ -e ELASTICSEARCH_HOSTS=http://es:9200 \ --network=hm-net \ -p 5601:5601 \ kibana:7.12.1
訪問5601端口,即可看到控制臺頁面:


ik分詞器
Elasticsearch的關(guān)鍵就是倒排索引,而倒排索引依賴于對文檔內(nèi)容的分詞,而分詞則需要高效、精準的分詞算法,IK分詞器就是這樣一個中文分詞算法。
安裝:
docker exec -it es ./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip
IK分詞器包含兩種模式:
ik_smart:智能語義切分ik_max_word:最細粒度切分
詞典:隨著互聯(lián)網(wǎng)的發(fā)展出現(xiàn)了很多新的詞語,在原有的詞匯列表中并不存在,IK分詞器無法對這些詞匯分詞,要想正確分詞,IK分詞器的詞庫也需要不斷的更新,IK分詞器提供了擴展詞匯的功能
在分詞器的配置目錄下

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 *** 添加擴展詞典-->
<entry key="ext_dict">ext.dic</entry>
</properties>改目錄下目錄新建一個 ext.dic,然后在里面添加新的詞匯,重啟后ik分詞器就能正確識別ext.dic的這些單詞
CRUD操作:
由于Elasticsearch采用的是Restful風格的API,因此其請求方式和路徑相對都比較規(guī)范,而且請求參數(shù)也都采用JSON風格。
我們直接基于Kibana的DevTools來編寫請求做測試,由于有語法提示,會非常方便。
.創(chuàng)建索引庫和映射
基本語法:
- 請求方式:
PUT - 請求路徑:
/索引庫名,可以自定義 - 請求參數(shù):
mapping映射
PUT /索引庫名稱
{
"mappings": {
"properties": {
"字段名":{
"type": "text",
"analyzer": "ik_smart"
},
"字段名2":{
"type": "keyword",
"index": "false"
},
"字段名3":{
"properties": {
"子字段": {
"type": "keyword"
}
}
},
// ...略
}
}
}查詢索引庫
基本語法:
- 請求方式:GET
- 請求路徑:/索引庫名
- 請求參數(shù):無
比如:GET /item
修改索引庫
倒排索引結(jié)構(gòu)雖然不復雜,但是一旦數(shù)據(jù)結(jié)構(gòu)改變(比如改變了分詞器),就需要重新創(chuàng)建倒排索引,這簡直是災難。因此索引庫一旦創(chuàng)建,無法修改mapping。
雖然無法修改mapping中已有的字段,但是卻允許添加新的字段到mapping中,因為不會對倒排索引產(chǎn)生影響。因此修改索引庫能做的就是向索引庫中添加新字段,或者更新索引庫的基礎(chǔ)屬性。
語法:
PUT /索引庫名/_mapping
{
"properties": {
"新字段名":{
"type": "integer"
}
}
}刪除索引庫
語法:
- 請求方式:DELETE
- 請求路徑:/索引庫名
- 請求參數(shù):無
文檔操作
有了索引庫,接下來就可以向索引庫中添加數(shù)據(jù)了。
4.1 新增文檔(指定 ID)?
PUT /product/_doc/1?
{?
"id": 1,?
"name": "華為Mate70",?
"price": 5999,?
"category": "手機"?
}?新增文檔(自動 ID)
POST /product/_doc?
{?
"id": 2,?
"name": "小米14",?
"price": 4299,?
"category": "手機"?
}4.3 查詢文檔(根據(jù) ID)
GET /product/_doc/1
全量更新文檔(相當于覆蓋原文檔)
PUT /product/_doc/1
{
"id": 1,
"name": "華為Mate70 Pro",
"price": 6999,
"category": "手機"
}局部更新文檔(推薦)
POST /product/_update/1
{
"doc": {
"price": 6799
}
}刪除文檔
DELETE /product/_doc/1
批處理
POST _bulk
{ "index" : { "_index" : "test", "_id" : "1" } }
{ "field1" : "value1" }
{ "delete" : { "_index" : "test", "_id" : "2" } }
{ "create" : { "_index" : "test", "_id" : "3" } }
{ "field1" : "value3" }
{ "update" : {"_id" : "1", "_index" : "test"} }
{ "doc" : {"field2" : "value2"} }index代表新增操作_index:指定索引庫名_id指定要操作的文檔id{ "field1" : "value1" }:則是要新增的文檔內(nèi)容
delete代表刪除操作_index:指定索引庫名_id指定要操作的文檔id
update代表更新操作_index:指定索引庫名_id指定要操作的文檔id{ "doc" : {"field2" : "value2"} }:要更新的文檔字段
總結(jié)
創(chuàng)建文檔:
POST /{索引庫名}/_doc/文檔id { json文檔 }查詢文檔:
GET /{索引庫名}/_doc/文檔id刪除文檔:
DELETE /{索引庫名}/_doc/文檔id修改文檔:
全量修改:
PUT /{索引庫名}/_doc/文檔id { json文檔 }局部修改:
POST /{索引庫名}/_update/文檔id { "doc":{字段}}
在java中訪問客戶端
引入依賴
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>因為SpringBoot默認的ES版本是7.17.10,所以我們需要覆蓋默認的ES版本
<properties>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>初始化RestHighLevelClient:
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));package com.hmall.item.es;
import org.apache.http.HttpHost;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.IOException;
public class IndexTest {
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@Test
void testConnect() {
System.out.println(client);
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}RestClient操作索引庫

代碼分為三步:
1)創(chuàng)建Request對象。
因為是創(chuàng)建索引庫的操作,因此Request是
CreateIndexRequest。
2)添加請求參數(shù)
其實就是Json格式的Mapping映射參數(shù)。因為json字符串很長,這里是定義了靜態(tài)字符串常量
MAPPING_TEMPLATE,讓代碼看起來更加優(yōu)雅。
3)發(fā)送請求
client.indices()方法的返回值是IndicesClient類型,封裝了所有與索引庫操作有關(guān)的方法。例如創(chuàng)建索引、刪除索引、判斷索引是否存在等
RestClient操作文檔
新增文檔
// 1.準備Request對象
IndexRequest request = new IndexRequest("items").id(itemDoc.getId());
// 2.準備Json文檔
request.source(doc, XContentType.JSON);
// 3.發(fā)送請求
client.index(request, RequestOptions.DEFAULT);1)創(chuàng)建Request對象,這里是
IndexRequest,因為添加文檔就是創(chuàng)建倒排索引的過程2)準備請求參數(shù),本例中就是Json文檔
3)發(fā)送請求
查詢文檔

刪除文檔
@Test
void testDeleteDocument() throws IOException {
// 1.準備Request,兩個參數(shù),第一個是索引庫名,第二個是文檔id
DeleteRequest request = new DeleteRequest("item", "100002644680");
// 2.發(fā)送請求
client.delete(request, RequestOptions.DEFAULT);
}修改文檔
修改我們講過兩種方式:
全量修改:本質(zhì)是先根據(jù)id刪除,再新增
局部修改:修改文檔中的指定字段值
在RestClient的API中,全量修改與新增的API完全一致,判斷依據(jù)是ID:
如果新增時,ID已經(jīng)存在,則修改
如果新增時,ID不存在,則新增
這里不再贅述,我們主要關(guān)注局部修改的API即可。

.批量導入文檔
@Test
void testBulk() throws IOException {
// 1.創(chuàng)建Request
BulkRequest request = new BulkRequest();
// 2.準備請求參數(shù)
request.add(new IndexRequest("items").id("1").source("json doc1", XContentType.JSON));
request.add(new IndexRequest("items").id("2").source("json doc2", XContentType.JSON));
// 3.發(fā)送請求
client.bulk(request, RequestOptions.DEFAULT);
}總結(jié)
初始化
RestHighLevelClient創(chuàng)建XxxRequest。
XXX是
Index、Get、Update、Delete、Bulk
準備參數(shù)(
Index、Update、Bulk時需要)發(fā)送請求。
調(diào)用
RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
解析結(jié)果(
Get時需要)
DSL查詢
在 Elasticsearch 中,所有復雜查詢都是通過 DSL(Domain Specific Language) 來實現(xiàn)的。簡單說:DSL = 用 JSON 寫查詢語句
所有查詢都分為兩類:
(1)查詢上下文(Query Context)
會計算相關(guān)性分數(shù)(_score)用于:全文搜索、模糊匹配例:match、match_phrase、multi_match
(2)過濾上下文(Filter Context)
不計算分數(shù),只判斷 是 / 否速度極快,會自動緩存用于:精確匹配、范圍、布爾過濾例:term、range、bool filter
最常用 DSL 查詢
match 全文檢索
對字段分詞后匹配
{
"query": {
"match": {
"name": "拉桿箱"
}
}
}查詢結(jié)果一般格式

term 精確匹配(不分詞)
用于 keyword、數(shù)字、布爾
{
"query": {
"term": {
"category": "手機"
}
}
}range 范圍查詢
{
"query": {
"range": {
"price": {
"gte": 3000,
"lte": 8000
}
}
}
}搜索的關(guān)鍵字高亮
GET /{索引庫名}/_search
{
"query": {
"match": {
"搜索字段": "搜索關(guān)鍵字"
}
},
"highlight": {
"fields": {
"高亮字段名稱": {
"pre_tags": "<em>",
"post_tags": "</em>"
}
}
}
}bool :組合查詢包含四個子句:
- must:必須滿足(算分)
- should:滿足任意一個
- must_not:必須不滿足
- filter:必須滿足(過濾,不計分)
示例
{
"query": {
"bool": {
"must": [
{ "match": { "name": "手機" } }
],
"filter": [
{ "range": { "price": { "gte": 2000 } } }
]
}
}
}分頁、排序、指定返回字段
{
"from": 0,
"size": 10,
"_source": ["name", "price"],
"sort": [
{ "price": { "order": "desc" } }
],
"query": {
"match_all": {}
}
}總結(jié)
查詢的DSL是一個大的JSON對象,包含下列屬性:
query:查詢條件from和size:分頁條件sort:排序條件highlight:高亮條件
Java中RestClient查詢

第一步,創(chuàng)建
SearchRequest對象,指定索引庫名第二步,利用
request.source()構(gòu)建DSL,DSL中可以包含查詢、分頁、排序、高亮等query():代表查詢條件,利用QueryBuilders.matchAllQuery()構(gòu)建一個match_all查詢的DSL
第三步,利用
client.search()發(fā)送請求,得到響應
request.source(),它構(gòu)建的就是DSL中的完整JSON參數(shù)。其中包含了query、sort、from、size、highlight等所有功能

QueryBuilders,其中包含了葉子查詢、復合查詢

解析響應結(jié)果

elasticsearch返回的結(jié)果是一個JSON字符串,結(jié)構(gòu)包含:
hits:命中的結(jié)果total:總條數(shù),其中的value是具體的總條數(shù)值max_score:所有結(jié)果中得分最高的文檔的相關(guān)性算分hits:搜索結(jié)果的文檔數(shù)組,其中的每個文檔都是一個json對象_source:文檔中的原始數(shù)據(jù),也是json對象
因此,我們解析響應結(jié)果,就是逐層解析JSON字符串,流程如下:
SearchHits:通過response.getHits()獲取,就是JSON中的最外層的hits,代表命中的結(jié)果SearchHits#getTotalHits().value:獲取總條數(shù)信息SearchHits#getHits():獲取SearchHit數(shù)組,也就是文檔數(shù)組SearchHit#getSourceAsString():獲取文檔結(jié)果中的_source,也就是原始的json文檔數(shù)據(jù)
葉子查詢
所有的查詢條件都是由QueryBuilders來構(gòu)建的,葉子查詢也不例外。因此整套代碼中變化的部分僅僅是query條件構(gòu)造的方式,其它不動。
match查詢:
@Test
void testMatch() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}multi_match查詢
@Test
void testMultiMatch() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
request.source().query(QueryBuilders.multiMatchQuery("脫脂牛奶", "name", "category"));
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}還有range查詢:
@Test
void testRange() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
request.source().query(QueryBuilders.rangeQuery("price").gte(10000).lte(30000));
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}term查詢:
@Test
void testTerm() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
request.source().query(QueryBuilders.termQuery("brand", "華為"));
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}復合查詢
復合查詢也是由QueryBuilders來構(gòu)建,我們以bool查詢?yōu)槔?,DSL和JavaAPI的對比如圖:

@Test
void testBool() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
// 2.1.準備bool查詢
BoolQueryBuilder bool = QueryBuilders.boolQuery();
// 2.2.關(guān)鍵字搜索
bool.must(QueryBuilders.matchQuery("name", "脫脂牛奶"));
// 2.3.品牌過濾
bool.filter(QueryBuilders.termQuery("brand", "德亞"));
// 2.4.價格過濾
bool.filter(QueryBuilders.rangeQuery("price").lte(30000));
request.source().query(bool);
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}排序和分頁

@Test
void testPageAndSort() throws IOException {
int pageNo = 1, pageSize = 5;
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
// 2.1.搜索條件參數(shù)
request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
// 2.2.排序參數(shù)
request.source().sort("price", SortOrder.ASC);
// 2.3.分頁參數(shù)
request.source().from((pageNo - 1) * pageSize).size(pageSize);
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}高亮
@Test
void testHighlight() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.組織請求參數(shù)
// 2.1.query條件
request.source().query(QueryBuilders.matchQuery("name", "脫脂牛奶"));
// 2.2.高亮條件
request.source().highlighter(
SearchSourceBuilder.highlight()
.field("name")
.preTags("<em>")
.postTags("</em>")
);
// 3.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 4.解析響應
handleResponse(response);
}
private void handleResponse(SearchResponse response) {
SearchHits searchHits = response.getHits();
// 1.獲取總條數(shù)
long total = searchHits.getTotalHits().value;
System.out.println("共搜索到" + total + "條數(shù)據(jù)");
// 2.遍歷結(jié)果數(shù)組
SearchHit[] hits = searchHits.getHits();
for (SearchHit hit : hits) {
// 3.得到_source,也就是原始json文檔
String source = hit.getSourceAsString();
// 4.反序列化
ItemDoc item = JSONUtil.toBean(source, ItemDoc.class);
// 5.獲取高亮結(jié)果
Map<String, HighlightField> hfs = hit.getHighlightFields();
if (CollUtils.isNotEmpty(hfs)) {
// 5.1.有高亮結(jié)果,獲取name的高亮結(jié)果
HighlightField hf = hfs.get("name");
if (hf != null) {
// 5.2.獲取第一個高亮結(jié)果片段,就是商品名稱的高亮值
String hfName = hf.getFragments()[0].string();
item.setName(hfName);
}
}
System.out.println(item);
}
}數(shù)據(jù)聚合
聚合 = 對數(shù)據(jù)進行統(tǒng)計、分組、求和、平均值、最大值、最小值、計數(shù)。
你可以把它理解為:MySQL 的 group by + sum + avg + count 超級加強版。
聚合的基本結(jié)構(gòu)
{
"size": 0, // 不返回原始數(shù)據(jù),只返回統(tǒng)計結(jié)果
"aggs": { // 聚合固定寫 aggs
"自定義名稱": {
"聚合類型": {
"field": "字段名"
}
}
}
}聚合常見的有三類:
桶(
Bucket)聚合:用來對文檔做分組TermAggregation:按照文檔字段值分組,例如按照品牌值分組、按照國家分組Date Histogram:按照日期階梯分組,例如一周為一組,或者一月為一組
度量(
Metric)聚合:用以計算一些值,比如:最大值、最小值、平均值等Avg:求平均值Max:求最大值Min:求最小值Stats:同時求max、min、avg、sum等
管道(
pipeline)聚合:其它聚合的結(jié)果為基礎(chǔ)做進一步運算
注意:參加聚合的字段必須是keyword、日期、數(shù)值、布爾類型
.Bucket聚合
例如我們要統(tǒng)計所有商品中共有哪些商品分類,其實就是以分類(category)字段對數(shù)據(jù)分組。category值一樣的放在同一組,屬于Bucket聚合中的Term聚合。
GET /items/_search
{
"size": 0,
"aggs": {
"category_agg": {
"terms": {
"field": "category",
"size": 20
}
}
}
}size:設(shè)置size為0,就是每頁查0條,則結(jié)果中就不包含文檔,只包含聚合aggs:定義聚合category_agg:聚合名稱,自定義,但不能重復terms:聚合的類型,按分類聚合,所以用termfield:參與聚合的字段名稱size:希望返回的聚合結(jié)果的最大數(shù)量
查詢的結(jié)果:

嵌套聚合
先按分類分組 → 再求每組的平均價格
GET /product/_search
{
"size": 0,
"aggs": {
"group_by_category": {
"terms": {
"field": "category"
},
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}
}
}結(jié)果
手機 → 平均價格 5800
電腦 → 平均價格 7200
按價格區(qū)間統(tǒng)計(range 聚合)
電商最常用:價格分段統(tǒng)計
GET /product/_search
{
"size": 0,
"aggs": {
"price_range": {
"range": {
"field": "price",
"ranges": [
{ "to": 3000 },
{ "from": 3000, "to": 6000 },
{ "from": 6000 }
]
}
}
}
}對搜索結(jié)果聚合。
例如,我想知道價格高于3000元的手機品牌有哪些
GET /items/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"category": "手機"
}
},
{
"range": {
"price": {
"gte": 300000
}
}
}
]
}
},
"size": 0,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}求平均值(avg)
GET /product/_search
{
"size": 0,
"aggs": {
"avg_price": {
"avg": {
"field": "price"
}
}
}
}求和(sum)
GET /product/_search
{
"size": 0,
"aggs": {
"total_price": {
"sum": {
"field": "price"
}
}
}
}最大值、最小值(max /min)
GET /product/_search
{
"size": 0,
"aggs": {
"max_price": {
"max": {
"field": "price"
}
},
"min_price": {
"min": {
"field": "price"
}
}
}
}例:
統(tǒng)計價格高于3000的手機品牌,形成一個個桶。對桶內(nèi)的商品做運算,獲取每個品牌價格的最小值、最大值、平均值。
GET /items/_search
{
"query": {
"bool": {
"filter": [
{
"term": {
"category": "手機"
}
},
{
"range": {
"price": {
"gte": 300000
}
}
}
]
}
},
"size": 0,
"aggs": {
"brand_agg": {
"terms": {
"field": "brand",
"size": 20
},
"aggs": {
"stats_meric": {
"stats": {
"field": "price"
}
}
}
}
}
}stat聚合,可以同時獲取min、max、avg的結(jié)果。
.RestClient實現(xiàn)聚合
可以看到在DSL中,aggs聚合條件與query條件是同一級別,都屬于查詢JSON參數(shù)。因此依然是利用request.source()方法來設(shè)置。
不過聚合條件的要利用AggregationBuilders這個工具類來構(gòu)造。DSL與JavaAPI的語法對比如下:

聚合結(jié)果與搜索文檔同一級別,因此需要單獨獲取和解析。具體解析語法如下:

@Test
void testAgg() throws IOException {
// 1.創(chuàng)建Request
SearchRequest request = new SearchRequest("items");
// 2.準備請求參數(shù)
BoolQueryBuilder bool = QueryBuilders.boolQuery()
.filter(QueryBuilders.termQuery("category", "手機"))
.filter(QueryBuilders.rangeQuery("price").gte(300000));
request.source().query(bool).size(0);
// 3.聚合參數(shù)
request.source().aggregation(
AggregationBuilders.terms("brand_agg").field("brand").size(5)
);
// 4.發(fā)送請求
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
// 5.解析聚合結(jié)果
Aggregations aggregations = response.getAggregations();
// 5.1.獲取品牌聚合
Terms brandTerms = aggregations.get("brand_agg");
// 5.2.獲取聚合中的桶
List<? extends Terms.Bucket> buckets = brandTerms.getBuckets();
// 5.3.遍歷桶內(nèi)數(shù)據(jù)
for (Terms.Bucket bucket : buckets) {
// 5.4.獲取桶內(nèi)key
String brand = bucket.getKeyAsString();
System.out.print("brand = " + brand);
long count = bucket.getDocCount();
System.out.println("; count = " + count);
}
}到此這篇關(guān)于Elasticsearch安裝 + CRUD + 查詢實戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Elasticsearch 入門到實戰(zhàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解spring boot實現(xiàn)多數(shù)據(jù)源代碼實戰(zhàn)
本篇文章主要介紹了詳解spring boot實現(xiàn)多數(shù)據(jù)源代碼實戰(zhàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-07-07
SpringBoot部署和前端連接問題解決的完整指南(net::ERR_CONNECTION_REFUSED)
在開發(fā)和部署 Spring Boot 應用時,可能會遇到各種問題,例如 JAR 文件無法運行、前端無法連接后端服務等,本文將詳細總結(jié)這些問題的解決方法,幫助你順利部署和運行 Spring Boot 應用,需要的朋友可以參考下2025-01-01
Java中Word與PDF轉(zhuǎn)換為圖片的方法詳解
這篇文章主要為大家詳細介紹了如何使用Java實現(xiàn)將Word與PDF轉(zhuǎn)換為圖片,文中的示例代碼講解詳細,感興趣的小伙伴可以跟隨小編一起學習一下2024-10-10

