拉呱,無論是當作全文檢索工具,還是僅僅當作NOSQL,Elasticsearch的性能,牛的沒法說!!!奈何和它相見恨晚
一. 使用場景
-
全文檢索-像淘寶京東類似的網上商城,當我們在在搜索框搜索某個商品名稱時,網絡沒有問題的話,獲取響應的速度,幾乎和我們鍵盤起落的速度是一致的,這足以ES的魅力,億萬級別的搜索,秒秒鍾完事
-
一個正規的項目運行過程中,日志的產出是源源不斷的,把日志的文本信息導入到ES,使用它的聚合功能輕松獲取我們關系的內容,結合Kibana做圖表分析,可視化日志記錄,動態分析
二. 基本概念
- 2.1 什么是是全文檢索
全文檢索就是計算機程序通過掃描文章中的每一個詞,對必要的詞建立一個索引(上篇博客我們使用ik分詞器,幫助es分詞),記錄下這個詞在文章中出現的位置和次數,這樣當用戶去查詢的時候,他可以根據索引極快的查找出相應的內容
稍微解釋下, 全文檢索可以片面的理解成是從敲代碼的人的角度上說的,假如你是用戶,你會關心什么全文檢索? 用戶關系的是搜索結果!那好,對我們敲代碼的來說,就是1.拿到用戶搜索框輸入的關鍵字送進ES (數據在存進es的時候,es對他們進行了索引)2.ES的程序會對關鍵字進行分詞,拿着這些碎片去ES中的索引庫里面的type中的field中匹配比對,那,那么多field,它和誰比對呢? 和標記上text屬性的字段比對)
另外要了解的內容1.全文檢索只處理文本不處理語義,2.全文檢索忽略英文的大小寫 3.結果可能不止一個,並且有相關得分
- 2.2 與關系型數據庫名稱上的對比
| mysql | Elasticsearch |
|---|---|
| Database 數據庫 | indices 索引 |
| Table 數據表 | type 類型 |
| Row 行 | document 文檔 |
| Columns 列 | Field 字段 |
其實大多數人都是先接觸的關系型數據庫,我也是,可能一開始感覺着好別扭,但是再回頭來看,好像他的名字比傳統的關系型數據庫名什么行啊列啊,更合理一些
- 2.3 分片(shard)和副本(replica)

通過圖片可以看到: 數據分成三部分,分別存放在三個分片里面,而且每個分片都有自己的副本,這樣就算是一台es,它同樣是分布式
二. 怎么玩?
這部分純屬扯皮了,不說語法,說一下我對它的感覺,首先呢, 為什么學它? 圖方便快速唄,大部分情況下,是需要使用它的全文檢索功能,但是總得有個下手點吧,不用說一開始都是環境配置一頓整,訪問個9200看到版本號,也算是開個頭了,然后呢? 先不用想他怎么檢索,怎么花里胡哨的檢索,我們得知道自己想檢索什么!先把數據給它,思路就來了,先去搞數據,下一步自然就是創建新的索引(數據庫),循環把我們的數據送進es的索引里面.到這里,也算是完成一半的任務了,下面就是使用人家提供好的api去索引庫,檢索就好了. 先有個大概的思路.想干什么,怎么干,往下學
三. 原生語法
- elasticsearch采用的REST風格的api,也就是說,其實他就是一次http的請求
下面會有一些關鍵字 也就是json的 key部分,對我們來說,一般可以見名知意
3.1.1 創建索引庫
- 請求方式: PUT
- 請求路徑: /索引庫名
- 請求參數: json
PUT /xiaowu1
{
"settings": {
"number_of_shards": 1
, "number_of_replicas": 1
}
}
成功的相應:
{
"acknowledged": true,
"shards_acknowledged": true,
"index": "xiaowu1"
}
3.1.2 查看數據庫
- 請求方式: GET
- 格式: GET /索引庫名
GET /xiaowu1
響應:相關的源信息
{
"xiaowu1": {
"aliases": {},
"mappings": {},
"settings": {
"index": {
"creation_date": "1552653562807",
"number_of_shards": "1",
"number_of_replicas": "1",
"uuid": "u0tMDD-pQXaHb77cTPorZA",
"version": {
"created": "6020499"
},
"provided_name": "xiaowu1"
}
}
}
}
3.1.3 刪除索引庫
- 請求方式: DELETE
- 格式: DELETE /索引庫名
3.2.1 配置映射
在玩搜索之前我們要思考如何定義文檔的,如文檔包括哪些字段,這些字段是否保存,是否索引,是否分詞等等, 實際上就像是在描述我們數據庫中的一張表,有哪些列,列是什么類型的,是主鍵不?自增長不?
以后看到"mappings":{} 就像查看關系型數據庫中對整張表的定義一樣
- 請求方式: PUT
- 格式: PUT /索引庫名/_mapping/類型名
PUT /xiaowu/_mapping/goods
{
"properties": { ---屬性關鍵字
"name":{ ---name為字段名,可以不止一個,就像數據庫中的列,你開心,多少個都行
"type": "text", ---類型常用:integer,long,text,object,date,short,string,float
"index": true, ---是否索引 默認true
"store": false, ---是否存儲 默認false
"analyzer": "ik_max_word" ---是否分詞
},
"images": {
"type": "keyword",
"index": "false"
},
"age": {
"type": "float"
}
}
}
1 .字符串類型有兩種
- text : 可檢索
- keyword : 不可檢索
2 .數值類型
- 基本數值類型: long ,float,byte,double...
- 浮點數高精度類型: scaled_float
- 它需要我們指定一個精度因子,比如10,100,es把真實值乘以這個精度因子存儲,取出后還原,(商品價格)
- 日期類型:
- Date
- 存儲對象
{girl:{name:"張三",age:23}
而是會把它處理成 girl.name和girl.age
5 .store
- 表示,是否將數據額外存儲,意思是,如果不存儲,用戶就搜索不出來
- 在solr中如果為store設為false,那么用戶就搜索不出來這個字段
- 但是elasticsearch中,並不用store的值控制是否可以被搜索出來,即便他是false,用戶仍然可以搜索出結果,原因是elasticsearch的底層,在創建文檔時,會將文檔的原始數據備份保存到一個叫 _source的屬性中,而我們可以通過過濾_source選擇那些需要顯示,那些不需要顯示,這樣如我們把store的值設置為ture它就會多儲存一份,得不償失(store相當於作廢了)
3.2.2 向已經存在的索引庫中添加數據(不一定要指定ID)
- 請求方式: POST
- 格式: POST /索引庫名/類型名{}
POST /changwu/item/
{
"title":"小米手機",
"price":2699.00
}
響應:
{
"_index": "changwu",
"_type": "item",
"_id": "Pj6bgWkB3eQnUSvRfoa2",
"_version": 1,
"result": "created",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
},
"_seq_no": 5,
"_primary_term": 6
}
id,是自動生成的當前數據的標識,我們還可以指定
POST /changwu/item/2
3.2.3 基本查詢
- 請求方式: GET
GET /索引庫名/_search
{
"query": {
"查詢類型":{
"查詢條件": "條件值"
}
}
}
- query是Elasticsearch內置的一個對象,里面有不同的查詢屬性
使用,SpringDataElasticsearch玩復雜查詢的時候,免不了會BuildQueryXXX
- 查詢類型:
- match_all 查詢所有
- match
- term
- range
- 查詢條件根據類型的不同而不同,(就像關系型數據庫中表的字段不同...)
3.2.4 匹配查詢 match
or 關系
match類型查詢,會把查詢條件進行分詞,然后再查詢,詞條之間是or關系,按照相關性得分排序
GET /goods/_search
{
"query": {
"match":{
"price": 269900
}
}
}
and關系
很多情況下我們希望更精確的查找,於是我們使用 and關系
GET /xiaowu/_search
{
"query":{
"match":{
"title":{
"query":"米手機",
"operator":"and"
}
}
}
}
這樣他在分詞的時候,米--手機同時都匹配上才會顯示出結果!
假設有這樣一種情況,用戶給定的條件分詞后,有五個詞,但是其中的四個是在描述他想要搜索的內容,如果使用or,毫無疑問,一大堆雜七雜八的東西被查詢出來,如果使用and,es很可能把那個目標文檔排除,那該怎么辦呢?看下面!
- match支持使用minimum_should_match 最小匹配參數,通常設置為一個百分數
GET /xiaowu/_search
{
"query":{
"match":{
"title":{
"query":"米手機",
"minimum_should_match":"75%"
}
}
}
}
意思是,用戶輸入的詞條滿足75%的匹配程,我就認為是匹配上了, ---用戶輸入的檢索條件,被分解為三個詞,3*0.75=2.25 也就是說,這三個詞,至少有兩個是匹配上的,es就認為匹配成功
3.2.5多字段查詢 muti_match
- muti_match和match一樣,但是不同的是它可以同時在多個字段中檢索
GET /xiaowu/_search
{
"query":{
"multi_match":{
"query":"米",
"fields":["title"]
}
}
}
他的fields接受一個字段數組
3.2.6 詞條查詢(term)
- 和前面的查詢條件不同的是term,它被用作精確查詢,比如數字,時間,布爾,和字段屬性為keyword類型的關鍵字
GET /xiaowu/_search
{
"query": {
"term": {
"price": {
"value": "1888"
}
}
}
}
3.2.7 多詞條精確匹配
- 和term一樣的精確匹配,但是不同的是它支持同時使用多個詞條進行精確匹配,如果只要索引庫中的文檔包含指定值中的任意一個,都算作滿足條件
3.3.1 結果過濾
默認情況下,elasicsearch在搜索的結果在,會把文檔保存在_source里面的所有字段都返回,如果我們想獲取里面的部分結果,添加_soure過濾
GET /xiaowu/_search
{
"_source": ["過濾字段"],
"query": {
"term": {
"price": {
"value": "1888"
}
}
}
}
- 另外,_source里面還有兩個屬性
- "includes":[想顯示的字段]
- "excludes":[不想顯示的字段]
解讀查詢結果:
{
"took": 1, ----花費時長,單位毫秒
"timed_out": false, ----是否超時
"_shards": { --- 分片信息
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": { --- 命中的結果
"total": 1, --- 結果總數
"max_score": 4.765019, ---相關性的最高得分
"hits": [ --- 搜索結果的對象數組
{
"_index": "goods", --- 索引
"_type": "docs", --- 類型
"_id": "180", --- 唯一id
"_score": 4.765019, ---文檔得分
"_source": { --- 文檔的數據源,(前面說過,結果都在_source里面)
"id": 180,
...
}
- 在所有的index里面查詢指定的字段
GET _search
{
"query": {
"match": {
"title": "小米2手機"
}
}
}
3.4.1 智能判斷
- 新增屬性的時候,可以添加新的字段
POST /xiaowu/goods/
{
"title":"es666",
"hehe":true
}
查詢結果:
{
"_index": "xiaowu",
"_type": "goods",
"_id": "Qz69gWkB3eQnUSvRC4ah",
"_score": 1,
"_source": {
"title": "es666",
"hehe": true
}
查看映射
{
"xiaowu": {
"mappings": {
"goods": {
"properties": {
"age": {
"type": "float"
},
"hehe": {
"type": "boolean"
},
"images": {
"type": "keyword",
"index": false
},
"name": {
"type": "text",
"store": true,
"analyzer": "ik_max_word"
},
"price": {
"type": "float"
},
"title": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
這樣看,其實我們在上面配置的映射關系就沒有必要了,Elasticsearch會智能化的推斷出字段的type,比如true它推斷為boolean, 但是有問題的是title 它推斷為 text ,title.keyword是 keyword類型
3.5.1 修改數據(指明ID)
把上面的POST轉變成PUT就是修改
- id對應的文檔存在則修改
- id對應的文檔不存在則新增
3.6.1 刪除數據
語法: DELETE /索引庫名/類型名/id
3.7.1 高級玩法-布爾組合
看得懂,會使用Elasticsearch的高級玩法很重要,這關系着,能不能理解如何使用它的原生api進行高級查詢
- 布爾組合,結合了其他的查詢,實現了這樣的功能, -- 我的搜索結果中,一定含有誰(must),一定不含有誰(must_not),可能含有誰(should)
GET /xiaowu/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "米"
}
}
],
"must_not": [
{
"match": {
"title": "大"
}
}
],
"should": [
{
"match": {
"price": "999"
}
}
]
}
}
}
3.7.2 高級玩法--范圍查詢range
- range實現的是查詢出滿足某個區間的結果
GET /xiaowu/_search
{
"query": {
"range": {
"FIELD": {
"gte": 10,
"lte": 20
}
}
}
}
| 關鍵字 | 含義 |
|---|---|
| gte | 大於等於 |
| lte | 小於等於 |
| gt | 大於 |
| lt | 小於 |
3.7.3 模糊查詢---fuzzy
模糊查詢很人性化,它允許用戶在編輯的條件有拼寫錯誤,但是出現偏差的編輯差距不得超過2
GET /xiaowu/_search
{
"query": {
"fuzzy": {
"title": {
"value": "appla",
"fuzziness": 1
}
}
}
}
- 這樣查詢是可以查詢出apple的
- "fuzziness": 限定編輯距離
3.7.4 過濾---filter
- 1.有條件查詢進行過濾
查詢就會影響文檔的相關性得分,假如我們只是想在現有的查詢基礎上,根據需求過濾下一些結果,卻不想影響文檔的相關性得分的話filter就派上用場了
GET /changwu/_search
{
"query":{
"bool":{
"must":{ "match": { "title": "小米手機" }},
"filter":{
"range":{"price":{"gt":2000.00,"lt":3800.00}}
}
}
}
}
filter中還可以在bool組合過濾
- 2.無條件查詢進行過濾
如果一次查詢只有過濾,沒有查詢條件,不希望進行評分,我們可以使用constant_score
GET /changwu/_search
{
"query":{
"constant_score": {
"filter": {
"range":{"price":{"gt":2000.00,"lt":3000.00}}
}
}
}
3.7.5 --- 排序 sort
排序sort和query是有先后順序的,先query,再排序
- 單字段排序
GET /changwu/_search
{
"query": {
"match": {
"title": "手機"
}
},
"sort": [
{
"price": {
"order": "asc"
}
}
]
}
- 多字段排序
看上面的sort后面的條件,是個數組,因此我們可以寫條件,按多個字段進行排序
聚合:aggregations
- 基本概念,兩個新概念,在Elasticsearch中的聚合分為兩個部分,聚合為 桶bucket和度量
- 桶就像mysql中的分組查詢,比如說學號相同,姓名相同的肯定是同一個人,我們就把它當成一組,在這里就是一個桶
- 度量--以每個桶為基礎,做運算
當然Elasticsearah里面分桶的方式很多
- Terms Aggregation:根據詞條內容分組,詞條內容完全匹配的為一組
- Range Aggregation:數值和日期的范圍分組,指定開始和結束,然后按段分組,這個范圍要手動告訴他,0-10 10-15 15-30等
- Histogram Aggregation:根據數值階梯(柱狀圖)分組,與日期類似, 告訴他一個段就行了,她會自動的分組
- Date Histogram Aggregation:根據日期階梯分組,例如給定階梯為周,會自動每周分為一組
常用的度量方法:
- Avg Aggregation:求平均值
- Max Aggregation:求最大值
- Min Aggregation:求最小值
- Percentiles Aggregation:求百分比
- Stats Aggregation:同時返回avg、max、min、sum、count等
- Sum Aggregation:求和
- Top hits Aggregation:求前幾
- Value Count Aggregation:求總數
es中進行過濾,排序,聚合的字段,不能被分詞!!!!*
GET /cars/_search
{
"size": 0,
"aggs": {
"popular_brand": {
"terms": {
"field": "color"
},
"aggs": {
"priceAvg": {
"avg": {
"field": "price"
}
}
}
}
}
}
一般都是先聚為桶,然后在桶的基礎上進行度量
四.SpringDataElsticsearch

這是原來畫的圖,JEST直接放棄了,讓我們自己拼接json串,簡直只有難受
終於到編碼階段,這部分相比前面的原生api看起來就好受多了,Spring一整合,啥東西都簡單的只剩下兩件事,1,寫個配置文件,2.用它的方法,玩他的注解--, 當然我現在回顧學習的整個過程,最重要的是還是那句話,不要忘記了自己的需求,不然學着學着容易迷失,不知道自己想干啥,對於Elasticsearch吧先覺的它啥都能干,又覺得它啥也干不了,這是個很尷尬的事情! 那,我用它做全文檢索,我就得去搞明白 知道下面那幾件事
- 怎么搭建起開發環境,使我的java代碼和ES交互
- spring整合它嘛,提供了哪些注解,表示我的javaBean是個document對象
- 如何建立索引庫
- 基本的CRUD
- 花里胡哨的查詢方法
下面挨個做這幾件事!
4.1 搭建開發環境
坐標
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
配置文件(java和它的交互走的是tcp協議)
spring:
application:
name: search-service
data:
elasticsearch:
cluster-nodes: 192.168.43.150:9300
cluster-name: elasticsearch
啟動類
4.1 實體類及注解
實體類在java中就像是接盤俠,啥樣的東東,它都有能給接下來,看完了下面的注解,就知道了如何把數據存進es認識的實體類,准備把他們存在索引庫
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
@Data
@Document(indexName="changwu",type = "item",shards = 1)
public class Item {
@Id
@Field(type=FieldType.Long)
Long id;
@Field(type = FieldType.Text,analyzer = "ik_smart")
String title; //標題
@Field(type=FieldType.Keyword,index = true)
String category;// 分類
@Field(type=FieldType.Keyword)
String brand; // 品牌
@Field(type=FieldType.Double)
Double price; // 價格
@Field(type=FieldType.Keyword,index = false)
String images; // 圖片地址
// 指定存儲時和檢索時使用的分詞器是同一個
// index=true 表示索引
// 是否索引, 就是看這個字段是否能被搜索, 比如: 如果對整篇文章建立了索引,那么從文章中任意抽出一段來,都可以搜索出這個文章
// 是否分詞, 就是表示搜索的時候,是整體匹配還是單詞匹配 比如: 如果不分詞的話,搜索時,一個詞不一樣,都搜索不出來結果
// 是否存儲, 就是,是否在頁面上展示 , 但是在es中默認字段值已經存儲在_source 字段里, 也是能檢索出原始字段的
@Field(index = true, store = true,type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
private String title;
/**
* analyzer 存進去的時候,按這個分詞
* searchAnalyzer: 搜索時,按這個分詞
*/
@Field(index = true, store = true, type = FieldType.Text ,analyzer = "ik_max_word", searchAnalyzer = "ik_max_word")
private String content;
@Field(index = true,type = FieldType.Keyword)
private String state;
}
- @Document
- 作用在類,標記實體類為文檔對象
- indexName:對應索引庫名稱---- 數據庫名
- type:對應在索引庫中的類型---- 表名
- shards:分片數量,默認5
-replicas:副本數量,默認1
- @Id
- 作用在成員變量id上
- 標記一個字段作為id主鍵(這個id別寫錯了,不然程序都啟動不起來)
- @Field
- 作用在成員變量,標記為文檔的字段,並指定字段映射屬性:
- type:字段類型,取值是枚舉:FieldType
- index:是否索引,布爾類型,默認是true
- store:是否存儲,布爾類型,默認是false
- analyzer:分詞器名稱
另外不要忘記了它的智能推斷,如果我們不在字段上添加@Field,他根據值的類型進行推斷
比如: 下面三個都會被推斷為long類型,
private Long cid3;//
private Date createTime;//
private List<Long> price;//
4.2 創建索引庫
索引庫的創建放到text里面就ok了
- 使用的是ElasticsearchTemplate,Spring一直都是這樣,整合完了,給你個模板
創建索引,添加映射,刪除索引
template.createIndex(Goods.class);
template.putMapping(Goods.class);
template.deleteIndex()
4.3 基本的CRUD
SpringData的強大之后就是,我們不再去寫dao層了,她會通過反射給我們寫好,有點Mybatis里面的通用mapper的意思,但是它更強大,---你給方法名它自動的生成方法
public interface GoodsRepository extends ElasticsearchRepository<Goods,Long> {}
使用 repository點一下,findXX saveXXX, deleteXXX全出來了,不再細說
如果可以看懂原生的語法,那么對他的使用就不多說了...就是無腦使用
到了這,就知道了如何把通過java代碼,對索引庫里面的數據進行簡單的增刪改查
4.4 着重看一下如何進行繁雜查詢
真正使用es的時候,Repository里面的方法可以滿足大部分的功能,但是聚合,過濾的話,只能使用原生的API
假設我們有下面的需求: 前端把用戶需要搜索的信息收集起來了--全文檢索
- 全文檢索
/**
* 全文檢索
*/
@Test
public void textQuery(){
//創建查詢構建器
NativeSearchQueryBuilder QueryBuilder = new NativeSearchQueryBuilder();
/**
* 給查詢構造器添加條件
* 1. 它仍然需要的是一個 QueryBuilder ,通過QueryBuilders里面的靜態方法創建,間接繼承了QueryBuild
* 2. 可也看到,基本上所有常用的條件查詢都有了, bool , 詞條term , match , 模糊查詢 fuzzy
* 3. 我們也可以抽出來一個方法, 方法里使用bool查詢, 他支持先添加查詢,緊接着過濾, 還記不記得那個 match{},filter:{}
* 3.1 注意區分開結果過濾_source 和 filter
*
* matchQuery @Param field
* matchQuery @Param field的值
* */
QueryBuilder.withQuery(QueryBuilders.matchQuery("title","小米"));
/**
* 結果過濾
* @Param : SourceFilter ,但是它是和接口,於是我用它唯一的實現類
*/
QueryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{"title","price"},null));
/**
* 排序
*/
QueryBuilder.withSort(SortBuilders.fieldSort("price").order(SortOrder.ASC));
/**
* 分頁
* 原生的api使用下面兩個字段控制
* "from": 0,
* "size":10
* 但是注意,他是的第一頁是0
* @Param : Pageable 他是個接口,我們使用它的實現類 PageRequest(靜態方法of)
*/
QueryBuilder.withPageable(PageRequest.of(1,10));
Page<Goods> result = repository.search(QueryBuilder.build()); //它仍然需要的是一個QueryBuilder , 通過構造器.build()構建出來
//解析結果
long elements = result.getTotalElements();
int totalPages = result.getTotalPages();
List<Goods> content = result.getContent();
}
/**
* 創建基本的查詢條件
* 創建布爾查詢,
* 一部分當作查詢條件(must)
* 一部分當作過濾條件(filter)
* @param searchRequest
* @return QueryBuilder 給QueryBuild.withQuery()使用
*/
private QueryBuilder buildBasicQuery(SearchRequest searchRequest) {
//創建布爾查詢
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery();
//查詢條件
queryBuilder.must(QueryBuilders.matchQuery("字段名",字段值));
if(過濾條件){
/*
*把處理好的字段,傳給過濾器 過濾
*@param name The name of the field
*@param value The value of the term
*/
queryBuilder.filter(QueryBuilders.termQuery(字段名,字段值));
}
return queryBuilder;
}
- 聚合查詢

/**
* 聚合查詢
*/
@Test
public void textAgg(){
//同樣少不了 查詢構造器
NativeSearchQueryBuilder QueryBuilder = new NativeSearchQueryBuilder();
/**
* 添加聚合add, 可以聚合多次
* @Param AbstractAggregationBuilder 它間接繼承與 AggregationBuilder 我們下面的工具類
*
* AggregationBuilders下面基本上涵蓋了我們所有的聚合方式
*/
QueryBuilder.addAggregation(AggregationBuilders.terms("popular_brand").field("brand"));
/**
* 推薦使用和這個,支持聚合查詢,並返回帶聚合的結果
* @Param :SearchQuery
* @Param :Class<T>
* @Return:
*/
AggregatedPage<Goods> result = template.queryForPage(QueryBuilder.build(), Goods.class);
/**
* 解析聚合
*
*/
Aggregations aggregations = result.getAggregations();
// 獲取指定名稱 聚合
//Aggregations agg = aggregations.get("popular_brand");
StringTerms agg = aggregations.get("popular_brand");
/**
* 問題來了, 不存在 agg.getBuckets()
* 原因看上面的圖,是 Aggregations是個頂級接口,
*/
List<StringTerms.Bucket> buckets = agg.getBuckets();
//遍歷buckets
for (StringTerms.Bucket bucket : buckets) {
System.out.println(bucket.getKeyAsString());
System.out.println(bucket.getDocCount());
}
}
