一文学会Elasticsearch

目录

一、Elasticsearch简介:

 二、ES实现原理:

1、正向索引:

 2、倒排索引:

三、 Elasticsearch基础:

1、文档:

2、索引:

3、与SQL对比:

 4、切割器:

 四、索引库:

1、mapping属性:

2、采用DSL语句操作索引库:

1、创建索引库:

2、查询索引库语法和设置:

 3、删除索引库:

 4、修改索引库:

​编辑

 五、文档

1、添加文档:

 2、查询与删除文档:

1、查询文档:

2、删除文档:

​编辑

3、修改文档:

​编辑

4、动态映射:

六、RestClient操作索引库:

 一、用前准备:

二、RestClient对索引库的创建和删除:

 三、RestClient对文档的操作:

1、添加单个文档

2、通过ID查询数据

3、通过ID删除

4、通过ID修改

5、同时添加多个文档 

七、DSL查询文档:

1、基本查询类型:

2、全文检索查询:

 3、精确查询:

4、地理查询:

1、geo_bounding_box:

2、geo_distance:

​编辑 5、复合查询:

1、fuction score:

2、bool查询:

6、对搜索结果的处理: 

1、排序:

7、分页查询:

8、高亮:

八、RestClient查询文档:

1、查询所有:

 2、单一字段和多字段查询:

3、精确查询:

4、复合查询:

1、bool查询:

2、function_score:

​编辑​编辑

5、分页和排序:

6、高亮:

九、数据聚合:

1、常见的聚合类型:

2、RestClient实现数据聚合:


一、Elasticsearch简介:

Elasticsearch是一款非常强大的搜索引擎,可以帮助我们从海量数据中快速找到需要的内容,elasticsearch结合kibanaLogstashBeats,也就是elastic stackELK)被广泛应用在日志数据分析、实时监控等领域。,elasticsearch主要负责存储、搜索、分析数据。

  • E:EalsticSearch 搜索和分析的功能
  • L:Logstach 搜集数据的功能,是日志收集系统
  • K:Kibana 数据可视化(分析),可以用图表的方式来去展示,是数据可视化平台

一文学会Elasticsearch_第1张图片


 二、ES实现原理:

想必大家都用过mySQL的模糊查询,like语句,但是SQL的查询时通过建立正向索引的方式,如果通过id查询效率还是很高的,但是如果查询某一文本数据在数据量很大时,效率就会非常的低下。而ES在文本查询使用了倒排索引,进而提高了查询效率。

下面我们将以一个简单的查询案例来介绍正向和倒排索引:

我们将查询title中的字段,来模拟搜索数据。

一文学会Elasticsearch_第2张图片

1、正向索引:

当我们输入关键字手机时,正向索引会逐行对title字段进行查询,如果包含手机字段就将数据存入结果集,不存在就丢弃进行下一行的查询。

显然这就是在进行逐行的字符串匹配,所以针对海量数据时模糊搜索的效率极低,完全无法应对大数据的搜索。

一文学会Elasticsearch_第3张图片

 2、倒排索引:

 Elasticsearch首先会建立一个索引库,将数据切割成不同的词条,每个词条都对应着它所在数据的id。  注:词条不能重复,必须唯一。

例:我们搜索“华为手机”,Elasticsearch先将其切割成两个词条,然后去与索引库的词条表进行比对,保存符合条件的词条对应的id,最后按id查询所有符合条件的数据返回。

一文学会Elasticsearch_第4张图片


三、 Elasticsearch基础:

1、文档:

elasticsearch是面向文档存储的,可以是数据库中的一条商品数据,一个订单信息。文档数据会被序列化为json格式后存储在elasticsearch中。

相当于MySQL中的一个行,每一个字段(id,title相当于一个列)

例如刚刚我们用到数据表,在数据库中是这样存的:

一文学会Elasticsearch_第5张图片

2、索引:

Elasticsearch会将相同类型的文档进行归类,索引就是相同类型文档的集合。

相当于MySQL中的一个表

下图本是一推杂乱无章的数据,进行归档之后就会将相同的合并:

一文学会Elasticsearch_第6张图片

归档之后:

一文学会Elasticsearch_第7张图片

3、与SQL对比:

一文学会Elasticsearch_第8张图片

二者对比:

一文学会Elasticsearch_第9张图片

 4、切割器:

我们知道ES搜索快速的原因就是因为建立了索引表,利用词条进行搜索,所以核心就在于如何将词条进行切割,ES内置的算法对英文的切割效果极佳,但是对于中文的切割效果不太理想,所以这里采用了ik分词器来弥补这一缺陷。官网。

ik分词器包含两种模式:

  • ik_smart:最少切分,粗粒度
  • ik_max_word:最细切分,细粒度

但ik也有一定的缺陷,因为新词频出,ik对目前我们常见的词汇分割效果十分明显,但是对新颖的组合词常见词就无法切割,但是好在我们可以自己配置。

1、进入ik的目录找到一个叫IkAnalyzer.cfg.xml的文件,进去进行编辑:

一文学会Elasticsearch_第10张图片

2、创建刚刚写的两个文件名:

一文学会Elasticsearch_第11张图片

3、之后我们在里面进行编辑就可以随着我们的需求进行词条的添加和删除了。

例:

未将奥力给添加到新增词汇,会将其切分为三个字

一文学会Elasticsearch_第12张图片

 将其添加进去之后:

一文学会Elasticsearch_第13张图片

将其添加到禁止列表中:将不再将“奥里给“添加到词条。

一文学会Elasticsearch_第14张图片

 上述操作页面在安装好kibana之后就可以通过  http://虚拟机IP地址:5601 访问


 四、索引库:

1、mapping属性:

mapping是对索引库中文档的约束,常见的属性值有:

1、type:字段数据类型,常见的简单类型有:

        字符串:text(可分词的文本)、keywordid用此类型保存。精确值,例:品牌、国家、ip

        数值:longintegershortbytedoublefloat

        布尔:boolean

        日期:date

        对象:object

2、index:是否创建索引,默认为true

3、analyzer:使用哪种分词器

4、properties:该字段的子字段

2、采用DSL语句操作索引库:

索引库相当于创建MySQL中的一个表:

1、创建索引库:

1、只有text类型才是要进行分词的字段。

2、index表示该字段是否要参加搜索,默认是true

3、properties定义子字段类型,类似于一个对象内部的属性。

4、索引库名必须小写。

语法案例:
PUT /索引库名称
{
  "mappings": {
    "properties": {
      "字段名":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "字段名2":{
        "type": "keyword",
        "index": "false"
      },
      "字段名3":{
        "properties": {
          "子字段": {
            "type": "keyword"
          }
        }
      },
    }
  }
}

真实语句:
PUT /mine
{
  "mappings": {
    "properties": {
      "info":{
        "type": "text",
        "analyzer": "ik_smart"
      },
      "email":{
        "type": "keyword",
        "index": "false"
      },
      "name":{
        "properties": {
          "firstName": {
            "type": "keyword"
          },
          "lastName": {
            "type": "keyword"
          }
        }
      }
    }
  }
}

 创建成功提示:一文学会Elasticsearch_第15张图片

2、查询索引库语法和设置:

一文学会Elasticsearch_第16张图片

 3、删除索引库:

一文学会Elasticsearch_第17张图片

 4、修改索引库:

索引库相当于数据库的表,但是数据库的表是允许进行字段的删除,修改名称操作的,索引库只支持添加新字段。

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

演示:
PUT /mine/_mapping
{
  "properties":{
    "age":{
      "type":"integer"
    }
  }
}

通过get语句查询: 

一文学会Elasticsearch_第18张图片


 五、文档

文档相当于MySQL中的一行,是一条一条的信息,只不过在ES中是以JSON的形式保存的。每一个文档都要有一个唯一的id

1、添加文档:

属性名和值的类型必须与索引库的结构保持一致。一文学会Elasticsearch_第19张图片

POST /mine/_doc/1
{
  "age":12,
  "email":"[email protected]",
  "info":"我学了ES啦",
  "name":{
    "firstName":"张",
    "lastName":"三"
  }
}

一文学会Elasticsearch_第20张图片

上例若未写info,则产生的文档中无此属性:

一文学会Elasticsearch_第21张图片

 2、查询与删除文档:

1、查询文档:

一文学会Elasticsearch_第22张图片

2、删除文档:

3、修改文档:

当文档id存在时,会覆盖之前的内容,不存在时会新增一个文档 

一文学会Elasticsearch_第23张图片

一文学会Elasticsearch_第24张图片

4、动态映射:

当我们向ES中插入文档时,如果文档中字段没有对应的mappingES会帮助我们字段设置mapping ,规则如下:

一文学会Elasticsearch_第25张图片 

 ES会自动将数据类型转化为它内置的类型。


六、RestClient操作索引库

RestClient是ES官方提供的针对各种不同语言的客户端,用来操作ES。这些客户端的本质就是组装DSL语句,通过http请求发送给ES。 官网

 一、用前准备:

1、引入esRestHighLevelClient依赖:


    org.elasticsearch.client
    elasticsearch-rest-high-level-client

2、修改的版本号,因为SpringBoot默认的ES版本与服务器上的ES版本不同,所以我们需要覆盖默认的ES版本:

版本号必须与服务器上的版本号保持一致,否则无法操作


    1.8
    7.12.1 

3、初始化RestHighLevelClient对象,该对象用于对索引表和索引库的操作:

之后可以将其注入到SpringBoot容器中,直接使用

//创建对象
RestHighLevelClient client = new RestHighLevelClient(RestClient.builder(
        HttpHost.create("http://服务器地址:9200")
));

//使用之后销毁对象
client.close();

二、RestClient对索引库的创建和删除:

client.indices()包含了对索引库所有的操作

@SpringBootTest
class HotelIndexTest {

    private RestHighLevelClient client;

    //在每次TEST前创建链接
    @BeforeEach
    void setUp() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://虚拟机地址:9200")
        ));
    }

    //每次TEST结束之后关闭链接
    @AfterEach
    void tearDown() throws IOException {
        client.close();
    }

    //创建一个索引库
    @Test
    void testCreateIndex() throws IOException {
        // 1.准备Request      PUT /hotel <=DSL语法
        CreateIndexRequest request = new CreateIndexRequest("hotel");
        // 2.准备请求参数   
        //第一个参数是DLS语句,与之前创建索引表的内容是一致的,由于太长,单独创建了一个文件保存
        //第二个参数是将前面的字符串转化为JSON形式
        request.source(MAPPING_TEMPLATE, XContentType.JSON);
        // 3.发送请求
        client.indices().create(request, RequestOptions.DEFAULT);
    }

    //查看索引库是否存在
    @Test
    void testExistsIndex() throws IOException {
        // 1.准备Request
        GetIndexRequest request = new GetIndexRequest("hotel");
        // 2.发送请求
        boolean isExists = client.indices().exists(request, RequestOptions.DEFAULT);

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

    //删除一个索引库
    @Test
    void testDeleteIndex() throws IOException {
        // 1.准备Request
        DeleteIndexRequest request = new DeleteIndexRequest("hotel");
        // 2.发送请求
        client.indices().delete(request, RequestOptions.DEFAULT);
    }
}

上面的常量:

public class HotelIndexConstants {
    public static final String MAPPING_TEMPLATE = "{\n" +
            "  \"mappings\": {\n" +
            "    \"properties\": {\n" +
            "      \"id\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"name\": {\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"address\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"price\": {\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"score\": {\n" +
            "        \"type\": \"integer\"\n" +
            "      },\n" +
            "      \"brand\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"city\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"starName\": {\n" +
            "        \"type\": \"keyword\"\n" +
            "      },\n" +
            "      \"business\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"copy_to\": \"all\"\n" +
            "      },\n" +
            "      \"pic\": {\n" +
            "        \"type\": \"keyword\",\n" +
            "        \"index\": false\n" +
            "      },\n" +
            "      \"location\": {\n" +
            "        \"type\": \"geo_point\"\n" +
            "      },\n" +
            "      \"all\": {\n" +
            "        \"type\": \"text\",\n" +
            "        \"analyzer\": \"ik_max_word\"\n" +
            "      }\n" +
            "    }\n" +
            "  }\n" +
            "}";
}

 三、RestClient对文档的操作:

操作索引表是client.indices()操作文档直接用client对象即可

下面包含了:

1、添加单个文档

2、通过ID查询数据

3、通过ID删除

4、通过ID修改

5、同时添加多个文档 

用于将MySQL中的数据导入的ES中

@SpringBootTest
class HotelDocumentTest {

    private RestHighLevelClient client;

    @Autowired
    private IHotelService hotelService;
    
    //在每次TEST前创建链接
    @BeforeEach
    void setUp() {
        client = new RestHighLevelClient(RestClient.builder(
                HttpHost.create("http://虚拟机地址:9200")
        ));
    }
    //每次TEST结束之后关闭链接
    @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);
    }

//通过文档的ID来查询数据
    @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);
    }
//通过ID删除文档
    @Test
    void testDeleteDocumentById() throws IOException {
        // 1.准备Request      // DELETE /hotel/_doc/{id}
        DeleteRequest request = new DeleteRequest("hotel", "61083");
        // 2.发送请求
        client.delete(request, RequestOptions.DEFAULT);
    }
//通过ID修改数据
    @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 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);
    }
}

七、DSL查询文档:

1、基本查询类型:

查询

所有

查询出所有数据,一般测试用 much_all

全文

检索

利用分词器对用户输入内容分词,然后去倒排索引库中匹配。模糊查询

much

mutil_much

精确

查询

根据精确词条值查找数据,一般是查找keyword、数值、日期、boolean等类型字段 term、ids、range

地理

查询

根据经纬度查询

geo_distance

geo_bounding_box

复合查询 复合查询可以将上述各种查询条件组合起来,合并查询条件 function_score、bool

一文学会Elasticsearch_第26张图片

2、全文检索查询:

模糊查询,用于对搜索框的查询:

一文学会Elasticsearch_第27张图片 match查询全文检索查询的一种,会对用户输入内容分词,然后去倒排索引库检索

multi_match:与match查询类似,只不过允许同时查询多个字段,一文学会Elasticsearch_第28张图片

 3、精确查询:

精确查询一般是查找keyword、数值、日期、boolean等类型字段。所以不会对搜索条件分词。常见的有:         只会查询出与输入的一致的信息

term :根据词条精确值查询
range :根据值的范围查询

一文学会Elasticsearch_第29张图片

 一文学会Elasticsearch_第30张图片

4、地理查询:

根据经纬度对数据进行查询

1、geo_bounding_box:

查询geo_point值落在某个矩形范围的所有文档

一文学会Elasticsearch_第31张图片

2、geo_distance

查询到指定中心点小于某个距离值的所有文档

一文学会Elasticsearch_第32张图片

对应Java部分:

一文学会Elasticsearch_第33张图片 5、复合查询:

复合查询:复合查询可以将其它简单查询组合起来,实现更复杂的搜索逻辑 

1、fuction score:

算分函数查询,可以控制文档相关性算分,控制文档排名。例如百度竞价:

一文学会Elasticsearch_第34张图片

一文学会Elasticsearch_第35张图片

2、bool查询:

布尔查询是一个或多个查询子句的组合:

常见的组合方式:

must 必须匹配每个子查询,类似“与”
should 选择性匹配子查询,类似“或”
must_not 必须不匹配,不参与算分,类似“非”
filter 必须匹配,不参与算分

 案例:搜索名字包含“如家”,价格不高于400,在坐标31.21,121.5周围10km范围内的酒店。

一文学会Elasticsearch_第36张图片

6、对搜索结果的处理: 

1、排序:

elasticsearch支持对搜索结果排序,默认是根据相关度算分(_score)来排序。可以排序字段类型有:keyword类型、数值类型、地理坐标类型、日期类型等。

一文学会Elasticsearch_第37张图片 案例1对酒店数据按照用户评价降序排序,评价相同的按照价格升序排序。

会默认按照先后顺序进行排序,先写得分就按得分排,当得分相同时按价格排。 

一文学会Elasticsearch_第38张图片

案例2对酒店数据按照到你的位置坐标的距离升序排序 

一文学会Elasticsearch_第39张图片

7、分页查询:

elasticsearch 默认情况下只返回top10的数据。而如果要查询更多数据就需要修改分页参数进行分页查询。elasticsearch中通过修改fromsize参数来控制要返回的分页结果:

一文学会Elasticsearch_第40张图片ES是分布式的,所以会面临深度分页问题,如果搜索页数过深,或者结果集(from + size)越大,对内存和CPU的消耗也越高。因此ES设定结果集查询的上限是10000 

8、高亮:

将搜索结果中把搜索关键字突出显示。 

一文学会Elasticsearch_第41张图片

语法:

一文学会Elasticsearch_第42张图片一文学会Elasticsearch_第43张图片

八、RestClient查询文档:

1、查询所有:

主要分两步:

1、发送查询请求

2、解析数据。解析数据就对照通过DSL语句查询出来的结果进行JSON模式解析即可。

@Test
void testMatchAll() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.组织DSL参数 
    request.source()
    .query(QueryBuilders.matchAllQuery());
    // 3.发送请求,得到响应结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    // 4.解析结果
    SearchHits searchHits = response.getHits();
    // 4.1.查询的总条数
    long total = searchHits.getTotalHits().value;
    // 4.2.查询的结果数组
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 4.3.得到source
        String json = hit.getSourceAsString();
        // 4.4.打印
        System.out.println(json);
    }
}

通过DSL语句查询出来的信息:

一文学会Elasticsearch_第44张图片

 2、单一字段和多字段查询:

根据DSL语句我们发现查询所有、查询单一字段、查询多个字段只是query部分不同,所以对于Java代码也是只有参数不同即可实现:

一文学会Elasticsearch_第45张图片

 只需要修改第二步即可实现对应的功能:

    // 2.组织DSL参数 

    //单一字段查询
    request.source()
    .query(QueryBuilders.matchQuery("all", "如家"));
    
    //多字段查询
    request.source()
    .query(QueryBuilders.multiMatchQuery("如家", "name", "business"));

3、精确查询:

与上面也是相同,只需要修改第二步的查询参数即可:

一文学会Elasticsearch_第46张图片

    // 2.组织DSL参数 

    //词条查询  
    request.source()
    .query(QueryBuilders.termQuery("city", "杭州"));
    
    //范围查询
    request.source()
    .query(QueryBuilders.rangeQuery("price").gte(100).lte(150));

4、复合查询:

1、bool查询:

同样是修第二步的代码,其余部分不变,相比前几个这里会稍微复杂一些:

一文学会Elasticsearch_第47张图片

//2、准备DSl
// 1、创建布尔查询
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 2、添加must条件
boolQuery.must(QueryBuilders.termQuery("city", "杭州")); 
// 3、添加filter条件
boolQuery.filter(QueryBuilders.rangeQuery("price").lte(250))
//4、添加到查询中去:
request.source().query(boolQuery);

2、function_score:

一文学会Elasticsearch_第48张图片一文学会Elasticsearch_第49张图片

5、分页和排序:

request.source()相当于整个JSON对象,通过下图可知 query和 sort、from 、size都是同级,所以像request.source()。query()一样去调用即可

一文学会Elasticsearch_第50张图片

6、高亮:

一文学会Elasticsearch_第51张图片

前面我们已经说过,解析数据是得到source部分,但是heighLight和source同级,所以需要特殊处理:



@Test
void testMatchAll() throws IOException {
    // 1.准备Request
    SearchRequest request = new SearchRequest("hotel");
    // 2.组织DSL参数 
    request.source()
    .query(QueryBuilders.matchAllQuery());
    // 3.发送请求,得到响应结果
    SearchResponse response = client.search(request, RequestOptions.DEFAULT);

    // 4.解析结果
    SearchHits searchHits = response.getHits();
    // 4.1.查询的总条数
    long total = searchHits.getTotalHits().value;
    // 4.2.查询的结果数组
    SearchHit[] hits = searchHits.getHits();
    for (SearchHit hit : hits) {
        // 4.3.得到source
        String json = hit.getSourceAsString();

        // 获取source
        HotelDoc hotelDoc = JSON.parseObject(hit.getSourceAsString(), HotelDoc.class);
        // 处理高亮
        Map highlightFields = hit.getHighlightFields();
        if (!CollectionUtils.isEmpty(highlightFields)) {
        // 获取高亮字段结果
        HighlightField highlightField = highlightFields.get("name");
            if (highlightField != null) {
                // 取出高亮结果数组中的第一个,就是酒店名称
                String name = highlightField.getFragments()[0].string();
                //覆盖高亮的值
                hotelDoc.setName(name);
            }
        }
    }
}

九、数据聚合:

实现对文档数据的统计、分析、运算。与MySQL中的聚合函数sum、avg相似。

1、常见的聚合类型:

 1、桶聚合:用来对文档做分组。

  • TermAggregation:按照文档字段值分组
  • Date Histogram:按照日期阶梯分组,例如一周为一组,或者一月为一组

2、度量聚合:用以计算一些值,比如:最大值、最小值、平均值等。

  • Avg:求平均值
  • Max:求最大值
  • Min:求最小值
  • Stats:同时求maxminavgsum

例:统计不同酒店评分的平均值:

        1、通过桶聚合将酒店进行分类

        2、通过度量聚合对已分类的酒店进行平均值求解

2、RestClient实现数据聚合:

仍然与之前一样,修改第二步的DSL部分:

一文学会Elasticsearch_第52张图片解析聚合结果:

一文学会Elasticsearch_第53张图片

你可能感兴趣的:(微服务,elasticsearch,搜索引擎,大数据)