JavaWeb_LeadNews_Day7-ElasticSearch, Mongodb

news2024/11/16 15:50:31

JavaWeb_LeadNews_Day7-ElasticSearch, Mongodb

  • elasticsearch
    • 安装配置
  • app文章搜索
    • 创建索引库
    • app文章搜索
      • 思路分析
      • 具体实现
    • 新增文章创建索引
      • 思路分析
      • 具体实现
  • MongoDB
    • 安装配置
    • SpringBoot集成MongoDB
  • app文章搜索记录
    • 保存搜索记录
      • 思路分析
      • 具体实现
    • 查询搜索历史
    • 删除搜索历史
  • 搜索联想词
  • 来源
  • Gitee

elasticsearch

安装配置

https://blog.csdn.net/Y_cen/article/details/131856995

app文章搜索

创建索引库

  • 使用postman添加映射
    Put请求, Json格式 : http://192.168.174.133:9200/app_info_article
    {
        "mappings":{
            "properties":{
                "id":{
                    "type":"long"
                },
                "publishTime":{
                    "type":"date"
                },
                "layout":{
                    "type":"integer"
                },
                "images":{
                    "type":"keyword",
                    "index": false
                },
                "staticUrl":{
                    "type":"keyword",
                    "index": false
                },
                "authorId": {
                    "type": "long"
                },
                "authorName": {
                    "type": "text"
                },
                "title":{
                    "type":"text",
                    "analyzer":"ik_smart"
                },
                "content":{
                    "type":"text",
                    "analyzer":"ik_smart"
                }
            }
        }
    }
    
  • 依赖
    <!--elasticsearch-->
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-high-level-client</artifactId>
        <version>7.12.1</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch.client</groupId>
        <artifactId>elasticsearch-rest-client</artifactId>
        <version>7.12.1</version>
    </dependency>
    <dependency>
        <groupId>org.elasticsearch</groupId>
        <artifactId>elasticsearch</artifactId>
        <version>7.12.1</version>
    </dependency>
    
  • 导入数据
    @Autowired
    private ApArticleMapper apArticleMapper;
    
    @Autowired
    private RestHighLevelClient restHighLevelClient;
    
    /**
     * 注意:数据量的导入,如果数据量过大,需要分页导入
     * @throws Exception
     */
    @Test
    public void init() throws Exception {
        // 1. 查询所有符合条件的文章数据
        List<SearchArticleVo> searchArticleVos = apArticleMapper.loadArticleList();
        // 2. 批量导入到es索引库
        BulkRequest bulkRequest = new BulkRequest("app_info_article");
        for (SearchArticleVo searchArticleVo : searchArticleVos) {
            IndexRequest indexRequest = new IndexRequest().id(searchArticleVo.getId().toString()).source(JSON.toJSONString(searchArticleVo), XContentType.JSON);
            // 批量添加数据
            bulkRequest.add(indexRequest);
        }
        restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
    }
    

app文章搜索

思路分析

具体实现

  • 配置
    没有使用数据库, 所以无需数据源的自动配置
    spring:
        autoconfigure:
            exclude: org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
    elasticsearch:
        host: 192.168.174.133
        port: 9200
    
  • 业务代码
    @Service
    public class ArticleSearchServiceImpl implements ArticleSearchService {
    
        @Autowired
        private RestHighLevelClient restHighLevelClient;
    
        /**
         * es文章分页检索
         * @param dto
         * @return
         */
        @Override
        public ResponseResult search(UserSearchDto dto) throws IOException {
            // 1. 检查参数
            if(dto == null || StringUtils.isBlank(dto.getSearchWords())){
                return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
            }
            // 2. 设置查询条件
            SearchRequest searchRequest = new SearchRequest("app_info_article");
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
    
            // 布尔查询
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
    
            // 2.1 关键字的分词之后查询
            QueryStringQueryBuilder queryStringQueryBuilder = QueryBuilders.queryStringQuery(dto.getSearchWords()).field("title").field("content").defaultOperator(Operator.OR);
            boolQueryBuilder.must(queryStringQueryBuilder);
    
            // 2.2 查询小于mindate的数据
            RangeQueryBuilder rangeQueryBuilder = QueryBuilders.rangeQuery("publishTime").lt(dto.getMinBehotTime().getTime());
            boolQueryBuilder.filter(rangeQueryBuilder);
    
            // 2.3 分页查询
            searchSourceBuilder.from(0);
            searchSourceBuilder.size(dto.getPageSize());
    
            // 2.4 按照发布时间倒序查询
            searchSourceBuilder.sort("publishTime", SortOrder.DESC);
    
            // 2.5 设置高亮 title
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("title");
            highlightBuilder.preTags("<font style='color: red; font-size: inherit;'>");
            highlightBuilder.postTags("</font>");
            searchSourceBuilder.highlighter(highlightBuilder);
    
            searchSourceBuilder.query(boolQueryBuilder);
            searchRequest.source(searchSourceBuilder);
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
    
            // 3. 结果封装返回
            List<Map> list = new ArrayList();
            SearchHit[] hits = searchResponse.getHits().getHits();
            for (SearchHit hit : hits) {
                String json = hit.getSourceAsString();
                Map map = JSON.parseObject(json, Map.class);
                // 处理高亮
                if(hit.getHighlightFields() != null && hit.getHighlightFields().size() > 0){
                    Text[] titles = hit.getHighlightFields().get("title").getFragments();
                    String title = StringUtils.join(titles);
                    // 高亮标题
                    map.put("h_title", title);
                }else{
                    // 原始标题
                    map.put("h_title", map.get("title"));
                }
                list.add(map);
            }
    
            return ResponseResult.okResult(list);
        }
    }
    
  • 网关配置
    app端网关
    #搜索微服务
    - id: leadnews-search
        uri: lb://leadnews-search
        predicates:
            - Path=/search/**
        filters:
            - StripPrefix= 1
    

新增文章创建索引

思路分析

具体实现

  • Producer
    SearchArticleVo需要静态地址, 所以需要在FreeMarker生成静态文章, 上传到MinIo之后再发送创建索引的消息
    @Service
    @Slf4j
    @Transactional
    public class ArticleFreemarkerServiceImpl implements ArticleFreemarkerService {
    
        ...
        @Override
        @Async
        public void buildArticleToMinIO(ApArticle article, String content) {
            if(StringUtils.isNotBlank(content)) {
                ...
    
                // kafka: 新增文章创建索引, 发送消息
                createArticleESIndex(article, content, path);
            }
        }
    
        @Autowired
        private KafkaTemplate<String, String> kafkaTemplate;
    
        private void createArticleESIndex(ApArticle article, String content, String path) {
    
            SearchArticleVo searchArticleVo = new SearchArticleVo();
            BeanUtils.copyProperties(article, searchArticleVo);
            searchArticleVo.setContent(content);
            searchArticleVo.setStaticUrl(path);
    
            kafkaTemplate.send(ArticleConstants.ARTICLE_ES_SYNC_TOPIC, JSON.toJSONString(searchArticleVo));
        }
    }
    
  • Listener
    @Component
    @Slf4j
    public class SyncArticleListener {
    
        @Autowired
        private RestHighLevelClient restHighLevelClient;
    
        @KafkaListener(topics = ArticleConstants.ARTICLE_ES_SYNC_TOPIC)
        public void onMessage(String message)
        {
            if(StringUtils.isNotBlank(message)){
    
                log.info("SyncArticleListener, message={}", message);
    
                SearchArticleVo vo = JSON.parseObject(message, SearchArticleVo.class);
                IndexRequest indexRequest = new IndexRequest("app_info_article");
                indexRequest.id(vo.getId().toString());
                indexRequest.source(message, XContentType.JSON);
                try {
                    restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    log.error("sync es error={}", e);
                    throw new RuntimeException(e);
                }
    
            }
        }
    
    }
    

MongoDB

安装配置

# 拉取镜像
docker pull mongo
# 创建容器
docker run -di --name mongo -p 27017:27017 -v ~/data/mongodata:/data mongo

SpringBoot集成MongoDB

  • 依赖
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-mongodb</artifactId>
    </dependency>
    
  • 配置
    spring:
        data:
            mongodb:
                host: 192.168.174.133
                port: 27017
                database: leadnews-history
    
  • 使用
    public class MongoTest {
    
    
        @Autowired
        private MongoTemplate mongoTemplate;
    
        //保存
        @Test
        public void saveTest(){
            ApAssociateWords apAssociateWords = new ApAssociateWords();
            apAssociateWords.setAssociateWords("黑马头条");
            apAssociateWords.setCreatedTime(new Date());
            mongoTemplate.save(apAssociateWords);
        }
    
        //查询一个
        @Test
        public void saveFindOne(){
            ApAssociateWords apAssociateWords = mongoTemplate.findById("64e46fd4f3a760442bf50527", ApAssociateWords.class);
            System.out.println(apAssociateWords);
        }
    
        //条件查询
        @Test
        public void testQuery(){
            Query query = Query.query(Criteria.where("associateWords").is("黑马头条"))
                    .with(Sort.by(Sort.Direction.DESC,"createdTime"));
            List<ApAssociateWords> apAssociateWordsList = mongoTemplate.find(query, ApAssociateWords.class);
            System.out.println(apAssociateWordsList);
        }
    
        @Test
        public void testDel(){
            mongoTemplate.remove(Query.query(Criteria.where("associateWords").is("黑马头条")),ApAssociateWords.class);
        }
    }
    

app文章搜索记录

保存搜索记录

思路分析

异步保存

保存数据

具体实现

public class ApUserSearchServiceImpl implements ApUserSearchService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 保存用户搜索记录
     * @param keyword
     * @param userId
     */
    @Override
    @Async
    public void insert(String keyword, Integer userId) {
        // 1. 查询当前用户的搜索关键词
        Query query = Query.query(Criteria.where("userId").is(userId).and("keyword").is(keyword));
        ApUserSearch apUserSearch = mongoTemplate.findOne(query, ApUserSearch.class);

        // 2. 存在, 更新创建时间
        if(apUserSearch != null){
            apUserSearch.setCreatedTime(new Date());
            mongoTemplate.save(apUserSearch);
            return;
        }

        // 3. 不存在, 判断当前历史记录总数量是否超过10
        apUserSearch = new ApUserSearch();
        apUserSearch.setUserId(userId);
        apUserSearch.setKeyword(keyword);
        apUserSearch.setCreatedTime(new Date());

        Query query1 = Query.query(Criteria.where("userId").is(userId));
        query1.with(Sort.by(Sort.Direction.DESC, "createTime"));
        List<ApUserSearch> apUserSearchList = mongoTemplate.find(query1, ApUserSearch.class);

        if(apUserSearchList == null || apUserSearchList.size() < 10){
            mongoTemplate.save(apUserSearch);
        }else{
            ApUserSearch lastUserSearch = apUserSearchList.get(apUserSearchList.size() - 1);
            mongoTemplate.findAndReplace(Query.query(Criteria.where("id").is(lastUserSearch.getId())), apUserSearch);
        }
    }
}

在文章搜索中调用, 因为未登录也可能有token(游客), 所以需要判断是否登录.

ApUser user = AppThreadLocalUtil.getUser();
// 异步调用, 保存搜索记录
if(user!=null && dto.getFromIndex() == 0){
    apUserSearchService.insert(dto.getSearchWords(), user.getId());
}

查询搜索历史

public ResponseResult findUserSearch() {
    // 获取当前用户
    ApUser user = AppThreadLocalUtil.getUser();
    if(user == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 根据用户查询数据, 根据时间排序
    Query query = Query.query(Criteria.where("userId").is(user.getId()));
    query.with(Sort.by(Sort.Direction.DESC, "createdTime"));
    List<ApUserSearch> apUserSearchList = mongoTemplate.find(query, ApUserSearch.class);
    return ResponseResult.okResult(apUserSearchList);
}

删除搜索历史

public ResponseResult delUserSearch(HistorySearchDto dto) {
    // 检查参数
    if(dto.getId() == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
    }
    // 获取当前用户
    ApUser user = AppThreadLocalUtil.getUser();
    if(user == null){
        return ResponseResult.errorResult(AppHttpCodeEnum.NEED_LOGIN);
    }
    // 删除
    Query query = Query.query(Criteria.where("id").is(dto.getId()).and("userId").is(user.getId()));
    mongoTemplate.remove(query, ApUserSearch.class);
    return ResponseResult.okResult(AppHttpCodeEnum.SUCCESS);
}

搜索联想词

public class ApAssociateWordsServiceImpl implements ApAssociateWordsService {

    @Autowired
    private MongoTemplate mongoTemplate;

    /**
     * 搜索联想词
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) {
        // 1. 检查参数
        if(StringUtils.isBlank(dto.getSearchWords())){
            return ResponseResult.errorResult(AppHttpCodeEnum.PARAM_INVALID);
        }
        // 2. 分页检索
        if(dto.getPageSize() > 20){
            dto.setPageSize(20);
        }
        // 3. 执行查询, 模糊查询
        Query query = Query.query(Criteria.where("associateWords").regex(".*?\\" + dto.getSearchWords() + ".*"));
        query.limit(dto.getPageSize());
        List<ApAssociateWords> apAssociateWordsList = mongoTemplate.find(query, ApAssociateWords.class);
        return ResponseResult.okResult(apAssociateWordsList);
    }
}

来源

黑马程序员. 黑马头条

Gitee

https://gitee.com/yu-ba-ba-ba/leadnews

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/921958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

关于java三元组的问题

在改代码的时候&#xff0c;发现一个奇怪的地方&#xff0c;举例如下 Testpublic void buildTest(){TT t new TT();Long time tnull?System.currentTimeMillis():t.getTime();System.out.println("done");}Datapublic static class TT{Long time;}这个地方运行就…

STL---vector

目录 1.vector的介绍及使用 2.vector接口说明及模拟实现 2.1vector定义 2.2vector迭代器的使用 2.3vector容量 2.4vector增删查改 3迭代器失效 4.使用memcpy拷贝 5.模拟实现 1.vector的介绍及使用 vector的文档介绍 1. vector是表示可变大小数组的序列容器。 2. 就像数…

电子病历系统EMR

电子病历系统EMR源码 一体化电子病历系统基于云端SaaS服务的方式&#xff0c;采用B/S&#xff08;Browser/Server&#xff09;架构提供&#xff0c;覆盖了医疗机构电子病历模板制作到管理使用的整个流程。除实现在线制作内容丰富、图文并茂、功能完善的电子病历模板外&#xff…

phpcms重置密码为123456

0b817b72c5e28b61b32ab813fd1ebd7f3vbCrK

实战项目ssm权限系统 3-总结篇,权限模块保护业务模块

一 工程模块介绍 1.1 工程模块关系 在业务微服务模块中引入安全认证模块&#xff0c;起到对业务模块的认证授权保护

ubuntu18.04复现yolo v8环境配置之CUDA与pytorch版本问题以及多CUDA版本安装及切换

最近在复现yolo v8的程序&#xff0c;特记录一下过程 环境&#xff1a;ubuntu18.04ros melodic 小知识&#xff1a;GPU并行计算能力高于CPU—B站UP主说的 Ubuntu可以安装多个版本的CUDA。如果某个程序的Pyorch需要不同版本的CUDA&#xff0c;不必删除之前的CUDA&#xff0c;…

我是如何使用Spring Retry减少1000 行代码

使用 Spring Retry 重构代码的综合指南。 问题介绍 在我的日常工作中&#xff0c;我主要负责开发一个庞大的金融应用程序。当客户发送请求时&#xff0c;我们使用他们的用户 ID 从第三方服务获取他们的帐户信息&#xff0c;保存交易并更新缓存中的详细信息。尽管整个流程看起来…

APT60DQ20BG-ASEMI新能源功率器件APT60DQ20BG

编辑&#xff1a;ll APT60DQ20BG-ASEMI新能源功率器件APT60DQ20BG 型号&#xff1a;APT60DQ20BG 品牌&#xff1a;ASEMI 封装&#xff1a;TO-3P 恢复时间&#xff1a;&#xff1e;50ns 正向电流&#xff1a;60A 反向耐压&#xff1a;200V 芯片个数&#xff1a;2 引脚数…

Springboot+dynamic-datasource+Druid数据库配置加密

Springbootmybatis-plusdynamic-datasourceDruid数据库配置加密 文章目录 0.前言1. 动态添加移除数据源2.基础介绍3. 使用步骤示例简单方式&#xff0c;使用默认的加密1. 使用下面 工具类输出&#xff0c;加密后的密码1. 将上面加密后的密码配置到配置文件中如果使用的默认key…

完全免费的GPT,最新整理,2023年8月24日,已人工验证,不用注册,不用登录,更不用魔法,点开就能用

完全免费的ChatGPT&#xff0c;最新整理&#xff0c;2023年8月24日&#xff0c;已人工验证&#xff0c; 不用注册&#xff0c;不用登录&#xff0c;更不用魔法&#xff0c;点开就能用&#xff01; 第一个&#xff1a;网址地址统一放在文末啦&#xff01;文末直达 看上图你就能…

基于ssm的共享客栈管理系统源码和论文

基于ssm的共享客栈管理系统源码和论文058 开发工具&#xff1a;idea 数据库mysql5.7 数据库链接工具&#xff1a;navcat,小海豚等 技术&#xff1a;ssm 摘 要 互联网发展至今&#xff0c;无论是其理论还是技术都已经成熟&#xff0c;而且它广泛参与在社会中的方方面面。…

ORB-SLAM2算法10之图像关键帧KeyFrame

文章目录 0 引言1 KeyFrame类1.1 构造函数1.2 成员函数1.3 关键帧之间共视图1.3.1 AddConnection1.3.2 UpdateBestCovisibles1.3.3 UpdateConnections1.3.4 EraseConnection1.3.5 SetBadFlag 1.4 地图点1.5 生成树 2 KeyFrame用途 0 引言 ORB-SLAM2算法7详细了解了System主类和…

机器学习实战之模型的解释性:Scikit-Learn的SHAP和LIME库详解

引言&#xff1a;机器学习模型的“黑箱”困境 机器学习模型的崛起让我们惊叹不已&#xff01;不论是预测房价、识别图片中的猫狗&#xff0c;还是推荐给你喜欢的音乐&#xff0c;这些模型都表现得非常出色。但是&#xff0c;有没有想过&#xff0c;这些模型到底是如何做出这些决…

STM32 F103C8T6学习笔记13:IIC通信—AHT10温湿度传感器模块

今日学习一下这款AHT10 温湿度传感器模块&#xff0c;给我的OLED手环添加上测温湿度的功能。 文章提供源码、测试工程下载、测试效果图。 目录 AHT10温湿度传感器&#xff1a; 特性&#xff1a; 连接方式&#xff1a; 适用场所范围&#xff1a; 程序设计&#xff1a; 设…

大模型技术实践(二)|关于Llama 2你需要知道的那些事儿

在上期文章中&#xff0c;我们简要回顾了Llama模型的概况&#xff0c;本期文章我们将详细探讨【关于Llama 2】&#xff0c;你需要知道的那些事儿。 01-Llama 2的性能有多好&#xff1f; 作为Meta新发布的SOTA开源大型语言模型&#xff0c;Llama 2是Llama模型的延续和升级。Ll…

免费的png打包plist工具CppTextu,一款把若干资源图片拼接为一张大图的免费工具

经常做游戏打包贴图的都知道&#xff0c;要把图片打包为一张或多张大图&#xff0c;要使用打包工具TexturePacker。 TexturePacker官方版可以直接导入PSD、SWF、PNG、BMP等常见的图片格式&#xff0c;主要用于网页、游戏和动画的制作&#xff0c;它可以将多个小图片汇聚成一个…

java八股文面试[java基础]——CGLIB动态代理与JDK动态代理

CGLIB CGLIB简介&#xff1a; 什么是CGLIB CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架&#xff08;Spring、dynaop&#xff09;中&#xff0c;用以提供方法拦截操作。Hibernate作为一个比较受欢迎的ORM框架&#xff0c;同样使用CGLIB来代理单端&#xff…

iPhone 15预计在A16仿生芯片上运行,性能将有何提升?

苹果最新的移动芯片A17仿生芯片无疑让人兴奋不已&#xff0c;该芯片将成为今年一些iPhone 15机型的驱动力。A17基于3nm处理器&#xff0c;这是第一款提出这一主张的移动硅&#xff0c;由于其更紧凑的尺寸&#xff0c;它有望在性能和能效方面都有所提高。今年秋天买一部A17供电的…

【Java架构-包管理工具】-Maven私服搭建-Nexus(三)

本文摘要 Maven作为Java后端使用频率非常高的一款依赖管理工具&#xff0c;在此咱们由浅入深&#xff0c;分三篇文章&#xff08;Maven基础、Maven进阶、私服搭建&#xff09;来深入学习Maven&#xff0c;此篇为开篇主要介绍Maven私服搭建-Nexus 文章目录 本文摘要1. Nexus安装…

爬虫逆向实战(二十)--某99网站登录

一、数据接口分析 主页地址&#xff1a;某99网站 1、抓包 通过抓包可以发现登录接口是AC_userlogin 2、判断是否有加密参数 请求参数是否加密&#xff1f; 通过查看“载荷”可以发现txtPassword和aws是加密参数 请求头是否加密&#xff1f; 无响应是否加密&#xff1f; 无…