ElastaticSearch-应用详解

声明: 本博客已标明出处,如有侵权请告知,马上删除。

ES基本操作

ES概述

概述

Elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容

Elasticsearch是专门用于文档搜索的一块搜索引擎.

结构
在这里插入图片描述

ES操作-索引

脚本操作

创建索引库

PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名": {
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2": {
        "type": "keyword",
        "index": "false"
      }
      #...}
  }
}

查询索引库

GET /索引库名

添加新字段

PUT /索引库名/_mapping
{
  "properties": {
    "新字段名":{
      "type": "integer"
    }
  }
}

删除索引库

DELETE /索引库名

Java操作

导入坐标

<!--定义版本-->
<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

<!--elasticsearch-->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

相关代码

@SpringBootTest
public class HotelIndexTest {

    private RestHighLevelClient client;
    
    @BeforeEach
    void setUp() {
        //建立联接
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        //释放联接
        client.close();
    }

    @Test
    void testCreateIndex() throws IOException {
        // 1.准备Request      PUT /hotel
        CreateIndexRequest request = new CreateIndexRequest("hotel");
        // 2.准备请求参数
        request.source("mappings映射的json字符串", XContentType.JSON);
        // 3.发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

    @Test
    void testExistsIndex() throws IOException {
        // 1.准备Request
        GetIndexRequest request = new GetIndexRequest("hotel");
        // 3.发送请求
        boolean isExists = client.indices().exists(request, RequestOptions.DEFAULT);

        System.out.println(isExists ? "存在" : "不存在");
    }

    @Test
    void testDeleteIndex() throws IOException {
        // 1.准备Request
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");
        // 3.发送请求
        client.indices().delete(request, RequestOptions.DEFAULT);
    }   
}

ES操作-文档

脚本操作

添加文档

POST /索引库名/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    "字段3": {
        "子属性1": "值3",
        "子属性2": "值4"
    },
    // ...
}

查询文档

GET /索引库名称/_doc/文档id

修改文档-替换

PUT /索引库名称/_doc/文档id
{
    "字段1": "值1",
    "字段2": "值2",
    // ... 略
}

修改文档-修改

POST /索引库名称/_update/文档id
{
    "doc": {
         "字段名": "新的值",
    }
}

删除文档

DELETE /索引库名称/_doc/文档id

Java操作

导入坐标

<!--定义版本-->
<properties>
    <java.version>1.8</java.version>
    <elasticsearch.version>7.12.1</elasticsearch.version>
</properties>

<!--elasticsearch-->
<dependency>
    <groupId>org.elasticsearch.client</groupId>
    <artifactId>elasticsearch-rest-high-level-client</artifactId>
</dependency>

相关代码

@SpringBootTest
public class HotelDocumentTest {

    private RestHighLevelClient client;

    @Autowired
    private IHotelService hotelService;
    
    
    @BeforeEach
    void setUp() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }

    @Test
    void testAddDocument() throws IOException {
        // 1.查询数据库hotel数据
        Hotel hotel = hotelService.getById(61083L);
        // 2.转换为HotelDoc
        HotelDoc hotelDoc = new HotelDoc(hotel);
        // 3.转JSON
        String json = JSON.toJSONString(hotelDoc);

        // 1.准备Request
        IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
        // 2.准备请求参数DSL,其实就是文档的JSON字符串
        request.source(json, XContentType.JSON);
        // 3.发送请求
        client.index(request, RequestOptions.DEFAULT);
    }

    @Test
    void testGetDocumentById() throws IOException {
        // 1.准备Request      // GET /hotel/_doc/{id}
        GetRequest request = new GetRequest("hotel", "61083");
        // 2.发送请求
        GetResponse response = client.get(request, RequestOptions.DEFAULT);
        // 3.解析响应结果
        String json = response.getSourceAsString();

        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        System.out.println("hotelDoc = " + hotelDoc);
    }

    @Test
    void testDeleteDocumentById() throws IOException {
        // 1.准备Request      // DELETE /hotel/_doc/{id}
        DeleteRequest request = new DeleteRequest("hotel", "61083");
        // 2.发送请求
        client.delete(request, RequestOptions.DEFAULT);
    }

    @Test
    void testUpdateById() throws IOException {
        // 1.准备Request
        UpdateRequest request = new UpdateRequest("hotel", "61083");
        // 2.准备参数
        request.doc(
                "price", "870"
        );
        // 3.发送请求
        client.update(request, RequestOptions.DEFAULT);
    }

    @Test
    void testBulkRequest() throws IOException {
        // 查询所有的酒店数据
        List<Hotel> list = hotelService.list();

        // 1.准备Request
        BulkRequest request = new BulkRequest();
        // 2.准备参数
        for (Hotel hotel : list) {
            // 2.1.转为HotelDoc
            HotelDoc hotelDoc = new HotelDoc(hotel);
            // 2.2.转json
            String json = JSON.toJSONString(hotelDoc);
            // 2.3.添加请求
            request.add(new IndexRequest("hotel").id(hotel.getId().toString()).source(json, XContentType.JSON));
        }

        // 3.发送请求
        client.bulk(request, RequestOptions.DEFAULT);
    }
}

ES复杂搜索

ES搜索-脚本操作

基本语法

GET /索引库名称/_search
{
  "query": {
    "查询类型": {
      "查询条件": "条件值"
    }
  }
}

查询所有

GET /索引库名称/_search
{
  "query": {
    "match_all": {
    }
  }
}

全文检索

match

GET /索引库名称/_search
{
  "query": {
    "match": {
      "列名": "搜索文本"
    }
  }
}

multi_match

GET /索引库名称/_search
{
  "query": {
    "multi_match": {
      "query": "搜索文本",
      "fields": ["列名1", " 列名2"]
    }
  }
}

精确查询

** term查询**

GET /索引库名称/_search
{
  "query": {
    "term": {
      "列名": {
        "value": "值"
      }
    }
  }
}

range查询

GET /索引库名称/_search
{
  "query": {
    "range": {
      "列名": {
        "gte": 小值,  # 这里的gte代表大于等于,gt则代表大于
        "lte": 大值   # lte代表小于等于,lt则代表小于
      }
    }
  }
}

地理查询

区域查询

GET /索引库名称/_search
{
  "query": {
    "geo_bounding_box": {
      "列名": {
        "top_left": { 	# 左上点
          "lat": 31.1,
          "lon": 121.5
        },
        "bottom_right": { # 右下点
          "lat": 30.9,
          "lon": 121.7
        }
      }
    }
  }
}

附近查询

GET /索引库名称/_search
{
  "query": {
    "geo_distance": {
      "distance": "15km", // 半径
      "列名": "31.21,121.5" // 圆心
    }
  }
}

相关性算分

  • 原始查询条件:query部分,基于这个条件搜索文档,并且基于BM25算法给文档打分,原始算分(query score)
  • 过滤条件:filter部分,符合该条件的文档才会重新算分
  • 算分函数:符合filter条件的文档要根据这个函数做运算,得到的函数算分(function score),有四种函数
    • weight:函数结果是常量
    • field_value_factor:以文档中的某个字段值作为函数结果
    • random_score:以随机数作为函数结果
    • script_score:自定义算分函数算法
  • 运算模式:算分函数的结果、原始查询的相关性算分,两者之间的运算方式,包括:
    • multiply:相乘
    • replace:用function score替换query score
    • 其它,例如:sum、avg、max、min
GET /索引库名称/_search
{
  "query": {
    "function_score": {
      "query": { .... }, 	# 原始查询,可以是任意条件
      "functions": [ 		# 算分函数
        {
          "filter": { 		# 满足的条件,品牌必须是如家
            "term": {
              "brand": "如家"
            }
          },
          "weight": 2 		# 算分函数,算分权重为2
        }
      ],
      "boost_mode": "sum" 	# 运算模式,求和
    }
  }
}

复合查询

布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分
GET /hotel/_search
{
  "query": {
    "bool": {
      "must": [
        {"term": {"city": "上海" }}
      ],
      "should": [
        {"term": {"brand": "皇冠假日" }},
        {"term": {"brand": "华美达" }}
      ],
      "must_not": [
        { "range": { "price": { "lte": 500 } }}
      ],
      "filter": [
        { "range": {"score": { "gte": 45 } }}
      ]
    }
  }
}

ES搜索-Java操作

查询模版

package cn.jd.hotel;

import cn.jd.hotel.pojo.HotelDoc;
import com.alibaba.fastjson.JSON;
import org.apache.http.HttpHost;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestClient;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.io.IOException;
import java.util.Map;

@SpringBootTest
public class HotelSearchTest {

    private RestHighLevelClient client;
    
    @BeforeEach
    void setUp() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://192.168.150.101:9200")
        ));
    }

    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }
    
    /**
     * 查询
     * @throws IOException
     */
    public void testESQuery(QueryBuilder query) throws IOException {
        // 1.准备request
        SearchRequest request = new SearchRequest("hotel");
        // 2.准备请求参数
        request.source().query(query);
        // 3.发送请求,得到响应
        SearchResponse response = client.search(request, RequestOptions.DEFAULT);
        // 4.结果解析
        handleResponse(response);
    }

   /**
     * 处理结果
     * @param response 查询到的结果
     */
    private void handleResponse(SearchResponse response) {
        SearchHits searchHits = response.getHits();
        // 4.1.总条数
        long total = searchHits.getTotalHits().value;
        System.out.println("总条数:" + total);
        // 4.2.获取文档数组
        SearchHit[] hits = searchHits.getHits();
        // 4.3.遍历
        for (SearchHit hit : hits) {
            // 4.4.获取source
            String json = hit.getSourceAsString();
            // 4.5.反序列化,非高亮的
            HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
            // 4.6.打印
            System.out.println(hotelDoc);
        }
    }
}

查询所有

@Test
public void testMatchAll() throws IOException {
    testESQuery(QueryBuilders.matchAllQuery());
}

全文检索

match

@Test
public void testMatch() throws IOException {
 	MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("列名", "搜索文本");
    testESQuery(matchQueryBuilder);
}

multi_match

@Test
public void testMultiMatch() throws IOException {
 	MultiMatchQueryBuilder mq = QueryBuilders.multiMatchQuery("搜索文本", "列名1", "列名2");
    testESQuery(mq);
}

精确查询

term查询

@Test
public void testTerm() throws IOException {
 	TermQueryBuilder tq = QueryBuilders.termQuery("列名", "值");    
    testESQuery(tq);
}

range查询

@Test
public void testTerm() throws IOException {
 	RangeQueryBuilder rq = QueryBuilders.rangeQuery("列名").gte("小值").lte("大值");
    testESQuery(rq);
}  

地理查询

区域查询

@Test
public void testGeoBoundingBox() throws IOException {
    GeoBoundingBoxQueryBuilder gbq = QueryBuilders.geoBoundingBoxQuery("列名")
        .setCorners("左上角维度,左上角经度", "右下角维度,右下角经度");
    testESQuery(gbq);
}

附近查询

@Test
public void testGeoDistance() throws IOException {
    GeoDistanceQueryBuilder dq = QueryBuilders.geoDistanceQuery("列名")
        .distance("15", DistanceUnit.KILOMETERS)
        .point(new GeoPoint("维度,经度"));
    testESQuery(dq);
}

相关性算分

@Test
public void testScoreQuery() throws IOException {
    //1.准备基础查询
 	MatchQueryBuilder matchQueryBuilder = QueryBuilders.matchQuery("列名", "搜索文本");
    // 2.算分函数查询
    FunctionScoreQueryBuilder fq = QueryBuilders.functionScoreQuery(
        matchQueryBuilder, // 原始查询,matchQueryBuilder
        new FunctionScoreQueryBuilder.FilterFunctionBuilder[]{ // function数组
            new FunctionScoreQueryBuilder.FilterFunctionBuilder(
                QueryBuilders.termQuery("isAD", true), // 过滤条件
                ScoreFunctionBuilders.weightFactorFunction(10) // 算分函数
            )
        }
    );
    testESQuery(fq);
}

复合查询

布尔查询是一个或多个查询子句的组合,每一个子句就是一个子查询。子查询的组合方式有:

  • must:必须匹配每个子查询,类似“与”
  • should:选择性匹配子查询,类似“或”
  • must_not:必须不匹配,不参与算分,类似“非”
  • filter:必须匹配,不参与算分
@Test
public void testTerm() throws IOException {
 	BoolQueryBuilder bq = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery("city", "上海"))
                .should(QueryBuilders.termQuery("brand","皇冠假日"))
                .should(QueryBuilders.termQuery("brand","华美达"))
                .mustNot(QueryBuilders.rangeQuery("price").lte(500))
                .filter(QueryBuilders.rangeQuery("score").gte(45));
    testESQuery(bq);
} 

ES结果处理

ES结果处理-脚本操作:

多个结果处理可以并用.

结果排序

普通字段排序

GET /indexName/_search
{
  "query": {
    "match_all": {}		#查询条件,可以是任意条件
  },
  "sort": [
    {
      "列名": "desc"  	#排序字段、排序方式ASC、DESC
    }
  ]
}

地理位置排序

GET /indexName/_search
{
  "query": {
    "match_all": {}		#查询条件,可以是任意条件
  },
  "sort": [
    {
      "_geo_distance" : {
          "列名" : "纬度,经度", 	#文档中geo_point类型的字段名、目标坐标点
          "order" : "asc", 		#排序方式
          "unit" : "km" 		#排序的距离单位
      }
    }
  ]
}

结果分页

GET /hotel/_search
{
  "query": {
    "match_all": {}		#查询条件,可以是任意条件
  },
  "from": 0, 			# 分页开始的位置,默认为0
  "size": 10, 			# 期望获取的文档总数
  "sort": [
    {"列名": "asc"}	#排序
  ]
}

结果高亮

所谓高亮查询,其实就是在查询的关键字前边添加前缀,在关键字的后边添加后缀

注意:

  • 高亮是对关键字高亮,因此搜索条件必须带有关键字,而不能是范围这样的查询。
  • 默认情况下,高亮的字段,必须与搜索指定的字段一致,否则无法高亮
  • 如果要对非搜索字段高亮,则需要添加一个属性:required_field_match=false
GET /hotel/_search
{
  "query": {
    "match": {
      "列名": "值"			#查询条件,高亮一定要使用全文检索查询
    }
  },
  "highlight": {
    "fields": { 		# 指定要高亮的字段
      "列名": {
        "pre_tags": "<em>",		# 用来标记高亮字段的前置标签
        "post_tags": "</em>", 	# 用来标记高亮字段的后置标签
        "required_field_match": "false"		#对非搜索字段高亮
      }
    }
  }
}

ES结果处理-Java操作

结果排序和分页:

对结果进行排序和分页, 都是在查询Request的对象处修改

/**
 * 查询-分页和排序
 * @throws IOException
 */
public void testESQuerySortPage(QueryBuilder query) throws IOException {
    // 1.准备request
    SearchRequest request = new SearchRequest("hotel");
    
    // 2.准备请求参数
    // 2.1.query
    request.source().query(query);
    // 2.2.排序 sort
    request.source().sort("列名", SortOrder.ASC);
    // 2.3.分页 from、size
    request.source().from((page - 1) * size).size(5);
    
    // // 2.3.距离排序
    // String location = "维度,经度";
    // if (StringUtils.isNotBlank(location)) {
    //     //按照地理位置排序
    //     request.source().sort(SortBuilders
    //             .geoDistanceSort("location", new GeoPoint(location))
    //             .order(SortOrder.ASC)
    //             .unit(DistanceUnit.KILOMETERS)
    //     );
    // }
    
    
    // 3.发送请求,得到响应
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.结果解析
    handleResponse(response);
}

高亮搜索

所谓高亮查询,其实就是在查询的关键字前边添加前缀,在关键字的后边添加后缀

高亮操作分为两部分: 1.设置高亮 2.获取高亮信息

注意: 高亮操作的列, 必须是"全文检索"的列, 也就是列的类型必须是text

设置高亮

**
 * 查询-高亮
 * @throws IOException
 */
public void testESQueryHighlight(QueryBuilder query) throws IOException {
    // 1.准备request
    SearchRequest request = new SearchRequest("hotel");
    
    // 2.准备请求参数
    // 2.1.query
    request.source().query(query);
    // 2.2.设置高亮
    request.source().highlighter(new HighlightBuilder().field("列名").requireFieldMatch(false));
    
    // 3.发送请求,得到响应
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.结果解析
    handleResponseHighlight(response);
}

** 获取高亮信息**

/**
  * 处理结果
  * @param response 查询到的结果
  */
private void handleResponseHighlight(SearchResponse response) {
    SearchHits searchHits = response.getHits();
    // 4.1.总条数
    long total = searchHits.getTotalHits().value;
    System.out.println("总条数:" + total);
    // 4.2.获取文档数组
    SearchHit[] hits = searchHits.getHits();
    // 4.3.遍历
    for (SearchHit hit : hits) {
        // 4.4.获取source
        String json = hit.getSourceAsString();
        // 4.5.反序列化,非高亮的
        HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
        // 4.6.处理高亮结果
        // 1)获取高亮map
        Map<String, HighlightField> map = hit.getHighlightFields();
        // 2)根据字段名,获取高亮结果
        HighlightField highlightField = map.get("name");
        // 3)获取高亮结果字符串数组中的第1个元素
        String hName = highlightField.getFragments()[0].toString();
        // 4)把高亮结果放到HotelDoc中
        hotelDoc.setName(hName);
        // 4.7.打印
        System.out.println(hotelDoc);
    }
}

ES聚合查询

ES聚合查询-脚本操作

桶聚合-分组:

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "lte": 200		#原始查询,可以修改(只对200元以下的文档聚合),如果不写,表示查询所有
      }
    }
  }, 
  "size": 0, 			#表示不显示文档,只显示聚合
  "aggs": {
    "brandAgg": {		#brandAgg是聚合名称,该名称自由定义,聚合可以书写多个
      "terms": {
        "field": "brand",	#根据brand进行分组
        "size": 20,			#最多显示20个结果
        "order": {
          "_count": "asc"	#按照_count升序排列,如果不写,默认根据count降序
        }
      }
    }
  }
}

度量聚合-聚合函数

直接聚合:

GET /hotel/_search
{
  "query": {
    "range": {
      "price": {
        "lte": 200	#原始查询,可以修改(只对200元以下的文档聚合),表示查询所有
      }
    }
  },
  "size": 0,	#表示不显示文档,只显示聚合
  "aggs": {		#如果直接写在外边,就是对所有聚合,如果写在桶聚合的里边,就是对分组之后进行聚合
    "score_stats": {	#score_stats是聚合名称,该名称自由定义
      "stats": {		#聚合类型,这里stats计算count,min,max,avg,sum等,也可以单独书写为min,max,avg,count,sum等
        "field": "score"	#聚合字段,这里是score
      }
    }
  }
}

分组后统计:

GET /hotel/_search
{
 "query": {
    "range": {
      "price": {
        "lte": 200	#原始查询,可以修改(只对200元以下的文档聚合),表示查询所有
      }
    }
  },
  "size": 0, 
  "aggs": {
    "brandAgg": { 			#聚合可以书写多个
      "terms": { 
        "field": "brand", 
        "size": 20
      },
      "aggs": { 			#书写在brandAgg里边,所以是brandAgg聚合的子聚合,也就是分组后对每组分别计算
        "score_stats": { 	#聚合名称,可以随便写
          "stats": { 		#聚合类型,这里stats可以计算min、max、avg等,也可以书写为min,max,avg,count
            "field": "score" #聚合字段,这里是score
          }
        }
      }
    }
  }
}

ES聚合查询-Java操作

桶聚合-分组:

@Test
public void testAgg() throws IOException {
    // 1.准备请求
    SearchRequest request = new SearchRequest("hotel");
    // 1.1 原始查询,如果不写,默认查询所有
    request.source().query(QueryBuilders.rangeQuery("price").lte(200));

    // 2.请求参数
    // 2.1.size
    request.source().size(0);
    // 2.2.聚合(聚合可以书写多个)
    request.source().aggregation(
        AggregationBuilders.terms("brandAgg")
        .field("brand")
        .size(20)
        .order(BucketOrder.count(true))
    );


    // 3.发出请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析结果
    Aggregations aggregations = response.getAggregations();
    // 4.1.根据聚合名称,获取聚合结果
    Terms brandAgg = aggregations.get("brandAgg");
    // 4.2.获取buckets
    List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
    // 4.3.遍历
    for (Terms.Bucket bucket : buckets) {
        String brandName = bucket.getKeyAsString();
        System.out.println("brandName = " + brandName);
        long docCount = bucket.getDocCount();
        System.out.println("docCount = " + docCount);
    }
}

度量聚合-聚合函数

直接聚合

/**
 * 度量聚合-聚合函数
 */
@Test
void testStatsAggs() throws IOException {
    // 1.准备请求
    SearchRequest request = new SearchRequest("hotel");
    // 1.1 原始查询,如果不写,默认查询所有
    request.source().query(QueryBuilders.rangeQuery("price").lte(200));

    // 2.请求参数
    // 2.1.size
    request.source().size(0);
    // 2.2.聚合
    request.source().aggregation(
        AggregationBuilders.stats("scoreStats").field("score")
    );

    // 3.发出请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析结果
    Aggregations aggregations = response.getAggregations();
    // 4.1.根据聚合名称,获取聚合结果
    Stats scoreStats = aggregations.get("scoreStats");
    // 4.2.获取数据
    double count = scoreStats.getCount();
    double max = scoreStats.getMax();
    double min = scoreStats.getMin();
    double avg = scoreStats.getAvg();
    double sum = scoreStats.getSum();
    System.out.println(count);
    System.out.println(max);
    System.out.println(min);
    System.out.println(avg);
    System.out.println(sum);
}

分组后统计

/**
     * 分组后统计
     */
@Test
void testTermsStatsAggs() throws IOException {
    // 1.准备请求
    SearchRequest request = new SearchRequest("hotel");
    // 1.1 原始查询,如果不写,默认查询所有
    request.source().query(QueryBuilders.rangeQuery("price").lte(200));

    // 2.请求参数
    // 2.1.size
    request.source().size(0);
    // 2.2.聚合
    request.source().aggregation(
        AggregationBuilders.terms("brandAgg")
        .field("brand")
        .size(20)
        .subAggregation(AggregationBuilders
                        .stats("scoreStats")
                        .field("score"))
    );


    // 3.发出请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);


    // 4.解析结果
    Aggregations aggregations = response.getAggregations();
    // 4.1.根据聚合名称,获取聚合结果
    Terms brandAgg = aggregations.get("brandAgg");
    // 4.2.获取buckets
    List<? extends Terms.Bucket> buckets = brandAgg.getBuckets();
    // 4.3.遍历
    for (Terms.Bucket bucket : buckets) {
        //获取key
        String brandName = bucket.getKeyAsString();
        //获取Count数量
        long docCount = bucket.getDocCount();

        //获取统计信息
        Aggregations scoreAgg = bucket.getAggregations();
        Stats scoreStats = scoreAgg.get("scoreStats");
        // 4.2.获取数据
        double count = scoreStats.getCount();
        double max = scoreStats.getMax();
        double min = scoreStats.getMin();
        double avg = scoreStats.getAvg();
        double sum = scoreStats.getSum();
        System.out.println(brandName + ":" + docCount);
        System.out.println(brandName + "统计信息:" + count + "-" + max + "-" + min + "-" + avg + "-" + sum);
    }
}

ES自定义分词器规则

PUT /hotel			#索引库名称
{
  "settings": {		#索引库设置
    "analysis": {
      "analyzer": {			#自定义分词器
        "text_anlyzer": {	#分词器名称
          "tokenizer": "ik_max_word",	#先用ik_max_word分词器,分词
          "filter": "py"				#然后用py过滤器进行进一步处理
        },
        "completion_analyzer": {	#分词器名称
          "tokenizer": "keyword",	#先用确定keyword规则
          "filter": "py"			#然后用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": "text_anlyzer",		#生成倒排索引时,使用text_anlyzer分词器
        "search_analyzer": "ik_smart",	#查询该列的时候,使用ik_smart分词器
        "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"
      },
      "business":{
        "type": "keyword",
        "copy_to": "all"
      },
      "location":{
        "type": "geo_point"
      },
      "pic":{
        "type": "keyword",
        "index": false
      },
      "all":{
        "type": "text",
        "analyzer": "text_anlyzer",		#生成倒排索引时,使用text_anlyzer分词器
        "search_analyzer": "ik_smart"	#查询该列的时候,使用ik_smart分词器
      },
      "suggestion":{
          "type": "completion",
          "analyzer": "completion_analyzer"	#生成和查询时,都使用completion_analyzer分词器
          									#因为已经指定了分词器,所以,该列中可以直接存储中文,查询时会自动根据拼音补全
      }
    }
  }
}

ES自动补全查询

ES自动补全查询-脚本操作

自动补全字段的类型必须是completion类型, 且字段值必须是多词条数组

GET /hotel/_search
{
  "suggest": {
    "hotel_suggest": {
      "text": "sh",		#查询关键字
      "completion": {
        "field": "suggestion",	#补全查询的字段(根据哪个字段自动补全)
        "skip_duplicates": true,	#跳过重复的
        "size": 10		#获取前10条结果
      }
    }
  }
}

ES自动补全查询-Java操作

@Test
public void testSuggest() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.准备DSL
    request.source().suggest(new SuggestBuilder().addSuggestion(
        "hotelSuggest",
        SuggestBuilders.completionSuggestion("suggestion")	//补全的字段
        .prefix("sh")            //查询关键字
        .skipDuplicates(true)   //跳过重复
        .size(10)               //获取前10条结果
    ));
    // 3.发起请求
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);
    // 4.解析结果
    Suggest suggest = response.getSuggest();
    // 4.1.根据补全查询名称,获取补全结果
    CompletionSuggestion suggestions = suggest.getSuggestion("hotelSuggest");
    // 4.2.获取options
    List<CompletionSuggestion.Entry.Option> options = suggestions.getOptions();
    // 4.3.遍历
    List<String> list = new ArrayList<>(options.size());
    for (CompletionSuggestion.Entry.Option option : options) {
        String text = option.getText().toString();
        list.add(text);
        System.out.println(text);
    }
}

ES集群-设置分片

注意,分片和副本设置只能在集群环境下设置

PUT /索引库名称			#索引库名称
{
  "settings": {	
    "number_of_shards": 3, 	#分片数量,默认1
    "number_of_replicas": 1 #副本数量
  },
  "mappings": {
    "properties": {
      //.....
    }
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值