springboot整合MeiliSearch轻量级搜索引擎

news2024/10/5 12:23:25

一、Meilisearch与Easy Search点击进入官网了解,本文主要从小微型公司业务出发,选择meilisearch来作为项目的全文搜索引擎,还可以当成来mongodb来使用。

二、starter封装

1、项目结构展示

2、引入依赖包

    <dependencies>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-common</artifactId>
        </dependency>
        <!-- meilisearch 轻量级搜索       -->
        <!-- https://mvnrepository.com/artifact/com.meilisearch.sdk/meilisearch-java -->
        <dependency>
            <groupId>com.meilisearch.sdk</groupId>
            <artifactId>meilisearch-java</artifactId>
            <version>0.11.2</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba.fastjson2/fastjson2 -->
        <dependency>
            <groupId>com.alibaba.fastjson2</groupId>
            <artifactId>fastjson2</artifactId>
            <version>2.0.40</version>
            <scope>provided</scope>
        </dependency>

        <!-- Web 相关 -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-web</artifactId>
            <scope>provided</scope> <!-- 设置为 provided,只有 OncePerRequestFilter 使用到 -->
        </dependency>
    </dependencies>

3、yml参数读取代码参考

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

/**
 * MeiliSearch 自动装配参数类
 * 2023年9月21日
 */
@ConfigurationProperties("yudao.meilisearch")
@Data
@Validated
public class MeiliSearchProperties {

    /**
     * 主机地址
     */
    private String hostUrl = "";
    /**
     * 接口访问标识
     */
    private String apiKey = "123456";

}

4、自动配置类代码参考

import com.meilisearch.sdk.Client;
import com.meilisearch.sdk.Config;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;

import javax.annotation.Resource;


/**
 * MeiliSearch 自动装配类
 * 2023年9月21日
 */
@AutoConfiguration
@EnableConfigurationProperties({MeiliSearchProperties.class})
@EnableCaching
public class MeiliSearchAutoConfiguration {
    @Resource
    MeiliSearchProperties properties;

    @Bean
    @ConditionalOnMissingBean(Client.class)
    Client client() {
        return new Client(config());
    }

    @Bean
    @ConditionalOnMissingBean(Config.class)
    Config config() {
        return new Config(properties.getHostUrl(), properties.getApiKey());
    }

}

5、数据处理类参考

import com.meilisearch.sdk.json.GsonJsonHandler;

import java.util.List;

/**
 * MeiliSearch json解析类
 * 2023年9月21日
 */
public class JsonHandler {

    private com.meilisearch.sdk.json.JsonHandler jsonHandler = new GsonJsonHandler();

    public <T> SearchResult<T> resultDecode(String o, Class<T> clazz) {
        Object result = null;
        try {
            result = jsonHandler.decode(o, SearchResult.class, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result == null ? null : (SearchResult<T>) result;
    }

    public <T> List<T> listDecode(Object o, Class<T> clazz) {
        Object list = null;
        try {
            list = jsonHandler.decode(o, List.class, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return list == null ? null : (List<T>) list;
    }

    public String encode(Object o) {
        try {
            return jsonHandler.encode(o);
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public <T> T decode(Object o, Class<T> clazz) {
        T t = null;
        try {
            t = jsonHandler.decode(o, clazz);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return t;
    }
}






import java.util.List;
import java.util.Map;

/**
 * MeiliSearch
 * 2023年9月21日
 */
public class MatchedBean<T> {

    private T _formatted;
    private Map<String, List<Matching>> _matchesInfo;

    public T get_formatted() {
        return _formatted;
    }

    public void set_formatted(T _formatted) {
        this._formatted = _formatted;
    }

    public Map<String, List<Matching>> get_matchesInfo() {
        return _matchesInfo;
    }

    public void set_matchesInfo(Map<String, List<Matching>> _matchesInfo) {
        this._matchesInfo = _matchesInfo;
    }

    private class Matching {
        long start;
        long length;

        public long getStart() {
            return start;
        }

        public void setStart(long start) {
            this.start = start;
        }

        public long getLength() {
            return length;
        }

        public void setLength(long length) {
            this.length = length;
        }
    }
}




import java.util.List;

/**
 * MeiliSearch
 * 2023年9月21日
 */
public class SearchResult<T> {

    private String query;
    private long offset;
    private long limit;
    private long processingTimeMs;
    private long nbHits;

    private boolean exhaustiveNbHits;

    private List<T> hits;

    public String getQuery() {
        return query;
    }

    public void setQuery(String query) {
        this.query = query;
    }

    public long getOffset() {
        return offset;
    }

    public void setOffset(long offset) {
        this.offset = offset;
    }

    public long getLimit() {
        return limit;
    }

    public void setLimit(long limit) {
        this.limit = limit;
    }

    public long getProcessingTimeMs() {
        return processingTimeMs;
    }

    public void setProcessingTimeMs(long processingTimeMs) {
        this.processingTimeMs = processingTimeMs;
    }

    public long getNbHits() {
        return nbHits;
    }

    public void setNbHits(long nbHits) {
        this.nbHits = nbHits;
    }

    public boolean isExhaustiveNbHits() {
        return exhaustiveNbHits;
    }

    public void setExhaustiveNbHits(boolean exhaustiveNbHits) {
        this.exhaustiveNbHits = exhaustiveNbHits;
    }

    public List<T> getHits() {
        return hits;
    }

    public void setHits(List<T> hits) {
        this.hits = hits;
    }

    @Override
    public String toString() {
        return "SearchResult{" +
                "query='" + query + '\'' +
                ", offset=" + offset +
                ", limit=" + limit +
                ", processingTimeMs=" + processingTimeMs +
                ", nbHits=" + nbHits +
                ", exhaustiveNbHits=" + exhaustiveNbHits +
                ", hits=" + hits +
                '}';
    }
}

6、自定义注解代码参考

import java.lang.annotation.*;

/**
 * MeiliSearch
 * 2023年9月21日
 */
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MSFiled {

    /**
     * 是否开启过滤
     */
    boolean openFilter() default false;

    /**
     * 是否不展示
     */
    boolean noDisplayed() default false;

    /**
     * 是否开启排序
     */
    boolean openSort() default false;


    /**
     *  处理的字段名
     */
    String key() ;
}





import java.lang.annotation.*;

/**
 * MeiliSearch
 * 2023年9月21日
 */
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MSIndex {

    /**
     * 索引
     */
    String uid() default "";

    /**
     * 主键
     */
    String primaryKey() default "";

    /**
     * 分类最大数量
     */
    int maxValuesPerFacet() default 100;

    /**
     *  单次查询最大数量
     */
    int maxTotalHits() default 1000;
}

7、基础操作接口封装

import cn.iocoder.yudao.framework.meilisearch.json.SearchResult;
import com.meilisearch.sdk.SearchRequest;
import com.meilisearch.sdk.model.Settings;
import com.meilisearch.sdk.model.Task;
import com.meilisearch.sdk.model.TaskInfo;

import java.util.List;

/**
 * MeiliSearch 基础接口
 * 2023年9月21日
 */
interface DocumentOperations<T> {

    T get(String identifier);

    List<T> list();

    List<T> list(int limit);

    List<T> list(int offset, int limit);

    long add(T document);

    long update(T document);

    long add(List<T> documents);

    long update(List<T> documents);

    long delete(String identifier);

    long deleteBatch(String... documentsIdentifiers);

    long deleteAll();

    SearchResult<T> search(String q);

    SearchResult<T> search(String q, int offset, int limit);

    SearchResult<T> search(SearchRequest sr);

    Settings getSettings();

    TaskInfo updateSettings(Settings settings);

    TaskInfo resetSettings();

    Task getUpdate(int updateId);
}

8、基本操作实现

import cn.iocoder.yudao.framework.meilisearch.json.JsonHandler;
import cn.iocoder.yudao.framework.meilisearch.json.MSFiled;
import cn.iocoder.yudao.framework.meilisearch.json.MSIndex;
import cn.iocoder.yudao.framework.meilisearch.json.SearchResult;
import com.alibaba.fastjson2.JSON;
import com.meilisearch.sdk.*;
import com.meilisearch.sdk.model.*;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;

/**
 * MeiliSearch 基本操作实现
 * 2023年9月21日
 */
public class MeilisearchRepository<T> implements InitializingBean, DocumentOperations<T> {

    private Index index;
    private Class<T> tClass;
    private JsonHandler jsonHandler = new JsonHandler();

    @Resource
    private Client client;


    @Override
    public T get(String identifier) {
        T document;
        try {
            document = index.getDocument(identifier, tClass);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return document;
    }

    @Override
    public List<T> list() {
        List<T> documents;
        try {
            documents = Optional.ofNullable(index.getDocuments(tClass))
                    .map(indexDocument -> indexDocument.getResults())
                    .map(result -> Arrays.asList(result))
                    .orElse(new ArrayList<>());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return documents;
    }

    @Override
    public List<T> list(int limit) {
        List<T> documents;
        try {
            DocumentsQuery query = new DocumentsQuery();
            query.setLimit(limit);
            documents = Optional.ofNullable(index.getDocuments(query, tClass))
                    .map(indexDocument -> indexDocument.getResults())
                    .map(result -> Arrays.asList(result))
                    .orElse(new ArrayList<>());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return documents;
    }

    @Override
    public List<T> list(int offset, int limit) {
        List<T> documents;
        try {
            DocumentsQuery query = new DocumentsQuery();
            query.setLimit(limit);
            query.setOffset(offset);
            documents = Optional.ofNullable(index.getDocuments(query, tClass))
                    .map(indexDocument -> indexDocument.getResults())
                    .map(result -> Arrays.asList(result))
                    .orElse(new ArrayList<>());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return documents;
    }

    @Override
    public long add(T document) {
        List<T> list = Collections.singletonList(document);
        return add(list);
    }

    @Override
    public long update(T document) {
        List<T> list = Collections.singletonList(document);
        return update(list);
    }

    @Override
    public long add(List documents) {
        int taskId;
        try {
            taskId = index.addDocuments(JSON.toJSONString(documents)).getTaskUid();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return taskId;
    }

    @Override
    public long update(List documents) {
        int updates;
        try {
            updates = index.updateDocuments(JSON.toJSONString(documents)).getTaskUid();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return updates;
    }


    @Override
    public long delete(String identifier) {
        int taskId;
        try {
            taskId = index.deleteDocument(identifier).getTaskUid();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return taskId;
    }

    @Override
    public long deleteBatch(String... documentsIdentifiers) {
        int taskId;
        try {
            taskId = index.deleteDocuments(Arrays.asList(documentsIdentifiers)).getTaskUid();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return taskId;
    }

    @Override
    public long deleteAll() {
        int taskId;
        try {
            taskId = index.deleteAllDocuments().getTaskUid();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return taskId;
    }


    @Override
    public cn.iocoder.yudao.framework.meilisearch.json.SearchResult<T> search(String q) {
        String result;
        try {
            result = JSON.toJSONString(index.search(q));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return jsonHandler.resultDecode(result, tClass);
    }

    @Override
    public cn.iocoder.yudao.framework.meilisearch.json.SearchResult<T> search(String q, int offset, int limit) {
        SearchRequest searchRequest = SearchRequest.builder()
                .q(q)
                .offset(offset)
                .limit(limit)
                .build();
        return search(searchRequest);
    }

    @Override
    public SearchResult<T> search(SearchRequest sr) {
        String result;
        try {
            result = JSON.toJSONString(index.search(sr));
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return jsonHandler.resultDecode(result, tClass);
    }

    @Override
    public Settings getSettings() {
        try {
            return index.getSettings();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public TaskInfo updateSettings(Settings settings) {
        try {
            return index.updateSettings(settings);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public TaskInfo resetSettings() {
        try {
            return index.resetSettings();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Task getUpdate(int updateId) {
        try {
            return index.getTask(updateId);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        initIndex();
    }

    public Index getIndex() {
        return index;
    }

    /**
     * 初始化索引信息
     *
     * @throws Exception
     */
    private void initIndex() throws Exception {
        Class<? extends MeilisearchRepository> clazz = getClass();
        tClass = (Class<T>) ((ParameterizedType) clazz.getGenericSuperclass()).getActualTypeArguments()[0];
        MSIndex annoIndex = tClass.getAnnotation(MSIndex.class);
        String uid = annoIndex.uid();
        String primaryKey = annoIndex.primaryKey();
        if (StringUtils.isEmpty(uid)) {
            uid = tClass.getSimpleName().toLowerCase();
        }
        if (StringUtils.isEmpty(primaryKey)) {
            primaryKey = "id";
        }
        int maxTotalHit=1000;
        int maxValuesPerFacet=100;
        if (Objects.nonNull(annoIndex.maxTotalHits())){
            maxTotalHit=annoIndex.maxTotalHits();
        }
        if (Objects.nonNull(annoIndex.maxValuesPerFacet())){
            maxValuesPerFacet=100;
        }

        List<String> filterKey = new ArrayList<>();
        List<String> sortKey = new ArrayList<>();
        List<String> noDisPlay = new ArrayList<>();
        //获取类所有属性
        for (Field field : tClass.getDeclaredFields()) {
            //判断是否存在这个注解
            if (field.isAnnotationPresent(MSFiled.class)) {
                MSFiled annotation = field.getAnnotation(MSFiled.class);
                if (annotation.openFilter()) {
                    filterKey.add(annotation.key());
                }

                if (annotation.openSort()) {
                    sortKey.add(annotation.key());
                }
                if (annotation.noDisplayed()) {
                    noDisPlay.add(annotation.key());
                }
            }
        }
        Results<Index> indexes = client.getIndexes();
        Index[] results = indexes.getResults();
        Boolean isHaveIndex=false;
        for (Index result : results) {
            if (uid.equals(result.getUid())){
                isHaveIndex=true;
                break;
            }
        }

        if (isHaveIndex){
            client.updateIndex(uid,primaryKey);

            this.index = client.getIndex(uid);
            Settings settings = new Settings();
            settings.setDisplayedAttributes(noDisPlay.size()>0?noDisPlay.toArray(new String[noDisPlay.size()]):new String[]{"*"});
            settings.setFilterableAttributes(filterKey.toArray(new String[filterKey.size()]));
            settings.setSortableAttributes(sortKey.toArray(new String[sortKey.size()]));
            index.updateSettings(settings);
        }else {
            client.createIndex(uid, primaryKey);
        }
    }
}

9、指定自动配置类所在

10、项目有统一版本管理的设置下版本管理

二、项目引用

1、引入starter依赖(没有版本统一管理的要把version加上)

2、基本使用

2.1、建立索引(宽表)

import cn.iocoder.yudao.framework.meilisearch.json.MSFiled;
import cn.iocoder.yudao.framework.meilisearch.json.MSIndex;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
@MSIndex(uid = "com_baidu_main", primaryKey = "id")
public class MainDO {
    private Long id;
    private String seedsName;
    @MSFiled(openFilter = true, key = "isDelete")
    private Integer isDelete;
    @MSFiled(openFilter = true, key = "status")
    private Integer status;

    @MSFiled(openFilter = true, key = "classFiledId")
    private Integer classFiledId;
    private String classFiledName;
    @MSFiled(openFilter = true, key = "tags")
    private List<TageInfo> tags;
}

2.2、集成starter里边的mapper对milisearch进行基本操作

import cn.iocoder.yudao.framework.meilisearch.core.MeilisearchRepository;
import org.springframework.stereotype.Repository;

@Repository
public class MeiliSearchMapper extends MeilisearchRepository<MainDO> {
}

2.3、基本使用

@Resource
private MeiliSearchMapper meiliSearchMapper;

//根据标签分页查询
SearchRequest searchRequest4 = SearchRequest.builder()
                .limit(pageParam.getPageSize().intValue())
                .sort(new String[]{"createTime:desc"})
                .offset(pageParam.getPageNo().intValue() == 0 ? pageParam.getPageNo().intValue() : (pageParam.getPageNo().intValue() - 1) * pageParam.getPageSize().intValue())
                .filter(new String[]{"tags.id=" + "10010" + " AND status=1 AND isDelete=0"}).build();
SearchResult<MainDO> search4 = meiliSearchMapper.search(searchRequest4);

//保存Or编辑
List<SeedsDO> articleCardDTOS = new ArrayList<>();
Boolean aBoolean = meiliSearchMapper.add(articleCardDTOS) > 0 ? Boolean.TRUE : Boolean.FALSE;
//按id删除
meiliSearchMapper.delete(String.valueOf(10085));

//根据类目分页查询
SearchRequest searchRequest3 = SearchRequest.builder()
                .limit(pageParam.getPageSize().intValue())
                .offset(pageParam.getPageNo().intValue() == 0 ? pageParam.getPageNo().intValue() : (pageParam.getPageNo().intValue() - 1) * pageParam.getPageSize().intValue())
                .build();
StringBuffer sb1 = new StringBuffer();
sb.append("status =1 AND isDelete=0").append(" AND ").append("categoryId =").append(10086L);
searchRequest.setFilter(new String[]{sb.toString()});
searchRequest.setSort(new String[]{"createTime:desc"});
SearchResult<SeedsDO> search3 = meiliSearchMapper.search(searchRequest3);

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

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

相关文章

【操作系统笔记十四】科普:POSIX 是什么

注&#xff1a;本文转载自该文章posix是什么都不知道&#xff0c;还好意思说你懂Linux&#xff1f; Linux开发者越来越多&#xff0c;但是仍然有很多人整不明白POSIX是什么。本文就带着大家来了解一下到底什么是POSIX&#xff0c;了解他的历史和重要性。 一、什么是 POSIX&…

windows使用小技巧之windows照片查看器无法显示此图片

碰到过好几次了&#xff0c;以前没有理会&#xff0c;今天特意去查了一下解决方法&#xff0c;不然确实不太方便。 1、打开“颜色管理”-“高级”&#xff1a; 2、将“设备配置文件”选择为“Agfa&#xff1a;Swop Standard” 3、关闭&#xff0c;重新打开图片&#xff0c;好…

火花塞工作原理

1.红旗H9轿车2023款发布 2023年元旦过后&#xff0c;红旗汽车在人民大会堂举办了红旗H9的新车发布会&#xff0c;一汽红旗全新的H9豪华轿车终于出炉了全套的配置参数&#xff0c;红旗H9的车身长度达到5137mm&#xff0c;宽度1904mm&#xff0c;轴距3060mm&#xff0c;总高则控…

分享一个基于Python的电子产品销售系统可视化销量统计java版本相同(源码+调试+开题+lw)

&#x1f495;&#x1f495;作者&#xff1a;计算机源码社 &#x1f495;&#x1f495;个人简介&#xff1a;本人七年开发经验&#xff0c;擅长Java、Python、PHP、.NET、微信小程序、爬虫、大数据等&#xff0c;大家有这一块的问题可以一起交流&#xff01; &#x1f495;&…

windows 部署 mindspore GPU 开发环境

基础环境 windows 环境&#xff1a; Windows 11 版本&#xff1a;22H2操作系统版本&#xff1a;22621.2283 wsl2&#xff1a; 1.2.5.0 Docker Desktop&#xff1a; Docker Desktop 4.23.0 CUDA driver for WSL 版本&#xff1a; 535.104.07 宿主机上的 nvidia 环境如下所示&a…

【李沐深度学习笔记】矩阵计算(4)

课程地址和说明 线性代数实现p4 本系列文章是我学习李沐老师深度学习系列课程的学习笔记&#xff0c;可能会对李沐老师上课没讲到的进行补充。 本节是第四篇&#xff0c;由于CSDN限制&#xff0c;只能被迫拆分 矩阵计算 矩阵的导数运算 向量对向量求导的基本运算规则 已知…

如何实现线程池之间的数据透传 ?

如何实现线程池之间的数据透传 &#xff1f; 引言transmittable-thread-local概览capture如何 capture如何保存捕获的数据 save 和 replayrestore 小结 引言 当我们涉及到数据的全链路透传场景时&#xff0c;通常会将数据存储在线程的本地缓存中&#xff0c;如: 用户认证信息透…

灾备系统中的多线程传输功能

多线程传输是指同时使用多个线程进行文件传输&#xff0c;使多个数据包可以同时传输&#xff0c;从而充分利用网络带宽的最大值&#xff0c;提高传输速度。 正常的IE页面文件下载与上传都只有一个线程&#xff0c;有些软件可以实现多线程文件传输&#xff0c;就好像在传输文件…

JDK21你可以不用,新特性还是要了解的

大家好&#xff0c;我是风筝 今年6月份的时候&#xff0c;写过一篇JDK21引入协程&#xff0c;再也不用为并发而头疼了&#xff0c;那时候只是预览版&#xff0c;终于&#xff0c;前两天&#xff08;2023年9月19日&#xff09;发布了 JDK21 正式版。 老早就在 YouTube 上订阅了…

在电脑上怎么分类管理笔记?支持分类整理的电脑云笔记软件

对于大多数上班族而言&#xff0c;在使用电脑办公时&#xff0c;随手记录工作笔记是一个非常常见的场景。无论是会议纪要、工作总结还是项目计划&#xff0c;记录下每一次思考和灵感是提高工作效率的关键。然而&#xff0c;随着时间的推移&#xff0c;电脑上记录的笔记内容逐渐…

OceanMind海睿思入选弯弓研究院《2023中国营销技术生态图谱8.0》

近日&#xff0c;由国内MarTech领域知名机构 弯弓研究院 主办的第五届营销数字化大会暨营销科技MarTech交易展在广州成功召开。 本次大会发布了《2023中国营销技术生态图谱8.0版》 (以下简称“弯弓图谱8.0”)&#xff0c;中新赛克海睿思 凭借成熟的技术实力成功入选弯弓图谱8.0…

K-最近邻算法

一、说明 KNN算法是一个分类算法&#xff0c;基本数学模型是距离模型。K-最近邻是一种超级简单的监督学习算法。它可以应用于分类和回归问题。虽然它是在 1950 年代引入的&#xff0c;但今天仍在使用。然而如何实现&#xff0c;本文将给出具体描述。 来源&#xff1a;维基百科 …

Docker 安装Redis(集群)

3主3从redis集群配置 1、新建6个docker容器 redis 实例 docker run -d --name redis-node-1 --net host --privilegedtrue -v /data/redis/share/redis-node-1:/data redis:6.0.8 --cluster-enabled yes --appendonly yes --port 6381 docker run -d --name redis-node-2 --ne…

Fiddler抓取Https请求配置

官网&#xff1a;https://www.telerik.com/fiddler 配置抓取https包 1.Tools->Options->Https&#xff0c;勾选下面。 2.Actions ->Trust Root Certificate.安装证书到本地 3.在手机端设置代理&#xff1a;本机ip如&#xff1a;192.168.1.168 端口号:8888。 4.手机…

有一个新工具,能让程序员变成高手,优雅撸它!

不知道从什么时候开始&#xff0c;程序员这个职位变得家喻户晓&#xff0c;对程序员的印象也从以前的高深莫测变成如今的加班代名词。对于程序员加班&#xff0c;不懂有话要说。 作为大厂的一枚螺丝钉&#xff0c;接到任务的第一时间需要缕清底层逻辑&#xff0c;并随时关注部门…

【2603. 收集树中金币】

来源&#xff1a;力扣&#xff08;LeetCode&#xff09; 描述&#xff1a; 给你一个 n 个节点的无向无根树&#xff0c;节点编号从 0 到 n - 1 。给你整数 n 和一个长度为 n - 1 的二维整数数组 edges &#xff0c;其中 edges[i] [ai, bi] 表示树中节点 ai 和 bi 之间有一条…

基于SpringBoot的网上超市系统的设计与实现

目录 前言 一、技术栈 二、系统功能介绍 管理员功能实现 用户功能实现 三、核心代码 1、登录模块 2、文件上传模块 3、代码封装 前言 网络技术和计算机技术发展至今&#xff0c;已经拥有了深厚的理论基础&#xff0c;并在现实中进行了充分运用&#xff0c;尤其是基于计…

【Java 基础篇】Java网络编程:下载进度监控实现详解

文件下载是许多应用程序的重要功能&#xff0c;而下载进度监控是提高用户体验的关键。在本文中&#xff0c;我们将详细介绍如何使用Java实现文件下载进度监控&#xff0c;以便用户可以实时了解文件下载的进度。 什么是下载进度监控 下载进度监控是一种用户界面元素或功能&…

真白给,太好考了!大爆冷+扩招!

一、学校及专业介绍 太原科技大学&#xff08;Taiyuan University of Science and Technology)位于山西省太原市&#xff0c;入选了国家中西部高校基础能力建设工程、教育部数据中国“百校工程”&#xff0c;教育部首批新工科研究与实践项目实施高校。 1.1 招生情况 太原科技…

【word格式】mathtype公式插入 | 段落嵌入后格式对齐 | 字体大小调整 |空心字体

1. 公式嵌入 推荐在线latex编辑器&#xff0c;可以截图转 latex 识别率很高 https://www.latexlive.com/home 美中不足&#xff0c;不开会员每天只能用3次识别。 通过公式识别后&#xff0c;输出选择align环境&#xff0c;然后在mathtype中直接粘贴latex就可以转好。 2.公式…