meilisearch全文检索elasticsearch的平替,应用于中小型项目足矣

摘要:本文主要介绍meilisearch的安装和简单使用。安装采用docker部署,案例与SpringBoot3结合,并展示了我们平常业务中会遇到的场景的使用案例。

meilisearchelasticsearch对比

1. 定位与核心目标

特性 MeiliSearch Elasticsearch
定位 轻量级、易用的全文搜索引擎,强调简单性和快速启动。 企业级搜索与分析引擎,支持复杂搜索、实时分析和大数据处理。
目标用户 开发者、初创团队、小型项目或内部工具。 中大型企业、需要复杂搜索和数据分析的场景(如日志、监控、电商)。

2. 架构与部署

特性 MeiliSearch Elasticsearch
架构 单节点为主,集群支持较简单。 分布式架构,天生支持分片(Shard)和副本(Replica),适合横向扩展。
部署复杂度 一键安装,依赖少,资源占用低。 需要 Java 环境,配置复杂,运维成本高。
扩展性 扩展性有限,适合中小规模数据。 支持动态扩缩容,适合海量数据和高并发场景。

3. 查询与功能

特性 MeiliSearch Elasticsearch
查询语法 简单 API,类似 RESTful,学习成本低。 复杂的 DSL(Domain-Specific Language),功能强大但学习曲线陡峭。
搜索模式 实时索引,支持模糊搜索、拼写纠正、同义词等基础功能。 支持聚合分析、地理搜索、模糊匹配、高亮等高级功能。
自定义功能 插件生态较少,扩展性有限。 丰富的插件生态(如中文分词器 IK、拼音分词),支持脚本和自定义分析器。

4. 性能与资源消耗

特性 MeiliSearch Elasticsearch
资源消耗 内存和 CPU 占用低,适合轻量级应用。 资源消耗较高,尤其是大数据量时需优化 JVM 和分片策略。
实时性 近实时(默认 1 秒刷新),适合一般场景。 近实时(默认 1 秒),支持更细粒度的刷新控制。
写入速度 较快,适合中小规模数据写入。 高吞吐写入,支持批量操作和复杂流水线处理。

安装

meilisearch安装

docker-compose.yml,挂载了数据出来

yaml 复制代码
services:
  meilisearch:
    image: getmeili/meilisearch:v1.14
    container_name: meilisearch
    environment:
      MEILI_MASTER_KEY: meilisearch_master
    ports:
      - 7700:7700
    volumes:
      - ./meili_data:/meili_data

环境变量说明

  • MEILI_MASTER_KEY: 相当于token,调用的秘钥,设计比较简单。

meilisearch-ui安装

该可视化工具只能在内网中使用,不能暴露到公网。

yaml 复制代码
services:
  meilisearch-ui:
    image: riccoxie/meilisearch-ui:lite
    container_name: meilisearch-ui
    ports:
      - 24900:24900

springboot集成使用

pom.xml

xml 复制代码
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>com.meilisearch.sdk</groupId>
        <artifactId>meilisearch-java</artifactId>
        <version>0.14.4</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba.fastjson2</groupId>
        <artifactId>fastjson2</artifactId>
        <version>2.0.31</version>
    </dependency>
</dependencies>

application.yml

yaml 复制代码
meilisearch:
  host-url: http://192.168.137.131:7700
  master-key: meilisearch_master

MeilisearchProperties

typescript 复制代码
@Data
public class MeilisearchProperties {

    private String hostUrl;

    private String masterKey;

}

MeilisearchConfig

typescript 复制代码
@Configuration
public class MeilisearchConfig {

    @Bean
    @ConfigurationProperties(prefix = "meilisearch")
    public MeilisearchProperties meilisearchProperties(){
        return new MeilisearchProperties();
    }

    @Bean
    public Client meilisearchClient(MeilisearchProperties meilisearchProperties){
        return new Client(new Config(meilisearchProperties.getHostUrl(), meilisearchProperties.getMasterKey()));
    }

}

Goods

定义的存储实体

vbnet 复制代码
@Data
public class Goods {

    private Long id;

    private String name;

    private String desc;

    private String score;

    private Long paid;

}

GoodsService

typescript 复制代码
@Service
public class GoodsService {

    @Autowired
    private Client client;

    /**
     * 新增或编辑内容
     * @param goodsList
     */
    public void add(List<Goods> goodsList){
        Index index = client.index("goods");
        index.addDocuments(JSON.toJSONString(goodsList));
    }

    /**
     * 简单搜索
     * @param search
     * @return
     */
    public SearchResult search(String search){
        Index index = client.index("goods");
        return index.search(search);
    }

    /**
     * 高亮搜索
     * @param search
     * @return
     */
    public Searchable search2(String search){
        SearchRequest searchRequest = new SearchRequest(search);
        searchRequest.setShowMatchesPosition(true);
        searchRequest.setAttributesToHighlight(new String[] {"name"});
        Index index = client.index("goods");
        return index.search(searchRequest);
    }

    /**
     * 带过滤条件的搜索
     * @param search
     * @return
     */
    public Searchable search3(String search){
        SearchRequest searchRequest = new SearchRequest(search);
        searchRequest.setShowMatchesPosition(true);
        searchRequest.setAttributesToHighlight(new String[] {"name"});
        searchRequest.setFilter(new String[] {"id > 1 AND name = Action"});
        Index index = client.index("goods");
        index.updateFilterableAttributesSettings(new String[]
                {
                        "id",
                        "name"
                });
        return index.search(searchRequest);
    }

    /**
     * 自定义评分搜索
     * @param search
     * @return
     */
    public Searchable search4(String search){
        Index index = client.index("goods");
        index.updateSortableAttributesSettings(new String[] {"score"});
        index.updateRankingRulesSettings(new String[]{
                "paid:desc",
                "words",
                "typo",
                "proximity",
                "attribute",
                "sort",
                "exactness"}
        );
        SearchRequest searchRequest = new SearchRequest(search);
        searchRequest.setShowRankingScore(true);
        //searchRequest.setSort(new String[]{"score:desc"});
        return index.search(searchRequest);
    }
}

GoodsController

入口控制器

typescript 复制代码
@RestController
@RequestMapping(value = "goods")
public class GoodsController {

    @Autowired
    public GoodsService goodsService;
    @Autowired
    private Client client;

    @GetMapping("createIndex")
    public Object createIndex(String index){
        client.createIndex(index, "id");
        client.getIndex(index).updateFilterableAttributesSettings(new String[]{ "id", "name", "age"});
        client.getIndex(index).updateRankingRulesSettings(new String[] {});
        return "success";
    }

    @PostMapping(value = "add")
    public Object add(@RequestBody List<Goods> goodsList){
        goodsService.add(goodsList);
        return "success";
    }

    @GetMapping(value = "search")
    public Object search(String search) {
        return goodsService.search(search);
    }

    @GetMapping(value = "search2")
    public Object search2(String search) {
        return goodsService.search2(search);
    }

    @GetMapping(value = "search3")
    public Object search3(String search) {
        return goodsService.search3(search);
    }

    @GetMapping(value = "search4")
    public Object search4(String search) {
        return goodsService.search4(search);
    }

}

实际场景案例

电商搜索案例

电商搜索一般都有价格区间查询,价格排序,销量排序,所以我们就要把评分标准的判断sort提前,一般提到第一个。

  • 配置
arduino 复制代码
Index index = client.index("goods");
        index.updateSortableAttributesSettings(new String[] {"score"});
        index.updateRankingRulesSettings(new String[]{
                "sort",
                "words",
                "typo",
                "proximity",
                "attribute",
                "exactness"}
        );
  • 查询
ini 复制代码
SearchRequest searchRequest = new SearchRequest(search);
        searchRequest.setShowRankingScore(true);
        searchRequest.setSort(new String[]{"score:desc"});
        return index.search(searchRequest);

知识库搜索案例

就默认的排序即可,完全按照权重数据来排序

相关推荐
心在飞扬20 分钟前
RAG 进阶检索学习笔记
后端
Moment21 分钟前
想要长期陪伴你的助理?先从部署一个 OpenClaw 开始 😍😍😍
前端·后端·github
Das1_22 分钟前
【Golang 数据结构】Slice 底层机制
后端·go
得物技术22 分钟前
深入剖析Spark UI界面:参数与界面详解|得物技术
大数据·后端·spark
古时的风筝24 分钟前
花10 分钟时间,把终端改造成“生产力武器”:Ghostty + Yazi + Lazygit 配置全流程
前端·后端·程序员
Cache技术分享25 分钟前
340. Java Stream API - 理解并行流的额外开销
前端·后端
初次攀爬者27 分钟前
RocketMQ 消息可靠性保障与堆积处理
后端·消息队列·rocketmq
ygxb34 分钟前
如何去创建一个规范化的Agent SKIll?
后端·ai编程·claude
JxWang051 小时前
Task01:环境搭建,初识数据库
后端
周杰伦jc1 小时前
RocketMQ 完全指南:从入门到原理到生产实战、八股面试
后端