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);

知识库搜索案例

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

相关推荐
星星电灯猴4 小时前
Thor 抓包工具详解 iOS 抓包方法、HTTPS 抓包难点与常见网络调试工具对比
后端
姓王者4 小时前
可能解决Tauri多窗口应用阻塞问题
后端
RoyLin4 小时前
TypeScript设计模式:抽象工厂模式
前端·后端·typescript
没逻辑5 小时前
Post-Quantum HTTPS:未来的安全通信架构
后端·安全
云中雾丽5 小时前
Redis 使用记录
后端
似水流年流不尽思念5 小时前
MongoDB 有哪些索引?适用场景?
后端·mongodb
MacroZheng5 小时前
横空出世!MyBatis-Plus 同款 ES ORM 框架,用起来够优雅!
java·后端·elasticsearch
武子康5 小时前
大数据-100 Spark DStream 转换操作全面总结:map、reduceByKey 到 transform 的实战案例
大数据·后端·spark
bobz9655 小时前
网段分配
后端
bobz9655 小时前
VRRP 负载均衡:一个网段多个网关(网关数量和交换机数目一致:一般是两个)
后端