第七课 Spring Cloud分布式微服务实战-分布式搜索引擎ES

第七课 Spring Cloud分布式微服务实战-分布式搜索引擎ES

tags:

  • Java
  • 慕课网

categories:

  • ES

文章目录

  • 第七课 Spring Cloud分布式微服务实战-分布式搜索引擎ES
    • 第一节 ES介绍安装
      • 1.1 ES环境安装
      • 1.2 ES环境分词器的使用
      • 1.3 项目中整合ES
      • 1.4 ES的索引创建和删除
      • 1.5 为索引创建mappings和文档数据的增删改查
    • 第二节 ES实践文章操作
      • 2.1 梳理文章索引lfield并创建
      • 2.2 文档数据的操作
      • 2.3 文章的三种ES搜索
      • 2.4 文章的三种ES搜索代码中实现
    • 第三节 ES实践粉丝操作
      • 3.1 创建索引保存es
      • 3.2 es查询粉丝
      • 3.3 es聚合统计
      • 3.4 es聚合统计业务代码实现

第一节 ES介绍安装

1.1 ES环境安装

  1. docker安装es和es head
# 启动es http://192.168.44.128:9200 查看是否启动成功
docker run -d --name elasticsearch -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" -e ES_JAVA_OPTS="-Xms256m -Xmx256m" elasticsearch:7.6.2
# 设置允许跨域访问 为了elasticsearch-head连接访问
docker exec -it elasticsearch /bin/bash
cd config
echo "
http.cors.enabled: true
http.cors.allow-origin: '*' " >> elasticsearch.yml


docker restart elasticsearch
# 安装elasticsearch-head
docker search elasticsearch-head
docker run -d --name elasticsearh-head -p 9100:9100 mobz/elasticsearch-head:5

# 修改head的Content-Type设置 不修改的话创建索引会报错406
docker exec -it 9fcdebef858b /bin/bash
apt-get update
apt-get install vim

vim _site/vendor.js

# 1. 6886行 contentType: "application/x-www-form-urlencoded  改成 contentType: "application/json;charset=UTF-8" 
# 2. 7574行 var inspectData = s.contentType ==`= "application/x-www-form-urlencoded" &&` 改成 var inspectData = s.contentType === "application/json;charset=UTF-8" &&

docker restart 9fcdebef858b

# 安装ik中文分词器 把压缩包拷贝到容器内plugins目录解压:
wget https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.6.2/elasticsearch-analysis-ik-7.6.2.zip

docker exec -it elasticsearch /bin/bash
cp plugin
unzip elasticsearch-analysis-ik-7.6.2.zip -d ik7.6
rm -rf elasticsearch-analysis-ik-7.6.2.zip

docker restart elasticsearch

1.2 ES环境分词器的使用

  1. 把文本转换为一个个的单词,分词称之为analysis。es默认只对英文语句做分词,中文不支持,每个中文字都会被拆分为独立的个体。
  2. es内置分词器
    • tandard:默认分词,单词会被拆分,大小会转换为小写。
    • simple:按照非字母分词。大写转为小写。
    • whitespace:按照空格分词。忽略大小写。
    • stop:去除无意义单词,比如the/a/an/is…
    • keyword:不做分词。把整个文本作为一个单独的关键词。
  3. 通过postman调用post接口。http://192.168.44.128:9200/_analyze/
    第七课 Spring Cloud分布式微服务实战-分布式搜索引擎ES_第1张图片
  4. 标准分词器结果。默认不支持中文,中文会一个个给分开。
POST /_analyze
{
    "analyzer": "standard",
    "text": "text文本"
}
{
    "tokens": [
        {
            "token": "text",
            "start_offset": 0,
            "end_offset": 4,
            "type": "",
            "position": 0
        },
        {
            "token": "文",
            "start_offset": 4,
            "end_offset": 5,
            "type": "",
            "position": 1
        },
        {
            "token": "本",
            "start_offset": 5,
            "end_offset": 6,
            "type": "",
            "position": 2
        }
    ]
}
  1. 中文分词器输入。需要先安装中文分词器插件才可以使用。https://github.com/medcl/elasticsearch-analysis-ik
    • ik_max_word: 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query;
    • ik_smart: 会做最粗粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,国歌”,适合 Phrase 查询。
POST /_analyze
{
    "analyzer": "ik_max_word",
    "text": "今天上下班车流量很大很拥堵"
}
  1. 中文分词器结果
{
    "tokens": [
        {
            "token": "今天",
            "start_offset": 0,
            "end_offset": 2,
            "type": "CN_WORD",
            "position": 0
        },
        {
            "token": "天上",
            "start_offset": 1,
            "end_offset": 3,
            "type": "CN_WORD",
            "position": 1
        },
        {
            "token": "上下班",
            "start_offset": 2,
            "end_offset": 5,
            "type": "CN_WORD",
            "position": 2
        },
        {
            "token": "上下",
            "start_offset": 2,
            "end_offset": 4,
            "type": "CN_WORD",
            "position": 3
        },
        {
            "token": "下班",
            "start_offset": 3,
            "end_offset": 5,
            "type": "CN_WORD",
            "position": 4
        },
        {
            "token": "班车",
            "start_offset": 4,
            "end_offset": 6,
            "type": "CN_WORD",
            "position": 5
        },
        {
            "token": "车流量",
            "start_offset": 5,
            "end_offset": 8,
            "type": "CN_WORD",
            "position": 6
        },
        {
            "token": "车流",
            "start_offset": 5,
            "end_offset": 7,
            "type": "CN_WORD",
            "position": 7
        },
        {
            "token": "流量",
            "start_offset": 6,
            "end_offset": 8,
            "type": "CN_WORD",
            "position": 8
        },
        {
            "token": "很大",
            "start_offset": 8,
            "end_offset": 10,
            "type": "CN_WORD",
            "position": 9
        },
        {
            "token": "很",
            "start_offset": 10,
            "end_offset": 11,
            "type": "CN_CHAR",
            "position": 10
        },
        {
            "token": "拥堵",
            "start_offset": 11,
            "end_offset": 13,
            "type": "CN_WORD",
            "position": 11
        }
    ]
}

1.3 项目中整合ES

  1. 引入依赖
        >
            >org.springframework.boot>
            >spring-boot-starter-data-elasticsearch>
        >
  1. 配置文件。代码中配置使用9300端口
spring:
  application:
    name: service-search
  data:
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.44.128:9300

1.4 ES的索引创建和删除

  1. 创建pojo, com.imooc.search.pojo.Stu,索引名称stu
package com.imooc.search.pojo;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

@Document(indexName = "stu", type = "_doc")
public class Stu {

    @Id
//    @Field
    private Long stuId;

    @Field
    private String name;

    @Field
    private Integer age;

    @Field
    private float money;

    @Field
    private String desc;

    public Long getStuId() {
        return stuId;
    }

    public void setStuId(Long stuId) {
        this.stuId = stuId;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public float getMoney() {
        return money;
    }

    public void setMoney(float money) {
        this.money = money;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    @Override
    public String toString() {
        return "Stu{" +
                "stuId=" + stuId +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", money=" + money +
                ", desc='" + desc + '\'' +
                '}';
    }
}
  1. controller中创建和删除索引。
    @Autowired
    private ElasticsearchTemplate esTemplate;

    @RequestMapping("hello")
    public Object hello() {
        return GraceJSONResult.ok();
    }

    @GetMapping("createIndex")
    public Object createIndex() {
        esTemplate.createIndex(Stu.class);
        return GraceJSONResult.ok();
    }

    @GetMapping("deleteIndex")
    public Object deleteIndex() {
        esTemplate.deleteIndex(Stu.class);
        return GraceJSONResult.ok();
    }
  1. 调用测试。http://127.0.0.1:8006/createIndex

1.5 为索引创建mappings和文档数据的增删改查

  1. 一般来说为了规范,我们都是通过手动来创建索引的,**因为索引一旦创建就无法修改,除非删除,连field的类型是什么也不能修改,所以往往都是在初期规划好的。**那么在这里我们就通过手动的方式来创建。
  2. ES 7.X以上版本不用输入_doc 。这里mapping就相当于表结构
# POST        /stu/_doc/_mapping 6.x
POST        /stu/_mappings 7.x
{
    "properties": {
        "stuId": {
            "type": "long"
        },
        "name": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "age": {
            "type": "integer"
        },
        "money": {
            "type": "float"
        },
        "desc": {
            "type": "text",
            "analyzer": "ik_max_word"
        }
    }
}
  1. http://192.168.44.128:9100/ 的stu的索引信息查看。
  2. 对文档数据的新增、修改、查询、删除。http://192.168.44.128:9100/ 数据浏览tab中可以看到具体信息。
    @GetMapping("addDoc")
    public Object addDoc() {
        Stu stu = new Stu();
        stu.setStuId(1002l);
        stu.setAge(18);
        stu.setName("imooc");
        stu.setMoney(100.2f);
        stu.setDesc("慕课网学习的学生");

        IndexQuery query = new IndexQueryBuilder()
                .withObject(stu)
                .build();

        esTemplate.index(query);
        return GraceJSONResult.ok();
    }

    @GetMapping("updateDoc")
    public Object updateDoc() {

        Map<String, Object> updateMap = new HashMap<>();
        updateMap.put("desc", "hello world");
        updateMap.put("age", 22);

        IndexRequest ir = new IndexRequest();
        ir.source(updateMap);

        UpdateQuery uq = new UpdateQueryBuilder()
                .withClass(Stu.class)
                .withId("1001")
                .withIndexRequest(ir)
                .build();

        esTemplate.update(uq);
        return GraceJSONResult.ok();
    }

    @GetMapping("getDoc")
    public Object getDoc(String id) {

        GetQuery getQuery = new GetQuery();
        getQuery.setId(id);
        Stu stu = esTemplate.queryForObject(getQuery, Stu.class);
        return GraceJSONResult.ok(stu);
    }

    @GetMapping("deleteDoc")
    public Object deleteDoc(String id) {
        esTemplate.delete(Stu.class, id);
        return GraceJSONResult.ok();
    }

第二节 ES实践文章操作

2.1 梳理文章索引lfield并创建

  1. 创建新的索引es-header中articles。
  2. http://192.168.44.128:9200/articles/_mappings 创建索引 title使用中文的分词器。
POST     /articles/_doc/_mapping
{
    "properties": {
        "id": {
            "type": "text"
        },
        "title": {
            "type": "text",
            "analyzer": "ik_max_word"
        },
        "categoryId": {
            "type": "integer"
        },
        "articleType": {
            "type": "integer"
        },
        "articleCover": {
            "type": "keyword"
        },
        "publishUserId": {
            "type": "text"
        },
        "publishTime": {
            "type": "date"
        }
    }
}
  1. model模块中引入依赖和创建eo实体类com.imooc.pojo.eo.ArticleEO
        >
            >org.springframework.boot>
            >spring-boot-starter-data-elasticsearch>
        >
  data:
    mongodb:
      uri: mongodb://root:[email protected]:27017
      database: imooc-news
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.44.128:9300
package com.imooc.pojo.eo;

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

import java.util.Date;

@Data
@Document(indexName = "articles", type = "_doc")
public class ArticleEO {
    @Id
    private String id;
    @Field
    private String title;
    @Field
    private Integer categoryId;
    @Field
    private Integer articleType;
    @Field
    private String articleCover;
    @Field
    private String publishUserId;
    @Field
    private Date publishTime;
    }

2.2 文档数据的操作

  1. 发布文章新增文档数据。com.imooc.article.service.impl.ArticleServiceImpl
    @Autowired
    private ElasticsearchTemplate esTemplate;


    @Transactional
    @Override
    public void updateArticleStatus(String articleId, Integer pendingStatus) {
        Example example = new Example(Article.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("id", articleId);

        Article pendingArticle = new Article();
        pendingArticle.setArticleStatus(pendingStatus);

        int res = articleMapper.updateByExampleSelective(pendingArticle, example);
        if (res != 1) {
            GraceException.display(ResponseStatusEnum.ARTICLE_REVIEW_ERROR);
        }

        // 如果审核通过,则查询article,把相应的数据字段信息存入es中
        if (pendingStatus == ArticleReviewStatus.SUCCESS.type) {
            Article result = articleMapper.selectByPrimaryKey(articleId);
            // 如果是即时发布的文章,审核通过后则可以直接存入es中
            if (result.getIsAppoint() == ArticleAppointType.IMMEDIATELY.type) {
                ArticleEO articleEO = new ArticleEO();
                BeanUtils.copyProperties(result, articleEO);
                IndexQuery iq = new IndexQueryBuilder().withObject(articleEO).build();
                esTemplate.index(iq);
            }
            // FIXME: 作业:如果是定时发布,此处不会存入到es中,需要在定时的延迟队列中去执行
        }
    }
  1. 撤回和删除文章时,删除文档数据。deleteArticlewithdrawArticle中删除文档。
esTemplate.delete(ArticleEO.class, articleId);

2.3 文章的三种ES搜索

  1. 首页的文章列表查询,在es中有3中形式,如下:
    • 首页默认查询所有
    • 按照分类查询
    • 按照关键字查询
  2. 在es中对应的脚本查询如下:
POST:http://192.168.44.128:9200/articles/_doc/_search

{
    "query": {
        "match_all": {}
    }
}

POST: http://192.168.44.128:9200/articles/_doc/_search

{
    "query": {
        "term": {
            "categoryId": "分类的id号"
        }
    }
}


POST: http://192.168.44.128:9200/articles/_doc/_search

{
    "query": {
        "match": {
            "title": "查询关键字"
        }
    }
}

2.4 文章的三种ES搜索代码中实现

  1. API中添加接口。com.imooc.api.controller.article.ArticlePortalControllerApi
    @GetMapping("es/list")
    @ApiOperation(value = "首页通过es查询文章列表", notes = "首页查询文章列表", httpMethod = "GET")
    public GraceJSONResult eslist(@RequestParam String keyword,
                                  @RequestParam Integer category,
                                  @ApiParam(name = "page", value = "查询下一页的第几页", required = false)
                                  @RequestParam Integer page,
                                  @ApiParam(name = "pageSize", value = "分页的每一页显示的条数", required = false)
                                  @RequestParam Integer pageSize);
  1. Article中代码实现com.imooc.article.controller.ArticlePortalController#eslist
    @Autowired
    private ElasticsearchTemplate esTemplate;
    
    @Override
    public GraceJSONResult eslist(String keyword,
                                  Integer category,
                                  Integer page,
                                  Integer pageSize) {

        /**
         * es查询:
         *      1. 首页默认查询,不带参数
         *      2. 按照文章分类查询
         *      3. 按照关键字查询
         */

        // es的页面是从0开始计算的,所以在这里page需要-1
        if (page<1) return null;
        page--;
        Pageable pageable = PageRequest.of(page, pageSize);

        SearchQuery query = null;
        AggregatedPage<ArticleEO> pagedArticle = null;
        // 符合第1种情况
        if (StringUtils.isBlank(keyword) && category == null) {
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.matchAllQuery())
                    .withPageable(pageable)
                    .build();
            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        }

        // 符合第2种情况
        if (StringUtils.isBlank(keyword) && category != null) {
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.termQuery("categoryId", category))
                    .withPageable(pageable)
                    .build();
            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        }

        // 符合第3种情况
//        if (StringUtils.isNotBlank(keyword) && category == null) {
//            query = new NativeSearchQueryBuilder()
//                    .withQuery(QueryBuilders.matchQuery("title", keyword))
//                    .withPageable(pageable)
//                    .build();
//        }

        // 基于第3种情况的关键字搜索高亮展示
        String searchTitleField = "title";
        if (StringUtils.isNotBlank(keyword) && category == null) {
            String preTag = "";
            String postTag = "";
            query = new NativeSearchQueryBuilder()
                    .withQuery(QueryBuilders.matchQuery(searchTitleField, keyword))
                    .withHighlightFields(new HighlightBuilder.Field(searchTitleField)
                            .preTags(preTag)
                            .postTags(postTag)
                    )
                    .withPageable(pageable)
                    .build();

            pagedArticle = esTemplate.queryForPage(query, ArticleEO.class, new SearchResultMapper() {
                @Override
                public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> clazz, Pageable pageable) {

                    List<ArticleEO> articleHighLightList = new ArrayList<>();
                    SearchHits hits = response.getHits();
                    for (SearchHit h : hits) {
                        HighlightField highlightField = h.getHighlightFields().get(searchTitleField);
                        String title = highlightField.getFragments()[0].toString();

                        // 获得其他的字段信息数据,并且重新封装
                        String articleId = (String)h.getSourceAsMap().get("id");
                        Integer categoryId = (Integer)h.getSourceAsMap().get("categoryId");
                        Integer articleType = (Integer)h.getSourceAsMap().get("articleType");
                        String articleCover = (String)h.getSourceAsMap().get("articleCover");
                        String publishUserId = (String)h.getSourceAsMap().get("publishUserId");
                        Long dateLong = (Long)h.getSourceAsMap().get("publishTime");
                        Date publishTime = new Date(dateLong);

                        ArticleEO articleEO = new ArticleEO();
                        articleEO.setId(articleId);
                        articleEO.setCategoryId(categoryId);
                        articleEO.setTitle(title);
                        articleEO.setArticleType(articleType);
                        articleEO.setArticleCover(articleCover);
                        articleEO.setPublishUserId(publishUserId);
                        articleEO.setPublishTime(publishTime);

                        articleHighLightList.add(articleEO);
                    }

                    // 如果使用es7的话,这部分的代码会精简很多

                    return new AggregatedPageImpl<>((List<T>)articleHighLightList,
                            pageable,
                            response.getHits().totalHits);
                }

                @Override
                public <T> T mapSearchHit(SearchHit searchHit, Class<T> type) {
                    return null;
                }
            });
        }


        // 重组文章列表

//        AggregatedPage pagedArticle = esTemplate.queryForPage(query, ArticleEO.class);
        List<ArticleEO> articEOleList = pagedArticle.getContent();
        List<Article> articleList = new ArrayList<>();
        for (ArticleEO a : articEOleList) {
//            System.out.println(a);
            Article article = new Article();
            BeanUtils.copyProperties(a, article);
            articleList.add(article);
        }

        // 重新封装成之前的grid格式
        PagedGridResult gridResult = new PagedGridResult();
        gridResult.setRows(articleList);
        gridResult.setPage(page + 1);
        gridResult.setTotal(pagedArticle.getTotalPages());
        gridResult.setRecords(pagedArticle.getTotalElements());

        gridResult = rebuildArticleGrid(gridResult);

        return GraceJSONResult.ok(gridResult);
    }

第三节 ES实践粉丝操作

3.1 创建索引保存es

  1. 定义EO模型com.imooc.pojo.eo.FansEO
package com.imooc.pojo.eo;

import lombok.Data;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;

import javax.persistence.Id;
        
@Data
@Document(indexName = "fans", type = "_doc")
public class FansEO {
    @Id
    private String id;
    @Field
    private String writerId;
    @Field
    private String fanId;
    @Field
    private String face;
    @Field
    private String fanNickname;
    @Field
    private Integer sex;
    @Field
    private String province;
}
  1. es的配置复制到user中。
  data:
     mongodb:
       uri: mongodb://root:[email protected]:27017
     database: imooc-news
    elasticsearch:
      cluster-name: docker-cluster
      cluster-nodes: 192.168.44.128:9300
  1. follow中保存粉丝到es, unfollow删除粉丝到es。com.imooc.user.service.impl.MyFanServiceImpl
    @Autowired
    private ElasticsearchTemplate esTemplate;

        // 保存粉丝关系到es中
        FansEO fansEO = new FansEO();
        BeanUtils.copyProperties(fans, fansEO);
        IndexQuery iq = new IndexQueryBuilder().withObject(fansEO).build();
        esTemplate.index(iq);

        // 删除es中的粉丝关系, DeleteQuery: 根据条件删除
        DeleteQuery dq = new DeleteQuery();
        dq.setQuery(QueryBuilders.termQuery("writerId", writerId));
        dq.setQuery(QueryBuilders.termQuery("fanId", fanId));
        esTemplate.delete(dq, FansEO.class);

3.2 es查询粉丝

  1. 添加接口com.imooc.user.service.MyFanService#queryMyFansESList
    public PagedGridResult queryMyFansESList(String writerId,
                                             Integer page,
                                             Integer pageSize);
  1. 实现接口
    @Override
    public PagedGridResult queryMyFansESList(String writerId,
                                             Integer page,
                                             Integer pageSize) {

        page--;
        Pageable pageable = PageRequest.of(page, pageSize);

        SearchQuery query = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.termQuery("writerId", writerId))
                .withPageable(pageable)
                .build();

        AggregatedPage<FansEO> pagedFans = esTemplate.queryForPage(query, FansEO.class);

        PagedGridResult gridResult = new PagedGridResult();
        gridResult.setRows(pagedFans.getContent());
        gridResult.setPage(page + 1);
        gridResult.setTotal(pagedFans.getTotalPages());
        gridResult.setRecords(pagedFans.getTotalElements());

        return gridResult;
    }

3.3 es聚合统计

  1. 统计总数:
GET  http://192.168.44.128:9200/fan/_doc/_count
  1. 统计男/女数量。可以结合head插件来进行搜索:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DiBgqEff-1654791144629)(.\printscreen\7节_2.png)]
  2. 复制json可以更方便自己进行查询
GET  http://192.168.44.128:9200/fan/_doc/_count

{
    "query":{
        "bool":{
            "must":[
                {
                    "match":{
                        "writerId":"2011127T2T7D2HSW"
                    }
                },
                {
                    "term":{
                        "sex":"0"
                    }
                }
            ]
        }
    }
}
  1. 统计不同省份粉丝数量:
GET   http://192.168.44.128:9200/fan/_doc/_count

{
    "query":{
        "bool":{
            "must":[
                {
                    "match":{
                        "writerId":"2009067X5CTFMCH0"
                    }
                },
                {
                    "term":{
                        "province":"北京"
                    }
                }
            ]
        }
    }
}
  1. 上面这些都是通过不同key要执行多次才能得到结果,一般来说我们会使用es的aggs功能做聚合统计,会更好。通过一个脚本来统计男女数量:
POST http://192.168.44.128:9200/fans/_doc/_search
{
    "size": 0,
    "query":{
        "match":{
            "writerId":"201116760SMSZT2W"
        }
    },
    "aggs": {
        "counts": {
            "terms": {
                "field": "sex"
            }
        }
    }
}
  1. 通过一个脚本来统计不同省份的粉丝数量:
POST http://192.168.44.128:9200/fans/_doc/_search
{
    "size": 0,
    "query":{
        "match":{
            "writerId":"201116760SMSZT2W"
        }
    },
    "aggs": {
        "counts": {
            "terms": {
                "field": "province"
            }
        }
    }
}

3.4 es聚合统计业务代码实现

  1. es数据可视化地域统计。 com.imooc.user.service.impl.MyFanServiceImpl#queryRegionRatioESCounts
 @Override
    public List<RegionRatioVO> queryRegionRatioESCounts(String writerId) {
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders
                .terms("region_counts")
                .field("province");

        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.matchQuery("writerId", writerId))
                .addAggregation(aggregationBuilder)
                .build();

        Aggregations aggs = esTemplate.query(searchQuery, new ResultsExtractor<Aggregations>() {
            @Override
            public Aggregations extract(SearchResponse response) {
                return response.getAggregations();
            }
        });

        Map aggMap = aggs.asMap();
        StringTerms strTerms = (StringTerms) aggMap.get("region_counts");
        List bucketList = strTerms.getBuckets();

        List<RegionRatioVO> list = new ArrayList<>();
        for (int i = 0 ; i < bucketList.size() ; i ++) {
            StringTerms.Bucket bucket = (StringTerms.Bucket)bucketList.get(i);
            Long docCount = bucket.getDocCount();
            String key = (String)bucket.getKey();

            System.out.println(key);
            System.out.println(docCount);

            RegionRatioVO regionRatioVO = new RegionRatioVO();
            regionRatioVO.setName(key);
            regionRatioVO.setValue(docCount.intValue());
            list.add(regionRatioVO);
        }

        return list;
    }
  1. com.imooc.user.controller.MyFansController#queryRatioByRegion
    @Override
    public GraceJSONResult queryRatioByRegion(String writerId) {
        return GraceJSONResult.ok(myFanService
                .queryRegionRatioESCounts(writerId));
    }

你可能感兴趣的:(Spring,Cloud分布式微服务实战,搜索引擎,elasticsearch,分布式)