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

752 阅读4分钟

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

meilisearchelasticsearch对比

1. 定位与核心目标

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

2. 架构与部署

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

3. 查询与功能

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

4. 性能与资源消耗

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

安装

meilisearch安装

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

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安装

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

services:
  meilisearch-ui:
    image: riccoxie/meilisearch-ui:lite
    container_name: meilisearch-ui
    ports:
      - 24900:24900

image.png

springboot集成使用

pom.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

meilisearch:
  host-url: http://192.168.137.131:7700
  master-key: meilisearch_master

MeilisearchProperties

@Data
public class MeilisearchProperties {

    private String hostUrl;

    private String masterKey;

}

MeilisearchConfig

@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

定义的存储实体

@Data
public class Goods {

    private Long id;

    private String name;

    private String desc;

    private String score;

    private Long paid;

}

GoodsService

@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

入口控制器

@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提前,一般提到第一个。

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

知识库搜索案例

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