ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

仿牛客网第六章

2021-06-02 14:04:30  阅读:241  来源: 互联网

标签:hit 仿牛 String toString 客网 第六章 new post public


一、Elasticsearch入门

  • Elasticsearch简介

    • 一个分布式的、Restful风格的搜索引擎。
    • 支持对各种类型的数据的检索。
    • 搜索速度快,可以提供实时的搜索服务。
    • 便于水平扩展,每秒可以处理PB级海量数据。
  • Elasticsearch术语

    • 索引、类型、文档、字段。
    • 集群、节点、分片、副本。

术语的解释

  • 索引:相当于数据库中的database 改版后作为table
  • 类型:相当于数据库中的table 不再使用
  • 文档:相当于数据库中的一行数据,数据结构为JSON
  • 字段:相当于数据库中的一列

Elasticsearch6.0以后开始逐步废除类型的概念,索引的含义中也包括了类型。

  • 集群:分布式部署,提高性能
  • 节点:集群中的每一台服务器
  • 分片:对一个索引的进一步划分存储,提高并发处理能力
  • 副本:对分片的备份,提高可用性
    image
    Elasticsearch相关链接:官网

Elasticsearch选择下载6.4.3版本和SpringBoot兼容

Elasticsearch配置

文件位置config/elasticsearch.yml

image配置环境变量

image

安装中文分词插件

github上找

image

image

解压到指定目录下

image

ik插件配置文件说明

IKAnalyzer.cfg 可以自己配置新词

imageimage

安装Postman模拟网页访问

Postman相关链接:官网

使用命令行操作Elasticsearch

1.启动ES—./bin/elasticsearch.bat

2.常用命令介绍

  • 查看节点健康状态
curl -X GET "localhost:9200/_cat/health?v"

这个命令需要下载curl相关包:参考链接

image

  • 查看节点具体信息
curl -X GET "localhost:9200/_cat/nodes?v"

image

  • 查看索引相关信息
curl -X GET "localhost:9200/_cat/indices?v"

新装的没有索引:

image

  • 创建索引
curl -X PUT "localhost:9200/test"  //test就是索引名字

image

查看状态显示yellow是因为没有分片(备份)。

image

  • 删除索引
curl -X DELETE "localhost:9200/test"  //test就是索引名字

image

使用Postman/RESTer访问ES

RESTer是火狐插件也很好用。

  • 查索引
    image
  • 建索引
    image
  • 删除索引
    image
  • 提交数据
//test:索引  _doc:固定格式  1:id号 然后在请求body中写数据
PUT localhost:9200/test/_doc/1   

image

  • 查数据
GET localhost:9200/test/_doc/1

image

  • 改数据

和添加数据一样,底层会先删除再添加

  • 删除数据
DELETE localhost:9200/test/_doc/1

image

  • 搜索功能的演示

    • 先插入一些数据
      imageimage
      image
  • 开始搜索演示
    image

搜索时会先分词然后搜索,并不一定完全匹配

image

二、Spring整合Elasticsearch

  • 引入依赖

    • spring-boot-starter-data-elasticsearch
  • 配置Elasticsearch

    • cluster-name、cluster-nodes
  • Spring Data Elasticsearch

    • ElasticsearchTemplate
    • ElasticsearchRepository
      image

导包

<dependency>
    <groupId>org.springframework.data</groupId>
    <artifactId>spring-data-elasticsearch</artifactId>
</dependency>

配置

1.application.properties

spring:
  data:
    elasticsearch:
      cluster-name: nowcoder
      cluster-nodes: 127.0.0.1:9300

2.解决Netty冲突问题

问题原因:Redis底层使用了Netty,Elasticsearch也用了Netty,当被注册两次就会报错

image

解决思路:Elasticsearch中注册Netty前会判断有无一个参数,如果有就不注册

image这么解决:

image

3.给discussPost加注解

//indexName:索引名,type:固定_doc,shards:分片,replicas:备份
@Document(indexName = "discusspost",type = "_doc",shards =6 ,replicas = 2)
public class DiscussPost {
    @Id
    private int id;
    @Field(type = FieldType.Integer)
    private int userId;
    //analyzer:互联网校招--->建立最大的索引(就是各种拆分)
    //searchAnalyzer 拆分尽可能少的满足意图的分词器
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
    private String title;
    @Field(type = FieldType.Text,analyzer = "ik_max_word",searchAnalyzer = "ik_smart")
    private String content;
    @Field(type = FieldType.Integer)
    //0-普通; 1-置顶;
    private int type;
    @Field(type = FieldType.Integer)
    //0-正常; 1-精华; 2-拉黑;
    private int status;
    @Field(type = FieldType.Date)
    private Date createTime;
    @Field(type = FieldType.Integer)
    private int commentCount;
    @Field(type = FieldType.Double)
    private double score;

写数据层

在dao下建立子包elasticsearch,并创建DiscussPostRepository接口

image

测试一波

Elasticsearch中配置再加一个这个:

image

elasticsearch包不要放在dao包下边,会导致bean注入失败。

@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class ElasticsearchTest {
    @Autowired
    private DiscussPostMapper discussMapper;
    @Autowired
    private DiscussPostRepository discussRepository;
    //有些功能上边的解决不了,所以引入下边的
    @Autowired
    private ElasticsearchTemplate elasticTemplate;
    @Test
    public void testInsert() {
        discussRepository.save(discussMapper.selectDiscussPostById(271));
        discussRepository.save(discussMapper.selectDiscussPostById(272));
        discussRepository.save(discussMapper.selectDiscussPostById(273));
    }
    @Test
    public void testInsertList() {
        discussRepository.saveAll(discussMapper.selectDiscussPosts(101, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(102, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(103, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(111, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(112, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(131, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(132, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(133, 0, 100));
        discussRepository.saveAll(discussMapper.selectDiscussPosts(134, 0, 100));
    }
    //localhost:9200/discusspost/_doc/231
    @Test
    public void testUpdate() {
        DiscussPost post = discussMapper.selectDiscussPostById(231);
        post.setContent("我是新人,使劲灌水.");
        discussRepository.save(post);
    }
    @Test
    public void testDelete() {
        discussRepository.deleteById(231);
        //discussRepository.deleteAll();
    }
    @Test
    public void testSearchByRepository() {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)) //按字段排序
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0, 10)) //分页
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
        // 底层调用:elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)
        // 底层获取得到了高亮显示的值, 但是没有返回.所以为了得到高亮显示直接用elasticTemplate.queryForPage见下面
        Page<DiscussPost> page = discussRepository.search(searchQuery);
        System.out.println(page.getTotalElements());
        System.out.println(page.getTotalPages());
        System.out.println(page.getNumber());
        System.out.println(page.getSize());
        for (DiscussPost post : page) {
            System.out.println(post);
        }
    }
    @Test
    public void testSearchByTemplate() {
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content"))
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(0, 10))
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
        Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                SearchHits hits = response.getHits();
                if (hits.getTotalHits() <= 0) {
                    return null;
                }
                List<DiscussPost> list = new ArrayList<>();
                for (SearchHit hit : hits) {
                    DiscussPost post = new DiscussPost();
                    String id = hit.getSourceAsMap().get("id").toString();
                    post.setId(Integer.valueOf(id));
                    String userId = hit.getSourceAsMap().get("userId").toString();
                    post.setUserId(Integer.valueOf(userId));
                    String title = hit.getSourceAsMap().get("title").toString();
                    post.setTitle(title);
                    String content = hit.getSourceAsMap().get("content").toString();
                    post.setContent(content);
                    String status = hit.getSourceAsMap().get("status").toString();
                    post.setStatus(Integer.valueOf(status));
                    String createTime = hit.getSourceAsMap().get("createTime").toString(); //long类型的字符串
                    post.setCreateTime(new Date(Long.valueOf(createTime)));
                    String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                    post.setCommentCount(Integer.valueOf(commentCount));
                    // 处理高亮显示的结果
                    HighlightField titleField = hit.getHighlightFields().get("title");
                    if (titleField != null) {
                        post.setTitle(titleField.getFragments()[0].toString());
                    }
                    HighlightField contentField = hit.getHighlightFields().get("content");
                    if (contentField != null) {
                        post.setContent(contentField.getFragments()[0].toString());
                    }
                    list.add(post);
                }
                return new AggregatedPageImpl(list, pageable,
                        hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());
            }
            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });
        System.out.println(page.getTotalElements());
        System.out.println(page.getTotalPages());
        System.out.println(page.getNumber());
        System.out.println(page.getSize());
        for (DiscussPost post : page) {
            System.out.println(post);
        }
    }
}

三、开发社区搜索功能

  • 搜索服务

    • 将帖子保存至Elasticsearch服务器。
    • 从Elasticsearch服务器删除帖子。
    • 从Elasticsearch服务器搜索帖子。
  • 发布事件

    • 发布帖子时,将帖子异步的提交到Elasticsearch服务器。
    • 增加评论时,将帖子异步的提交到Elasticsearch服务器。
    • 在消费组件中增加一个方法,消费帖子发布事件。
  • 显示结果

    • 在控制器中处理搜索请求,在HTML上显示搜索结果。

一个小问题的解决

image

ElasticsearchService

@Service
public class ElasticsearchService {
    @Autowired
    private DiscussPostRepository discussPostRepository;
    @Autowired
    private ElasticsearchTemplate elasticTemplate;//高亮显示
    //添加、修改
    public void saveDiscussPost(DiscussPost discussPost){
        discussPostRepository.save(discussPost);
    }
    //删除
    public void deleteDiscussPost(int id){
        discussPostRepository.deleteById(id);
    }
    //查询
    public Page<DiscussPost> searchDiscussPost(String keyword,int current,int limit){
        SearchQuery searchQuery = new NativeSearchQueryBuilder()
                .withQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content"))
                .withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC))
                .withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC))
                .withPageable(PageRequest.of(current, limit))
                .withHighlightFields(
                        new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),
                        new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")
                ).build();
        return elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {
            @Override
            public <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {
                SearchHits hits = response.getHits();
                if (hits.getTotalHits() <= 0) {
                    return null;
                }
                List<DiscussPost> list = new ArrayList<>();
                for (SearchHit hit : hits) {
                    DiscussPost post = new DiscussPost();
                    String id = hit.getSourceAsMap().get("id").toString();
                    post.setId(Integer.valueOf(id));
                    String userId = hit.getSourceAsMap().get("userId").toString();
                    post.setUserId(Integer.valueOf(userId));
                    String title = hit.getSourceAsMap().get("title").toString();
                    post.setTitle(title);
                    String content = hit.getSourceAsMap().get("content").toString();
                    post.setContent(content);
                    String status = hit.getSourceAsMap().get("status").toString();
                    post.setStatus(Integer.valueOf(status));
                    String createTime = hit.getSourceAsMap().get("createTime").toString(); //long类型的字符串
                    post.setCreateTime(new Date(Long.valueOf(createTime)));
                    String commentCount = hit.getSourceAsMap().get("commentCount").toString();
                    post.setCommentCount(Integer.valueOf(commentCount));
                    // 处理高亮显示的结果
                    HighlightField titleField = hit.getHighlightFields().get("title");
                    if (titleField != null) {
                        post.setTitle(titleField.getFragments()[0].toString());
                    }
                    HighlightField contentField = hit.getHighlightFields().get("content");
                    if (contentField != null) {
                        post.setContent(contentField.getFragments()[0].toString());
                    }
                    list.add(post);
                }
                return new AggregatedPageImpl(list, pageable,
                        hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());
            }
            @Override
            public <T> T mapSearchHit(SearchHit searchHit, Class<T> aClass) {
                return null;
            }
        });    
    }
}

处理DiscussPostController.addDiscussPost

image

处理CommentController.addComment

image

EventConsumer写一个消费发帖事件的方法

@KafkaListener(topics = {TOPIC_PUBLISH})
    public void handlePublishMessage(ConsumerRecord record){
        if(record==null||record.value()==null){
            logger.error("消息的内容为空");
            return;
        }
        Event event = JSONObject.parseObject(record.value().toString(),Event.class);
        if(event==null){
            logger.error("消息格式错误");
            return;
        }
        //查询出这个帖子
        DiscussPost post = discussPostService.findDiscussPostById(event.getEntityId());
        //往es中存数据
        elasticsearchService.saveDiscussPost(post);
    }
• 1
• 2
• 3
• 4
• 5
• 6
• 7
• 8
• 9
• 10
• 11
• 12
• 13
• 14
• 15
• 16
• 17

新写一个SearchController

@Controller
public class SearchController implements CommunityContant {
    @Autowired
    private ElasticsearchService elasticsearchService;
    @Autowired
    private UserService userService;
    @Autowired
    private LikeService likeService;
    @RequestMapping(path="/search",method = RequestMethod.GET)
    //路径 search?keyword=xxx
    public String search(String keyword, Page page, Model model){
        //搜索帖子
        org.springframework.data.domain.Page<DiscussPost> 
                searchResult = elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());
        //聚合数据
        List<Map<String,Object>> discussPosts = new ArrayList<>();
        if(searchResult!=null){
            for(DiscussPost post:searchResult){
                Map<String,Object> map = new HashMap<>();
                //帖子
                map.put("post",post);
                //作者
                map.put("user",userService.findUserById(post.getUserId()));
                //点赞数量
                map.put("likeCount",likeService.findEntityLikeCount(ENTITY_TYPE_POST,post.getId()));
                discussPosts.add(map);
            }
        }
        model.addAttribute("discussPosts",discussPosts);
        model.addAttribute("keyword",keyword);
        
        //分页信息
        page.setPath("/search?keyword="+keyword);
        page.setRows(searchResult==null?0:(int)searchResult.getTotalElements());
        return "/site/search";
    }
}

处理页面index处理搜索框

image

处理页面search

image

突然发现之前的一个bug

MessageController.getNoticeList

先把message加入messageVo,不然模板判断显示不显示的时候会报null

image

标签:hit,仿牛,String,toString,客网,第六章,new,post,public
来源: https://www.cnblogs.com/xiaochenNN/p/14840983.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有