1. 數(shù)據(jù)聚合
1.1 聚合介紹
聚合(aggregations)可以實(shí)現(xiàn)對文檔數(shù)據(jù)的統(tǒng)計(jì)、分析、運(yùn)算。
聚合常見的有三類:
-
桶(Bucket)聚合:用來對文檔做分組
- TermAggregation:按照文檔字段值分組
- Date Histogram:按照日期階梯分組,例如一周為一組,或者一月為一組
-
度量(Metric)聚合:用以計(jì)算一些值,比如:最大值、最小值、平均值等
- Avg:求平均值
- Max:求最大值
- Min:求最小值
- Stats:同時(shí)求max、min、avg、sum等
- 管道(pipeline)聚合:其它聚合的結(jié)果為基礎(chǔ)做聚合
參與聚合的字段為以下字段:
- keyword
- 數(shù)值
- 日期
- 布爾
注意,不能是 text 字段
1.2 Bucket 聚合
這里假如我們需要對不同品牌的酒店進(jìn)行聚合人,那么我們就可以使用桶聚合,桶聚合的例子如下:
GET /hotel/_search
{
"size": 0, // 設(shè)置size為0,結(jié)果中不包含文檔,只包含聚合結(jié)果
"aggs": { // 定義聚合
"brandAgg": { //給聚合起個(gè)名字
"terms": { // 聚合的類型,按照品牌值聚合,所以選擇term
"field": "brand", // 參與聚合的字段
"order": {
"_count": "asc"// 按照聚合數(shù)量進(jìn)行升序排列,默認(rèn)降序
},
"size": 20 // 希望獲取的聚合結(jié)果數(shù)量
}
}
}
}
聚合結(jié)果如下:
如果我們需要在一些查詢的條件下進(jìn)行聚合,比如我們只對200元一下的酒店文檔進(jìn)行聚合,那么聚合條件如下:
GET /hotel/_search
{
"query": {
"range": {
"price": {
"lte": 200 // 只對200元以下的文檔聚合
}
}
},
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
}
}
}
}
1.3 Metrics 聚合
如果我們需要按照每個(gè)品牌的用戶的評分的最大值、最小值、平均值等進(jìn)行排序,那么這就需要用到 Metrics 聚合了,我們使用 stats
查看所有的聚合屬性,該聚合的實(shí)現(xiàn)如下:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20
},
"aggs": { // 是brands聚合的子聚合,也就是分組后對每組分別計(jì)算
"scoreAgg": { // 聚合名稱
"stats": { // 聚合類型,這里stats可以計(jì)算min、max、avg等
"field": "score" // 聚合字段,這里是score
}
}
}
}
}
}
聚合結(jié)果如下:
如果我們想要對按照聚合的平均值進(jìn)行排序,那么DSL語句如下:
GET /hotel/_search
{
"size": 0,
"aggs": {
"brandAgg": {
"terms": {
"field": "brand",
"size": 20,
"order": {
"scoreAgg.avg": "asc" //按照平均值進(jìn)行排序
}
},
"aggs": {
"scoreAgg": {
"stats": {
"field": "score"
}
}
}
}
}
}
1.4 使用 RestClient 進(jìn)行聚合
我們以各個(gè)酒店的品牌聚合為例,其中java語句與DSL語句的一一對應(yīng)關(guān)系如下:
使用RestClient進(jìn)行聚合的代碼如下:
@Test
void testAgg() throws IOException {
//1.準(zhǔn)備Request對象
SearchRequest request = new SearchRequest("hotel");
//2.準(zhǔn)備size
request.source().size(0);
//3.進(jìn)行聚合
request.source().aggregation(AggregationBuilders
.terms("brandAgg")
.field("brand")
.size(10));
//4.發(fā)送請求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//解析聚合結(jié)果
Aggregations aggregations = response.getAggregations();
//根據(jù)名稱獲取聚合結(jié)果
Terms brandterms = aggregations.get("brandAgg");
//獲取桶
List<? extends Terms.Bucket> buckets = brandterms.getBuckets();
// 遍歷
for (Terms.Bucket bucket: buckets){
// 獲取key,也就是品牌信息
String brandName = bucket.getKeyAsString();
System.out.println(brandName);
}
}
結(jié)果如下:
2. 自動(dòng)補(bǔ)全
2.1 安裝補(bǔ)全包
自動(dòng)補(bǔ)全我們需要實(shí)現(xiàn)的效果是當(dāng)我們輸入拼音的時(shí)候,就有一些產(chǎn)品的提示,這種情況下就需要我們對拼音有一定的處理,所以我們在這里下載一個(gè)拼音分詞器,下載的方式與上面下載 IK
分詞器相差不大,都是首先進(jìn)入容器內(nèi)部,然后在容器插件目錄下進(jìn)行安裝,
# 進(jìn)入容器內(nèi)部
docker exec -it es /bin/bash
# 在線下載并安裝
/usr/share/elasticsearch/bin/elasticsearch-plugin install --batch \
https://github.com/medcl/elasticsearch-analysis-pinyin/releases/download/v7.12.1/elasticsearch-analysis-pinyin-7.12.1.zip
#退出
exit
#重啟容器
docker restart es
重啟后,使用拼音分詞器試試效果,如下:
POST /_analyze
{
"text": ["你干嘛哎喲"],
"analyzer": "pinyin"
}
2.2 自定義分詞器
從上面的例子我們可以看出,拼音分詞器是將一句話的每個(gè)字都進(jìn)行分開,并且首字母的拼音全部都在一起的,這肯定不是我們想要看到的,我們想要的是對句子進(jìn)行分詞后還能根據(jù)詞語來創(chuàng)建拼音的索引,所以,這就需要我們自定義分詞器了。
首先,我們需要了解分詞器的工作步驟,elasticsearch中分詞器(analyzer)的組成包含三部分:
- character filters:在tokenizer之前對文本進(jìn)行處理。例如刪除字符、替換字符
- tokenizer:將文本按照一定的規(guī)則切割成詞條(term)。例如keyword,就是不分詞;還有ik_smart tokenizer
- filter:將tokenizer輸出的詞條做進(jìn)一步處理。例如大小寫轉(zhuǎn)換、同義詞處理、拼音處理等
自定義分詞器的DSL代碼如下:
PUT /test //針對的是test索引庫
{
"settings": {
"analysis": {
"analyzer": { //自定義分詞器
"my_analyzer": { //分詞器名稱
"tokenizer": "ik_max_word",
"filter": "py" //過濾器名稱
}
},
"filter": {
"py": {
"type": "pinyin", //拼音分詞器
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": { //創(chuàng)建的索引庫的映射
"properties": {
"name": {
"type": "text",
"analyzer": "my_analyzer",//創(chuàng)建索引時(shí)的
"search_analyzer": "ik_smart"
}
}
}
}
進(jìn)行測試如下:
POST /test/_doc/1
{
"id": 1,
"name": "下雪"
}
POST /test/_doc/2
{
"id": 2,
"name": "瞎學(xué)"
}
GET /test/_search
{
"query": {
"match": {
"name": "武漢在下雪嘛"
}
}
}
查詢結(jié)果如下:
2.3 自動(dòng)補(bǔ)全查詢
elasticsearch提供了 Completion Suggester
查詢來實(shí)現(xiàn)自動(dòng)補(bǔ)全功能。這個(gè)查詢會匹配以用戶輸入內(nèi)容開頭的詞條并返回。為了提高補(bǔ)全查詢的效率,對于文檔中字段的類型有一些約束:
- 參與補(bǔ)全查詢的字段必須是
completion
類型。 - 字段的內(nèi)容一般是用來補(bǔ)全的多個(gè)詞條形成的數(shù)組。
首先我們先建立索引庫以及索引庫約束,
PUT test2
{
"mappings": {
"properties": {
"title":{
"type": "completion"
}
}
}
}
在 test2
索引庫中添加數(shù)據(jù)
// 示例數(shù)據(jù)
POST test2/_doc
{
"title": ["Sony", "WH-1000XM3"]
}
POST test2/_doc
{
"title": ["SK-II", "PITERA"]
}
POST test2/_doc
{
"title": ["Nintendo", "switch"]
}
進(jìn)行自動(dòng)補(bǔ)全查詢,我們給出一個(gè)關(guān)鍵字 s
,對其進(jìn)行補(bǔ)全查詢?nèi)缦拢?/p>
GET /test2/_search
{
"suggest": {
"title_suggest": {
"text": "s", // 關(guān)鍵字
"completion": {
"field": "title", // 補(bǔ)全查詢的字段
"skip_duplicates": true, // 跳過重復(fù)的
"size": 10 // 獲取前10條結(jié)果
}
}
}
}
查詢結(jié)果如下:
2.4 拼音自動(dòng)補(bǔ)全查詢
如果想要使用拼音自動(dòng)補(bǔ)全進(jìn)行查詢,那么就必須自定義分詞器了,自定義分詞器如下:
PUT /test3
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
},
"completion_analyzer": {
"tokenizer": "keyword",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"title": {
"type": "completion",
"analyzer": "completion_analyzer"
}
}
}
}
然后創(chuàng)建一個(gè)索引庫,并添加一些文檔,如下:
PUT /test3
{
"mappings": {
"properties": {
"title":{
"type": "completion"
}
}
}
}
POST test3/_doc
{
"title": ["上子", "熵字", "下雪"]
}
POST test3/_doc
{
"title": ["賞金", "秀色", "獵人"]
}
然后就可以根據(jù)首字母縮寫或者拼音來進(jìn)行查詢了,DSL代碼如下:
GET /test3/_search
{
"suggest": {
"suggestions": {
"text": "xx",
"completion": {
"field": "title",
"skip_duplicates": true,
"size": 10
}
}
}
}
如上,我們輸入的是下雪的拼音縮寫 xx
,進(jìn)行查詢時(shí),結(jié)果如下:
當(dāng)然,也可以對拼音進(jìn)行按順序的補(bǔ)全查詢。
2.5 RestClient 實(shí)現(xiàn)自動(dòng)補(bǔ)全
2.5.1 建立索引
要實(shí)現(xiàn)對索引庫的內(nèi)容進(jìn)行自動(dòng)補(bǔ)全,我們需要重新創(chuàng)建索引庫,我們的索引庫需要多出一個(gè) completion
字段的類型,所以刪除原有的索引庫后創(chuàng)建新的索引庫如下:
DELETE /hotel
PUT /hotel
{
"settings": {
"analysis": {
"analyzer": {
"my_analyzer": {
"tokenizer": "ik_max_word",
"filter": "py"
},
"completion_analyzer": {
"tokenizer": "keyword",
"filter": "py"
}
},
"filter": {
"py": {
"type": "pinyin",
"keep_full_pinyin": false,
"keep_joined_full_pinyin": true,
"keep_original": true,
"limit_first_letter_length": 16,
"remove_duplicated_term": true,
"none_chinese_pinyin_tokenize": false
}
}
}
},
"mappings": {
"properties": {
"id": {
"type": "keyword"
},
"name": {
"type": "text",
"analyzer": "ik_max_word",
"copy_to": "all"
},
"address": {
"type": "keyword",
"index": false
},
"price": {
"type": "integer"
},
"score": {
"type": "integer"
},
"brand": {
"type": "keyword",
"copy_to": "all"
},
"city": {
"type": "keyword"
},
"starName": {
"type": "keyword"
},
"bussiness": {
"type": "keyword",
"copy_to": "all"
},
"location": {
"type": "geo_point"
},
"pic": {
"type": "keyword",
"index": false
},
"all": {
"type": "text",
"analyzer": "ik_max_word"
},
"suggestion": {
"type": "completion",
"analyzer": "completion_analyzer"
}
}
}
}
2.5.2 修改數(shù)據(jù)定義
除此之外,還需要將 Hotel
的定義進(jìn)行修改,因?yàn)樽詣?dòng)補(bǔ)全的字段不止一個(gè)字段,所以我們使用列表類型,定義如下:
@Data
@NoArgsConstructor
@ToString
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
private List<String> suggestion;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
this.suggestion = Arrays.asList(this.brand, this.business);
}
}
以上定義就是將 brand
和 business
都進(jìn)行補(bǔ)全的數(shù)據(jù)結(jié)構(gòu)定義。
之后再按照之前的批量添加將數(shù)據(jù)庫中的數(shù)據(jù)進(jìn)行批量新增即可。
2.5.3 補(bǔ)全查詢
以下是RestClient與DSL語句的一一對應(yīng)關(guān)系,
補(bǔ)全查詢的語句如下:
@Test
void testSuggest() throws IOException{
//1. 準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//2. 準(zhǔn)備DSL
request.source().suggest(new SuggestBuilder().addSuggestion(
"suggestions",
SuggestBuilders.completionSuggestion("suggestion")
.prefix("hu")
.skipDuplicates(true)
.size(10)
));
//3. 發(fā)起請求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//4. 輸出結(jié)果
System.out.println(response);
}
2.5.4 解析結(jié)果
輸出的查詢結(jié)果是一個(gè)包含很多形式的信息的Map類型,我們需要對其進(jìn)行解析,獲取其中想要的結(jié)果才行,解析的語句與DSL查詢的結(jié)果對應(yīng)關(guān)系如下:
解析的結(jié)果的語句如下:文章來源:http://www.zghlxwxcb.cn/news/detail-708924.html
@Test
void testSuggest() throws IOException{
//1. 準(zhǔn)備Request
SearchRequest request = new SearchRequest("hotel");
//2. 準(zhǔn)備DSL
request.source().suggest(new SuggestBuilder().addSuggestion(
"suggestions",
SuggestBuilders.completionSuggestion("suggestion")
.prefix("hu")
.skipDuplicates(true)
.size(10)
));
//3. 發(fā)起請求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
//4. 解析結(jié)果
Suggest suggest = response.getSuggest();
//4.1 根據(jù)補(bǔ)全查詢名稱,獲取補(bǔ)全結(jié)果
CompletionSuggestion suggestions = suggest.getSuggestion("suggestions");
//4.2 獲取options
List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
//4.3 遍歷
for (CompletionSuggestion.Entry.Option option : options) {
String text = option.getText().toString();
System.out.println(text);
}
}
處理后的結(jié)果輸出如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-708924.html
到了這里,關(guān)于SpringCloud(十)——ElasticSearch簡單了解(三)數(shù)據(jù)聚合和自動(dòng)補(bǔ)全的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!