搜索自动补全-elasticsearch实现

news2024/9/22 2:42:40

1. elasticsearch准备

1.1 拼音分词器

github地址:https://github.com/infinilabs/analysis-pinyin/releases?page=6
必须与elasticsearch的版本相同
在这里插入图片描述第四步,重启es

docker restart es

1.2 定义索引库

PUT /app_info_article
{
  "settings": {
    "analysis": {
      "analyzer": {
        "text_anlyzer": {
          "tokenizer": "ik_max_word",
          "filter": "py"
        },
        "completion_analyzer": {
          "tokenizer": "keyword",
          "filter": "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":"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":"text_anlyzer",
                "search_analyzer": "ik_max_word", 
                "copy_to": "all"
            },
            "content":{
                "type":"text",
                "analyzer":"text_anlyzer",
                "search_analyzer": "ik_max_word", 
                "copy_to": "all"
            },
            "all":{
              "type": "text",
              "analyzer": "ik_max_word"
            },
            "suggestion":{
              "type": "completion",
              "analyzer": "completion_analyzer"
            }
        }
    }
}

1.3 给索引库添加文档

详情参考我的另一篇博客: xxljob分片广播+多线程实现高效定时同步elasticsearch索引库
app_info_article对应的pojo类

@Data
public class SearchArticleVo {

    // 文章id
    private Long id;
    // 文章标题
    private String title;
    // 文章发布时间
    private Date publishTime;
    // 文章布局
    private Integer layout;
    // 封面
    private String images;
    // 作者id
    private Long authorId;
    // 作者名词
    private String authorName;
    //静态url
    private String staticUrl;
    //文章内容
    private String content;

    //状态
    private int enable;

    //单词自动补全
    private List<String> suggestion;

    public void initSuggestion(){
        suggestion = new ArrayList<String>();
        suggestion.add(this.title);
        suggestion.add(this.authorName);
    }
}

核心代码

@XxlJob("syncIndex")
    public void syncIndex()  {
        //1、获取任务传入的参数   {"minSize":100,"size":10}
        String jobParam = XxlJobHelper.getJobParam();
        Map<String,Integer> jobData = JSON.parseObject(jobParam,Map.class);
        int minSize = jobData.get("minSize"); //分片处理的最小总数据条数
        int size =  jobData.get("size"); //分页查询的每页条数   小分页

        //2、查询需要处理的总数据量  total=IArticleClient.searchTotal()
        Long total = articleClient.searchTotal();

        //3、判断当前分片是否属于第1片,不属于,则需要判断总数量是否大于指定的数据量[minSize],大于,则执行任务处理,小于或等于,则直接结束任务
        int cn = XxlJobHelper.getShardIndex(); //当前节点的下标
        if(total<=minSize && cn!=0){
            //结束
            return;
        }
        //4、执行任务   [index-范围]   大的分片分页处理
        //4.1:节点个数
        int n = XxlJobHelper.getShardTotal();
        //4.2:当前节点处理的数据量
        int count = (int) (total % n==0? total/n :  (total/n)+1);
        //4.3:确定当前节点处理的数据范围
        //从下标为index的数据开始处理  limit #{index},#{count}
        int indexStart = cn*count;
        int indexEnd = cn*count+count-1; //最大的范围的最后一个数据的下标
        //5.小的分页查询和批量处理
        int index =indexStart; //第1页的index

        System.out.println("分片个数是【"+n+"】,当前分片下标【"+cn+"】,处理的数据下标范围【"+indexStart+"-"+indexEnd+"】");
        do {
            //=============================================小分页================================
            //5.1:分页查询
            //5.2:将数据导入ES
            push(index,size,indexEnd);

            //5.3:是否要查询下一页 index+size
            index = index+size;
        }while (index<=indexEnd);
    }


    /**
     * 数据批量导入
     * @param index
     * @param size
     * @param indexEnd
     * @throws IOException
     */
    public void push(int index,int size,int indexEnd)  {

        pool.execute(()->{
            System.out.println("当前线程处理的分页数据是【index="+index+",size="+(index+size>indexEnd? indexEnd-index+1 : size)+"】");
            //1)查询数据库数据
            List<SearchArticleVo> searchArticleVos = articleClient.searchPage(index, index+size>indexEnd? indexEnd-index+1 : size);  //size可能越界
                                                                                                       // 第1页  index=0
                                                                                                       //       indexEnd=6
                                                                                                       // 第2页  index=5
                                                                                                       //       indexEnd-index+=2
            //2)创建BulkRequest - 刷新策略
            BulkRequest bulkRequest = new BulkRequest()
                    //刷新策略-立即刷新
                    .setRefreshPolicy(WriteRequest.RefreshPolicy.IMMEDIATE);
            for (SearchArticleVo searchArticleVo : searchArticleVos) {
                //A:创建XxxRequest
                searchArticleVo.initSuggestion();
                IndexRequest indexRequest = new IndexRequest("app_info_article")
                        //B:向XxxRequest封装DSL语句数据
                        .id(searchArticleVo.getId().toString())
                        .source(com.alibaba.fastjson.JSON.toJSONString(searchArticleVo), XContentType.JSON);

                //3)将XxxRequest添加到BulkRequest
                bulkRequest.add(indexRequest);
            }

            //4)使用RestHighLevelClient将BulkRequest添加到索引库
            if(searchArticleVos!=null && searchArticleVos.size()>0){
                try {
                    restHighLevelClient.bulk(bulkRequest, RequestOptions.DEFAULT);
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

在xxl-job任务调度平台执行一次该任务,文档就被添加进去了
如图
在这里插入图片描述

1.4 自动补全查询

// 自动补全查询
GET /test/_search
{
  "suggest": {
    "title_suggest": {	//设置这个自动查询操作的名称
      "text": "java", // 关键字
      "completion": {
        "field": "suggestion", // 补全查询的字段名
        "skip_duplicates": true, // 跳过重复的
        "size": 10 // 获取前10条结果
      }
    }
  }
}

示例1.
在这里插入图片描述
示例2.
在这里插入图片描述

2. 代码流程

2.1 核心业务代码

AssociateController

@RestController
@RequestMapping(value = "/api/v1/associate")
public class AssociateController {

    @Autowired
    private AssociateService associateService;

    /***
     * 单词自动补全
     */
    @PostMapping(value = "/search")
    public ResponseResult search(@RequestBody UserSearchDto dto) throws IOException {
        return associateService.search(dto);
    }
}

核心search方法

	@Autowired
    private RestHighLevelClient restHighLevelClient;
	/***
     * 单词自动补全
     * @param dto
     * @return
     */
    @Override
    public ResponseResult search(UserSearchDto dto) throws IOException {
        //1)新建一个SearchRequest
        SearchRequest request = new SearchRequest("app_info_article");

        //2)创建一个单词自动补全配置 Suggest,给它取个别名
        request.source().suggest(
                new SuggestBuilder()
                .addSuggestion(
                                //给它取个别名
                        "article_suggest",
                        SuggestBuilders
                                //指定查询的字段
                                .completionSuggestion("suggestion")
                                //去重
                                .skipDuplicates(true)
                                //搜索的前缀
                                .prefix(dto.getSearchWords())
                                .size(10)
                        )
        );

        //4)执行搜索
        SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);

        //5)解析结果集
        CompletionSuggestion suggests = response.getSuggest().getSuggestion("article_suggest");

        //List
        List<Map<String,String>> options = new ArrayList<Map<String,String>>();
        for (CompletionSuggestion.Entry.Option option : suggests.getOptions()) {
            Map<String,String> dataMap = new HashMap<String,String>();
            dataMap.put("associateWords",option.getText().toString());
            options.add(dataMap);

        }
        return ResponseResult.okResult(options);
    }

结果集解析
在这里插入图片描述
在这里插入图片描述

2.2 测试

请求url:http://127.0.0.1:8801/app/search/api/v1/associate/search/
其中/app/search为nginx和gateway处理过

  • 测试1
    在这里插入图片描述

  • 测试2
    在这里插入图片描述
    ps:联想词中的蓝色高亮是前端处理的。

  • 测试3
    在这里插入图片描述

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

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

相关文章

体检系统商业源码,C/S架构的医院体检系统源码,大型健康体检中心管理系统源码

体检系统商业源码&#xff0c;C/S架构的医院体检系统源码&#xff0c;大型健康体检中心管理系统源码 体检信息管理系统软件是对医院体检中心进行系统化和规范化的管理。系统从检前&#xff0c;检中&#xff0c;检后整个业务流程提供标准化以及精细化的解决方案。实现体检业务市…

上位机图像处理和嵌入式模块部署(f103 mcu的最小软件系统)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 我们都知道mcu电路有最小系统。一个最小硬件系统里面包含了mcu、晶振、复位、输入和输出。其实不光硬件如此&#xff0c;软件也有一个最小系统。而…

9.任务调度

一、开启任务调度器 1.函数 vTaskStartScheduler() 函数 vTaskStartScheduler()用于启动任务调度器&#xff0c;任务调度器启动后&#xff0c;FreeRTOS 便会开始 进行任务调度&#xff0c;除非调用函数 xTaskEndScheduler()停止任务调度器&#xff0c;否则不会再返回。函数 vTa…

【对角线遍历】python

没啥思路 class Solution:def findDiagonalOrder(self, mat: List[List[int]]) -> List[int]:mlen(mat)nlen(mat[0])ret[]if len(mat)0:return retcount0#mn-1是对角线总数while count<mn-1:#x和y的和刚好是count数#偶数为右上走if count%20:xcount if(count<m)else (…

Django 里html模板

Django 提供两种方式让程序员自定义html模板。 第一种方法 在项目文件夹里的urls.py进行添加 修改代码如下 from django.contrib import admin from django.urls import path from app01 import views # 得添加这行urlpatterns [path(xxx/, views.home), # 添加这行path(…

有一个3x4的矩阵,要求用函数编写程序求出其中值最大的那个元素,以及其所在的行号和列号

常量和变量可以用作函数实参&#xff0c;同样数组元素也可以作函数实参&#xff0c;其用法与变量相同。数组名也可以作实参和形参&#xff0c;传递的是数组的起始地址。 用数组元素作函数实参&#xff1a; 由于实参可以是表达式&#xff0c;而数组元素可以是表达式的组…

如何在Windows 10上对硬盘进行碎片整理?这里提供步骤

随着时间的推移&#xff0c;由于文件系统中的碎片&#xff0c;硬盘驱动器可能会开始以较低的效率运行。为了加快驱动器的速度&#xff0c;你可以使用内置工具在Windows 10中对其进行碎片整理和优化。方法如下。 什么是碎片整理 随着时间的推移&#xff0c;组成文件的数据块&a…

电机控制系列模块解析(22)—— 零矢量刹车

一、零矢量刹车 基本概念 逆变器通常采用三相桥式结构&#xff0c;包含六个功率开关元件&#xff08;如IGBT或MOSFET&#xff09;&#xff0c;分为上桥臂和下桥臂。每个桥臂由两个反并联的开关元件组成&#xff0c;上桥臂和下桥臂对应于电机三相绕组的正负端。正常工作时&…

原哥花了1个多月的时间终于开发了一款基于android studio的原生商城app

大概讲一下这个app实现的功能和前后端技术架构。 功能简介 广告展示商品展示跳转淘宝联盟优惠卷购买发布朋友圈宝妈知识资讯商品搜索朋友圈展示/点赞/评论登陆注册版本升级我的个人资料商品和资讯收藏我的朋友圈意见反馈 安卓端技术选型 Arouter组件化daggerrxjavaretrofit…

技术面试,项目实战,求职利器

之前找工作一直想找一个能真正系统性学开发的地方&#xff0c;之前毕业找工作的时候无意间碰到下面这个网站&#xff0c;感觉还挺不错的&#xff0c;用上面的技术实战内容应对技术面试&#xff0c;也算是求职利器了。有需要的可以自取&#xff1a; https://how2j.cn?p156336 实…

基于springboot+vue的智慧外贸平台

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

基础技术-ELF系列(1)-ELF文件基础

成就更好的自己 本篇是基础技术系列中ELF相关技术的首篇文章。 尽管网上有许多关于ELF相关内容的文章&#xff0c;但总体而言&#xff0c;要么是一些非常基础且重复性强的内容&#xff0c;要么直接深入探讨相对高深的主题&#xff0c;缺乏系统化分析和解释。 接下来&#xf…

Redis - 缓存场景

学习资料 学习的黑马程序员哔站项目黑马点评&#xff0c;用作记录和探究原理。 Redis缓存 缓存 &#xff1a;就是数据交换的缓冲区&#xff0c;是存储数据的临时地方&#xff0c;读写性能较高 缓存常见的场景: 数据库查询加速&#xff1a;通过将频繁查询的数据缓存起来&…

论文阅读--ActionCLIP

原来的动作识别问题在于标注太难太贵&#xff0c;将动作表示为短语的latent space太大 本文的贡献&#xff1a;&#xff08;1&#xff09;将CLIP的image encoder换成video encoder&#xff0c;方法与CLIP4Clip几乎一样 &#xff08;2&#xff09;CLIP的ground truth来自于文本…

使用pyqt绘制一个爱心!

使用pyqt绘制一个爱心&#xff01; 介绍效果代码 介绍 使用pyqt绘制一个爱心&#xff01; 效果 代码 import sys from PyQt5.QtWidgets import QApplication, QMainWindow, QWidget from PyQt5.QtGui import QPainter, QPen, QBrush, QColor from PyQt5.QtCore import Qt, Q…

【气象常用】间断时间序列图

效果图&#xff1a; 主要步骤&#xff1a; 1. 数据准备&#xff1a;随机数组 2. 图像绘制&#xff1a;绘制间断的时间序列 详细代码&#xff1a;着急的直接拖到最后有完整代码 步骤一&#xff1a;导入库包及图片存储路径并设置中文字体为宋体&#xff0c;西文为新罗马&…

没有telnet情况下判断主机端口是否开放的方法

没有telnet情况下判断主机端口是否开放的方法 方式一 ssh -v 101.132.64.231 -p 80显示结果 如果有显示 debug1: Connection established. 就说明端口是开放的 端口未开放的情况是显示 方式二 echo >/dev/tcp/101.132.64.231/3306效果如下 如果没有任何输出&#xff0c;…

Redis开发实战

单机部署安装 服务端下载&#xff0c;安装&#xff0c;启动去官网下载最新的版本&#xff1a;http://redis.io/download &#xff0c;这里用的是3.0.2解压后&#xff0c;进入解压好的文件夹redis的安装非常简单&#xff0c;因为已经有现成的Makefile文件&#xff0c;所以直接先…

Photoshop插件(UXP)编写过程中,如何更新sp-checkbox的选中状态

✨问题说明 sp-checkbox是uxpSpectrum UXP Widgets下的一个小组件&#xff0c;内置样式大概是这样&#xff1a; 那么&#xff0c;如果用js动态的改变选中的状态&#xff0c;应该如何做呢&#xff1f; 如果直接是html来写&#xff1a; <sp-checkbox checked>Checked<…

freemarker ftl模板 格式、列表、图片

文章目录 前言一、freemarker实现内容替换二、ftl 模板1.word另存ftl2.编辑ftl文件2.1 了解一下常用的标记及其说明2.2 list处理2.3 红线2.4 图片 总结 前言 固定内容word生成&#xff1a;freemarker ftl模板 动态表格生成&#xff1a;https://blog.csdn.net/mr_wanter/articl…