ElasticSearch查詢(二)

来源:https://www.cnblogs.com/sss4/archive/2022/07/04/16435496.html
-Advertisement-
Play Games

前言 上文介紹了ES的各種查詢; 本文介紹如何在ES進行MySQL中的分組和聚合查詢 實現用戶輸入拼音自動補全功能 實現MySQL和ES之間的數據自動同步; 一、分組聚合 在ES中對於聚合查詢,主要分為2大類:指標(Metric)聚合 與 桶(Bucket)聚合。 指標聚合:max、min、sum等 ...


前言

上文介紹了ES的各種查詢;

本文介紹如何在ES進行MySQL中的分組和聚合查詢

實現用戶輸入拼音自動補全功能

實現MySQL和ES之間的數據自動同步;

一、分組聚合

在ES中對於聚合查詢,主要分為2大類:指標(Metric)聚合 與 桶(Bucket)聚合。

  • 指標聚合:max、min、sum等,作用等同於Mysql中的相關聚合函數。
  • 桶聚合:group by,作用等同於Mysql中根據哪1個欄位進行分組

註意,我們不能對text類型的欄位進行分組,因為text會進行分詞,導致無法進行分組。

 

1.指標聚合(聚合函數)

指標聚合相當於MySQL中聚合函數,統計品牌為萬豪的最貴酒店價格

GET /hotel/_search
{
  "query": {
    "term": {
      "brand": {
        "value": "萬豪"
      }
    }
  },
  "size": 0, 
  "aggs": {
    "最貴的": {
      "max": {
        "field": "price"
      }
    },
     "最便宜的": {
      "min": {
        "field": "price"
      }
    }
  }
}

 

2.桶聚合(分組)

桶聚合相當於MySQL中的分組,統計品牌為萬豪的酒店有哪些星級;

GET /hotel/_search
{
  "size": 0,
  "query": {
    "term": {
      "brand": {
        "value": "萬豪"
      }
    }
  },
  "aggs": {
    "按星級名稱分組": {
      "terms": {
        "field": "specs",
        "size": 20
      }
    }
  }
  
}

 對資料庫中所有數據,按照星級和品牌分組;

GET /hotel/_search
{
  "size": 0, 
  "aggs": {
    "按品牌分組": {
      "terms": {
        "field": "brand",
        "size": 20
      }
    },
    "按星級分組": {
      "terms": {
        "field": "specs",
        "size": 20
      }
    }
  }
}

3.總結

 在ES中1次請求,可以寫多個聚合函數;

4.功能實現

根據搜索條件篩選之後,再根據品牌進行分組;

4.1.Kibana查詢

根據搜索條件對品牌進行數據分組

GET hotel/_search
{
  "size": 0, 
  "query": {
    "query_string": {
      "fields": ["name","synopsis","area","address"],
      "query": "三亞 OR 商務"
    }
  },
  "aggs": {
    "hotel_brands": {
      "terms": {
        "field": "brand",
        "size": 100
      }
    }
  }
}

4.2.JavaAPI查詢

@Override
    public Map<String, Object> searchBrandGroupQuery(Integer current, Integer size, Map<String, Object> searchParam) {
        //設置查詢請求頭
        SearchRequest searchRequest = new SearchRequest("hotel");
        //設置查詢請求體
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        //設置查詢方式
        if (!StringUtils.isEmpty(searchParam.get("condition"))) {
            QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
                    .field("name")
                    .field("synopsis")
                    .field("area")
                    .field("address")
                    .defaultOperator(Operator.OR);
            searchSourceBuilder.query(queryBuilder);
        }
        //設置按品牌分組
        AggregationBuilder aggregationBuilder = AggregationBuilders.terms("brand_groups")
                .size(200)
                .field("brand");
        searchSourceBuilder.aggregation(aggregationBuilder);

        //設置分頁
        searchSourceBuilder.from((current - 1) * size);
        searchSourceBuilder.size(size);
        searchRequest.source(searchSourceBuilder);
        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits hits = searchResponse.getHits();
            long totalHits = hits.getTotalHits().value;
            ArrayList<String> groupNameList = new ArrayList<>();
            //獲取並處理聚合查詢結果
            Terms brandGroups = searchResponse.getAggregations().get("brand_groups");
            for (Terms.Bucket bucket : brandGroups.getBuckets()) {
                String key = (String) bucket.getKey();
                groupNameList.add(key);
            }

            Map<String, Object> map = new HashMap<>();
//            map.put("list", list);
            map.put("totalResultSize", totalHits);
            map.put("current", current);
            //設置總頁數
            map.put("totalPage", (totalHits + size - 1) / size);
            //設置品牌分組列表
            map.put("brandList", groupNameList);
            return map;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }
HotelServiceImpl.java

 

5.分組和聚合一起使用

通常情況我們統計數據時,會先進行分組,然後再在分組的基礎上進行聚合操作

根據用戶輸入的日期,統計某品牌下所有酒店銷量。 對於該功能的實現,需要進行多層聚合。

  • 對日期時間段範圍查詢
  • 根據品牌進行分組查詢
  • 對分組查詢結果進行sum聚合

5.1.Kibana查詢

在桶聚合中嵌套指標聚合,就等於MySQL會先進行分組操作,然後再在數據分組的基礎上,進行聚合函數統計;

GET hotel/_search
{
  "size": 0,
  "query": {
    "range": {
      "createTime": {
        "gte": "2015-01-01",
        "lte": "2015-12-31"
      }
    }
  },
  "aggs": {
    "根據品牌分組": {
      "terms": {
        "field": "brand",
        "size": 100
      },
      "aggs": {
        "該品牌總銷量": {
          "sum": {
            "field": "salesVolume"
          }
        },
        "該品牌銷量平均值": {
          "avg": {
            "field": "salesVolume"
          }
        }
      }
    }
  }
}

5.2.JavaAPI查詢

public List<Map<String, Object>> searchDateHistogram(Map<String, Object> searchParam) {

    //定義結果集
    List<Map<String, Object>> result = new ArrayList<>();

    //設置查詢
    SearchRequest searchRequest = new SearchRequest("hotel");

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    //todo 自定義日期時間段範圍查詢
    RangeQueryBuilder queryBuilder = QueryBuilders.rangeQuery("createTime")
        .gte(searchParam.get("minTime"))
        .lte(searchParam.get("maxTime"))
        .format("yyyy-MM-dd");
    searchSourceBuilder.query(queryBuilder);

    //todo 聚合查詢設置
    TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("hotel_brand").field("brand").size(100);

    //構建二級聚合
    SumAggregationBuilder secondAggregation = AggregationBuilders.sum("hotel_salesVolume").field("salesVolume");
    aggregationBuilder.subAggregation(secondAggregation);

    searchSourceBuilder.aggregation(aggregationBuilder);


    searchRequest.source(searchSourceBuilder);
    try {

        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        //todo 獲取聚合結果並處理
        Aggregations aggregations = searchResponse.getAggregations();
        Map<String, Aggregation> aggregationMap = aggregations.asMap();
        Terms terms = (Terms) aggregationMap.get("hotel_brand");
        List<? extends Terms.Bucket> buckets = terms.getBuckets();
        buckets.forEach(bucket -> {

            Map<String, Object> info = new HashMap<>();
            info.put("brand",bucket.getKeyAsString());

            //獲取二級聚合數據
            ParsedSum parsedSum = bucket.getAggregations().get("hotel_salesVolume");
            Integer sumValue = (int) parsedSum.getValue();
            info.put("sumValue",sumValue);

            result.add(info);
        });

        return result;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
HotelServiceImpl.java

 

二、權重搜索

複合(compound)查詢:複合查詢可以將其它簡單查詢組合起來,實現更複雜的搜索邏輯。常見的有兩種:

  • fuction score:算分函數查詢,可以控制文檔相關性算分,控制文檔排名

  • bool query:布爾查詢,利用邏輯關係組合多個其它的查詢,實現複雜搜索

1.相關性算分

當我們利用match查詢時,文檔結果會根據與搜索詞條的關聯度打分(_score),返回結果時按照分值降序排列。

例如,我們搜索 "北京市東城區萬豪",結果如下:

GET hotel/_search
{
  "query": {
    "match": {
      "name": "北京市東城區萬豪"
    }
  }
}

#結果
[
  {
    "_score" : 7.060467,
    "_source" : {
      "name" : "北京市東城區萬豪酒店",
    }
  },
  {
    "_score" : 7.060467,
    "_source" : {
      "name" : "北京市東城區金陵酒店",
    }
  },
  {
    "_score" : 7.060467,
    "_source" : {
      "name" : "北京市東城區華天酒店",
    }
  }
]

在ElasticSearch中,早期使用的打分演算法是TF-IDF演算法,公式如下:

 

 在後來的5.1版本升級中,elasticsearch將演算法改進為BM25演算法,公式如下:

 

 

 TF-IDF演算法有一各缺陷,就是詞條頻率越高,文檔得分也會越高,單個詞條對文檔影響較大。而BM25則會讓單個詞條的算分有一個上限,曲線更加平滑:

小結:

elasticsearch會根據詞條和文檔的相關度做打分,演算法由兩種:

  • TF-IDF演算法

  • BM25演算法,elasticsearch5.1版本後採用的演算法

 

2.人為控制相關性算分

當進行數據搜索時,但是有時需要將一些特定的商品排名更加靠前(競價排名),以百度為例:

2.1.權重介紹

當搜索時,對於每條搜索結果都會有一個打分,匹配度越高,則排名越靠前

#查詢多域展示相關結果數據
GET hotel/_search
{
  "query": {
    "query_string": {
    "fields": ["name","synopsis","area","address"],
    "query": "北京市萬豪spa三星"
    }
  }

查詢結果

根據查詢結果可以看出,每條搜索結果都是會_score欄位;

該欄位代表搜索結果的得分,搜索結果越貼近搜索條件,則分值越高,排名越靠前。

如要想將分數值設置的更高,則可以通過權重來進行改變。

2.2.權重設置

我們可以通過3種方式設置每1條查詢結果的的權重;

2.2.1.boost設置(內捲)

在查詢的時候給每1條數據的權重進行加分操作,但是沒有用因為每1條數據都漲了(內捲),無法實現競價排名;

GET hotel/_search
{
  "query": {
    "query_string": {
    "fields": ["name","synopsis","area","address"],
    "query": "北京市萬豪spa三星",
    "boost": 50
    }
  }
}

查詢結果

2.2.2.索引設置(靜態)

在創建索引時,指定欄位的配置權重;

該方式在開發中不常用,因為隨著業務的改變,無法隨時調整權重;

而索引一旦創建則無法修改,除非刪除索引重建。

PUT hotel
{
  "mappings": {
    "properties": { 
      "name":{
        "type": "text",
        "analyzer": "ik_max_word",
        "boost": 5
      },
      "address":{
        "type": "text",
        "analyzer": "ik_max_word",
        "boost": 3
      }
    }
  }
}

2.2.3.查詢設置(動態)

該方式在開發中很常用,根據業務條件需求,在查詢時靈活的配置權重。

在下列查詢中,query中的內容為主查詢條件,functions中為判斷要為哪些數據加權。weight為加權值。

假設x豪掏了告費用,那我就為品牌為x豪的酒店,權重值增加50倍;

GET hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "query_string": {
        "fields": ["name","synopsis","area","address"],
        "query": "北京市spa三星"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "brand": "x豪"
            }
          },
          "weight": 50
        }
      ]
    }
  }
}

查詢結果

 

3.Kibana查詢

對搜索結果中,判定是廣告的商品,加權100倍。

GET hotel/_search
{
  "query": {
    "function_score": {
      "query": {
        "query_string": {
          "fields": [
            "name",
            "specs",
            "area"
          ],
          "query": "北京市萬豪sap三星"
        }
      },
      "functions": [
        {
          "filter": {
            "term": {
              "isAd": "1"
            }
          },
          "weight": 100
        }
      ]
    }
  }
}

4.JavaAPI查詢

public Map<String, Object> searchScoreQuery(Integer current, Integer size, Map<String, Object> searchParam) {

    SearchRequest searchRequest = new SearchRequest("hotel");

    SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

    //構建主查詢條件
    QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(searchParam.get("condition").toString())
        .field("name")
        .field("synopsis")
        .field("area")
        .field("address")
        .defaultOperator(Operator.OR);

    //構建加權條件
    FunctionScoreQueryBuilder.FilterFunctionBuilder[] scoreFunctionBuilder = new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{
        new FunctionScoreQueryBuilder.FilterFunctionBuilder(QueryBuilders.termQuery("isAd",1), ScoreFunctionBuilders.weightFactorFunction(100))
    };

    FunctionScoreQueryBuilder queryBuilder = QueryBuilders.functionScoreQuery(queryStringQueryBuilder, scoreFunctionBuilder);
    searchSourceBuilder.query(queryBuilder);


    //設置分頁
    searchSourceBuilder.from((current - 1) * size);
    searchSourceBuilder.size(size);

    searchRequest.source(searchSourceBuilder);

    try {
        //處理查詢結果
        SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

        SearchHits hits = searchResponse.getHits();

        long totalHits = hits.getTotalHits().value;

        SearchHit[] searchHits = hits.getHits();

        List<HotelEntity> list = new ArrayList<>();

        for (SearchHit searchHit : searchHits) {
            String sourceAsString = searchHit.getSourceAsString();
            list.add(JSON.parseObject(sourceAsString, HotelEntity.class));
        }

        Map<String, Object> map = new HashMap<>();
        map.put("list", list);
        map.put("totalResultSize", totalHits);
        map.put("current", current);
        //設置總頁數
        map.put("totalPage", (totalHits + size - 1) / size);

        return map;
    } catch (IOException e) {
        e.printStackTrace();
    }
    return null;
}
HotelServiceImpl.java

 

三、自動補全

當用戶在搜索框輸入拼音字元時,也可以提示出與該拼音字元有關的搜索項,如圖:

這種根據用戶輸入的字母,提示完整詞條的功能,就是自動補全了

因為需要根據拼音字母來推斷,因此要用到拼音分詞功能。

1. copy_to屬性

當我們在做query_string查詢的時候 關聯多個欄位查詢;

PUT user
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "text"
      },
      "last_name": {
        "type": "text"
      } 
    }
  }
}
#添加數據
PUT user/_doc/1
{
  "first_name": "John",
  "last_name": "Smith"
}

#查詢
GET user/_search
{
  "query": {
    "query_string": {
      "fields": ["first_name","last_name"],
      "query": "John OR Smith"
    }
  }
}

我們可以利用copy_to屬性完成將多個欄位,合併拷貝到一個欄位中簡化查詢;

這是典型的空間換時間操作;

DELETE user

PUT user
{
  "mappings": {
    "properties": {
      "first_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "last_name": {
        "type": "text",
        "copy_to": "full_name" 
      },
      "full_name": {
        "type": "text"
      }
    }
  }
}


PUT user/_doc/1
{
  "first_name": "John",
  "last_name": "Smith"
}

#用match當做單欄位查詢
GET user/_search
{
  "query": {
    "match": {
      "full_name": { 
        "query": "John Smith",
        "operator": "and"
      }
    }
  }
}

1.2.總結

  • copy_to屬性可以幫助我們將多個欄位或者一個欄位拷貝到另外一個欄位
  • copy_to屬性可以幫助我們簡化查詢
  • copy_to屬性可以幫助我們提高查詢速度

2.拼音分詞器

要實現根據字母做補全,就必須對文檔按照拼音分詞。

在GitHub上有elasticsearch的拼音分詞插件。地址:https://github.com/medcl/elasticsearch-analysis-pinyin

安裝方式與IK分詞器一樣,分三步:

2.1.解壓

2.2.上傳到ES的插件目錄下

[root@zhanggen plugins]# ls
elasticsearch-analysis-ik-7.10.1  elasticsearch-analysis-pinyin-7.10.1
[root@zhanggen plugins]# pwd
/mydata/elasticsearch/plugins

2.3.重啟es容器

 

2.4.測試拼音分詞器

POST /_analyze
{
  "text": "張根",
  "analyzer": "pinyin"
}

2.5.測試結果

{
  "tokens" : [
    {
      "token" : "zhang",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "zg",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 0
    },
    {
      "token" : "gen",
      "start_offset" : 0,
      "end_offset" : 0,
      "type" : "word",
      "position" : 1
    }
  ]
}

3.自定義分詞器

現有ES已經有兩個分詞器(IK+pinyin)了,那麼如何在創建索引映射定義欄位的時候,怎麼同時使用2個分詞器呢?

這就需要自定義分詞器,把兩個分詞器合二為一;

 

3.1.聲明自定義分詞器 

聲明自定義分詞器的語法如下:

PUT 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": {
    "properties": {
      "name": {
        "type": "text",
        "analyzer": "my_analyzer",
        "search_analyzer": "ik_smart"
      }
    }
  }
}

POST test/_analyze
{
  "text": "張根",
  "analyzer": "my_analyzer"
}

3.2.查看分詞結果

{
  "tokens" : [
    {
      "token" : "張",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "zhang",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "z",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "根",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "gen",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "g",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    }
  ]
}

4.自動補全

Elasticsearch提供了Completion Suggester查詢來實現自動補全功能。

這個查詢會匹配以用戶輸入內容開頭的詞條並返回。為了提高補全查詢的效率,對於文檔中欄位的類型有一些約束:

  • 參與補全查詢的欄位必須是completion類型

  • 欄位的內容一般是用來補全的多個詞條形成的數組。

比如,一個這樣的索引(表):

PUT test
{
  "mappings": {
    "properties": {
      "title": {
        "type": "completion"
      }
    }
  }
}

然後插入下麵的數據

#示例數據
POST test/_doc
{
  "title": [
    "Sony",
    "WH-1000XM3"
  ]
}
POST test/_doc
{
  "title": [
    "SK-II",
    "PITERA"
  ]
}
POST test/_doc
{
  "title": [
    "Nintendo",
    "switch"
  ]
}

查詢的DSL語句如下

#自動補全
GET test/_search
{
  "suggest": {
    "YOUR_SUGGESTION": {
      "text": "s",
      "completion": {
        "field": "title",
        "skip_duplicates":true,
        "size":10
      }
    }
  }
}

 

5.自動補全酒店信息

重建ES中索引(表)以支持自動補全功能;

5.1.重新創建1個酒店的索引(表)

  • 1.定義分詞器
  • 2.創建suggest欄位
  • 3.將name和brand欄位 拷貝到suggest欄位里去
# 酒店數據索引庫
PUT hotel_3
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "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": {
      "suggest":{
        "type": "completion",
        "analyzer": "completion_analyzer"
      },
      "address" : {
          "type" : "text",
          "analyzer" : "text_anlyzer",
          "search_analyzer" : "ik_smart"
        },
        "area" : {
          "type" : "text",
          "analyzer" : "text_anlyzer",
          "search_analyzer" : "ik_smart"
        },
        "brand" : {
          "type" : "keyword",
          "copy_to": "suggest"
        },
        "createTime" : {
          "type" : "date",
          "format" : "yyyy-MM-dd"
        },
        "id" : {
          "type" : "long"
        },
        "imageUrl" : {
          "type" : "text"
        },
        "isAd" : {
          "type" : "integer"
        },
        "name" : {
          "type" : "text",
          "analyzer" : "text_anlyzer",
          "search_analyzer" : "ik_smart",
          "copy_to": "suggest"
        },
        "price" : {
          "type" : "integer"
        },
        "salesVolume" : {
          "type" : "integer"
        },
        "specs" : {
          "type" : "keyword"
        },
        "synopsis" : {
          "type" : "text",
          "analyzer" : "text_anlyzer",
          "search_analyzer" : "ik_smart"
        },
        "type" : {
          "type" : "keyword"
        }
    }
  }
}

5.2.向新建的索引(表)中遷移數據

#平滑遷移數據
POST _reindex?wait_for_completion=false&requests_per_second=200
{
  "source": {
    "index": "hotel_2"
  },
  "dest": {
    "index":"hotel_3"
  }
}
#檢查任務狀態
GET _tasks/_6af5BFpS7mrvRyP6f8xlg:6792

#重新指向別名
#斷開原來的關係
POST _aliases
{
  "actions": [
    {
      "remove": {
        "index": "hotel_2",
        "alias": "hotel"
      }
    }
  ]
}
#刪除原來的索引表
DELETE hotel_2

#新建hotel_2的關係
POST _aliases
{
  "actions": [
    {
      "add": {
        "index": "hotel_3",
        "alias": "hotel"
      }
    }
  ]
}

5.3.Kibana查詢

模擬用戶輸入了1個拼音wan

GET hotel/_search
{
  "_source": false, 
  "suggest": {
    "my_suggest": {
      "text": "wan",
      "completion": {
        "field": "suggest",
        "skip_duplicates":true,
        "size":10
      }
    }
  }
}

5.4.查看結果

查到了萬事達、萬豪、王朝

{
  "took" : 2,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 0,
      "relation" : "eq"
    },
    "max_score" : null,
    "hits" : [ ]
  },
  "suggest" : {
    "my_suggest" : [
      {
        "text" : "wan",
        "offset" : 0,
        "length" : 3,
        "options" : [
          {
            "text" : "萬事達",
            "_index" : "hotel_3",
            "_type" : "_doc",
            "_id" : "AeSfyIEBhlAS7ARu8P7t",
            "_score" : 1.0
          },
          {
            "text" : "萬悅",
            "_index" : "hotel_3",
            "_type" : "_doc",
            "_id" : "_uSfyIEBhlAS7ARu8P3t",
            "_score" : 1.0
          },
          {
            "text" : "萬豪",
            "_index" : "hotel_3",
            "_type" : "_doc",
            "_id" : "wuSfyIEBhlAS7ARu8P3t",
            "_score" : 1.0
          },
          {
            "text" : "王朝",
            "_index" : "hotel_3",
            "_type" : "_doc",
            "_id" : "1eSfyIEBhlAS7ARu8P3t",
            "_score" : 1.0
          }
        ]
      }
    ]
  }
}

5.5.JavaAPI查詢

public List<String> searchSuggestInfo(String key) {

        //定義結果集
        List<String> result = new ArrayList<>();

        //設置查詢
        SearchRequest searchRequest = new SearchRequest("hotel");
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();

        //todo 構建自動補全搜索
        searchSourceBuilder.fetchSource(false);
        SuggestBuilder suggestBuilder = new SuggestBuilder();
        CompletionSuggestionBuilder suggest = SuggestBuilders
                        .completionSuggestion("suggest")
                        .prefix(key)
                        .skipDuplicates(true)
                        .size(10);
        suggestBuilder.addSuggestion("my_suggest",suggest);
        searchSourceBuilder.suggest(suggestBuilder);


        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            //處理自動補全查詢結果
            CompletionSuggestion my_suggest = searchResponse.getSuggest().getSuggestion("my_suggest");

            List<CompletionSuggestion.Entry.Option> options = my_suggest.getOptions();
            for (CompletionSuggestion.Entry.Option option : options) {
                String s = option.getText().string();
                result.add(s);
            }

            return result;
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
HotelServiceImpl.java

5.6.效果

 

 

四、自動同步

Elasticsearch中的酒店數據來自於MySQL資料庫;

因此mysql數據發生改變時,Elasticsearch也必須跟著改變,這中機制就是Elasticsearch與MySQL之間的數據同步

 

 

 

 

 

參考


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 在VMware Workstation上新建了一個虛擬機,安裝了Ubuntu 10.04版本的iso,創建好後發現了兩個問題: (1) 虛擬機界面並不能填充滿整個屏幕,使用起來十分反人類。 (2) 主機和虛擬機不能隨意相互複製粘貼,無論是文件還是 ...
  • 鏡像下載、功能變數名稱解析、時間同步請點擊 阿裡雲開源鏡像站 查看私有倉庫有哪些鏡像 如果私有倉庫帶有認證,在使用 curl 命令的時候需要帶上 -u 參數 使用方法: curl -XGET -u <倉庫用戶名>:<用戶名密碼> http://<倉庫ip地址>:<倉庫埠>/v2/_catalog curl ...
  • 一、通配符 匹配參數,匹配文件/目錄名字 : *.txt *.sh lidao{1,4}.txt | * | 所有 | | | | | {} | 生成序列 | | [] | 【a-z】匹配小寫字母,一個中括弧相當於一個字元 | | [^] | 取反排除 | | ? | 任何一個字元 | 1. 通配符 ...
  • Background NGINX 是一個通用且流行的應用程式。也是最流行的 Web 伺服器,它可用於提供靜態文件內容,但也通常與其他服務一起用作分散式系統中的組件,在其中它用作反向代理、負載均衡 或 API 網關。 分散式追蹤 distributed tracing 是一種可用於分析與監控應用程式的 ...
  • 1 存儲引擎 1、簡單描述一個Mysql的內部結構? MySQL的基本架構示意圖: 大體來說,MySQL可以分為server層和存儲引擎層兩部分。 ① server層包括連接器、查詢緩存、分析器、優化器、執行器等,涵蓋MySQL的大多數核心服務功能 ② 存儲引擎層:存儲引擎層負責數據的存儲和提取。其 ...
  • ###@Spark分區器(Partitioner) ####HashPartitioner(預設的分區器) HashPartitioner分區原理是對於給定的key,計算其hashCode,並除以分區的個數取餘,如果餘數小於0,則餘數+分區的個數,最後返回的值就是這個key所屬的分區ID,當key為 ...
  • 一鍵直達直播間 一、直播介紹 上兩期渡劫同學為大家分享了ChunJun數據還原的DDL模塊,想必大家對這一模塊有了比較深入的瞭解,本期無倦同學將會為大家分享ChunJun同步Hive事務表的相關內容,直播將從Hive事務表的結構及原理、ChunJun讀寫Hive事務表實戰、源碼解析及ChunJun文 ...
  • 一. 下載mysql 8.0.29軟體包 下載點我 二. 解壓,初始化安裝 1,打開下載後文件所在目錄,使用解壓軟體解壓,打開文件夾!(如圖,文件路徑不要出現中文!) 2,創建my.ini文件,創建前先開啟文件尾碼名顯示防止文件格式錯誤! 3,右鍵空白處,新建>文本文檔>選中文件>重命名>全選>文件 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...