Elasticsearch REST API 超详细指南:从入门概念到 Java 实战,小白也能看懂

作为一名后端开发,我曾被 “海量数据检索” 这个需求难住 —— 普通数据库查 10 万条数据要等好几秒,用户早就没耐心了。后来接触到 Elasticsearch(简称 ES),才发现它能把检索速度提升到毫秒级,而 REST API 就是和 ES 打交道的 “万能钥匙”。

不过刚学的时候,我也踩了不少坑:搞不清 “索引” 和 “文档” 的区别,写个 API 请求总报格式错误,Java 集成时又因为版本不兼容卡了半天。今天就把这些经验整理成一篇指南,从最基础的概念讲起,再到实际操作和 Java 落地,尽量用通俗的话讲明白,帮大家少走弯路~

一、先搞懂 3 个核心概念,不然 API 根本用不明白

很多人刚学 ES 时,一上来就查 API 文档,结果越看越懵 —— 其实是没搞懂 ES 的 “数据逻辑”。咱们可以把它和熟悉的 MySQL 对比,一下子就清楚了:

ES 里的概念对应 MySQL 里的啥简单理解

索引(Index)数据库(Database)存一类数据的 “大容器”,比如 “商品库”“订单库”

映射(Mapping)表结构(Table Schema)规定数据的 “格式”,比如哪个字段是文字、哪个是数字、能不能搜索

文档(Document)行数据(Row)具体的一条数据,用 JSON 格式存,比如 “华为 Mate 60 的商品信息”

举个例子:如果要做商品检索,第一步得建个 “商品索引”(相当于建了个商品数据库),第二步定义 “映射”(规定商品 ID 是数字、商品名是可搜索的文字),第三步才往里面加 “文档”(一条条商品数据)。

搞懂这个逻辑,后面用 API 就不会 “无的放矢” 了~

二、ES REST API 实操:用 curl/Postman 轻松上手

ES 的 REST API 特别好理解,就是 “用 HTTP 方法发请求”—— 比如查数据用 GET,加数据用 POST,改数据用 PUT,删数据用 DELETE。下面用 curl 命令演示常用操作,Postman 操作也一样,复制 URL 和请求体就行。

1. 先搞定 “索引”:建库、查结构、删库

(1)创建索引(顺便定义映射)

需求:建一个 “商品索引”,存商品 ID、名称、价格、创建时间,还要支持中文搜索。

PUT http://localhost:9200/product_index

{

  "mappings": {

    "properties": {

      "product_id": { "type": "integer" },  # 商品ID,数字类型

      "product_name": {

        "type": "text",

        "analyzer": "ik_max_word"  # 中文分词器,要先装IK插件哦

      },

      "price": { "type": "float" },  # 价格,浮点型

      "create_time": {

        "type": "date",

        "format": "yyyy-MM-dd HH:mm:ss"  # 日期格式

      }

    }

  }

}

小贴士:如果用的是 ES 7.x 及以上版本,别在 mappings 里加 “_doc” 这种 “类型名”,不然会报错 —— 新版本已经取消这个概念了。

(2)查索引结构

建完之后,想确认一下格式对不对,用 GET 请求:

# 查单个索引的结构

GET http://localhost:9200/product_index/_mapping

# 查所有索引列表(加?v显示表头,看得更清楚)

GET http://localhost:9200/_cat/indices?v

(3)删除索引(谨慎!删了就找不回来了)

如果建错了要删除,用 DELETE 请求:

DELETE http://localhost:9200/product_index

2. 再操作 “文档”:增删改查数据

(1)新增文档(加数据)

有两种方式:指定 ID,或者让 ES 自动生成 ID。

# 1. 指定ID(适合知道唯一标识的场景,比如用商品ID当文档ID)

PUT http://localhost:9200/product_index/_doc/1

{

  "product_id": 1001,

  "product_name": "华为Mate 60 Pro 5G手机",

  "price": 6999.0,

  "create_time": "2024-01-15 10:30:00"

}

# 2. 不指定ID(ES自动生成随机ID,适合批量加数据)

POST http://localhost:9200/product_index/_doc

{

  "product_id": 1002,

  "product_name": "苹果iPhone 15 Pro",

  "price": 9999.0,

  "create_time": "2024-02-20 14:15:00"

}

(2)查询文档(查数据)

按 ID 查:最快的方式,直接定位到某条数据。

GET http://localhost:9200/product_index/_doc/1

条件查:比如搜 “手机” 关键词,还要按价格降序排,每页看 2 条。

GET http://localhost:9200/product_index/_search

{

  "query": {

    "match": { "product_name": "手机" }  # 分词匹配,会找到所有带“手机”的商品

  },

  "from": 0,  # 从第0条开始(就是第1页)

  "size": 2,  # 每页2条

  "sort": [{ "price": "desc" }]  # 按价格降序

}

(3)更新文档(改数据)

有两种方式:全量更(覆盖整条数据)和局部更(只改某字段)。

# 1. 全量更新(要传所有字段,不然没传的会丢)

PUT http://localhost:9200/product_index/_doc/1

{

  "product_id": 1001,

  "product_name": "华为Mate 60 Pro 5G手机(12GB+512GB)",  # 只改了名称

  "price": 6999.0,  # 没改的字段也要传

  "create_time": "2024-01-15 10:30:00"

}

# 2. 局部更新(只传要改的字段,效率高)

POST http://localhost:9200/product_index/_update/1

{

  "doc": { "price": 6799.0 }  # 只改价格

}

(4)删除文档(删数据)

按 ID 删,很简单:

DELETE http://localhost:9200/product_index/_doc/1

3. 批量操作:用 Bulk API 省时间

如果要加几十上百条数据,一条一条传太慢了,用 Bulk API 一次搞定,效率能提 10 倍以上。

格式有个小要求:每两行一组,第一行说要做啥,第二行是数据,最后还要空一行。

示例:批量加 2 个商品,再删 1 个商品。

POST http://localhost:9200/_bulk

{"index":{"_index":"product_index","_id":"3"}}  # 操作:新增,索引:product_index,ID=3

{"product_id":1003,"product_name":"小米14","price":4299.0,"create_time":"2024-03-01 09:00:00"}

{"index":{"_index":"product_index","_id":"4"}}

{"product_id":1004,"product_name":"OPPO Find X7","price":4499.0,"create_time":"2024-03-10 11:00:00"}

{"delete":{"_index":"product_index","_id":"2"}}  # 操作:删除,ID=2

踩坑提醒:Bulk 请求体不能格式化(别换行缩进),不然会报 “JSON 格式错误”,我第一次用就因为这个卡了半小时…

三、Java 实战:把 ES 集成到项目里

光用 curl 测试还不够,实际项目要在 Java 里调用 ES API。官方推荐用 “Elasticsearch Rest High Level Client”,封装得很好,不用自己写 HTTP 请求。

1. 第一步:加依赖(Maven)

关键是客户端版本要和 ES 服务器版本一致,比如服务器是 7.17.0,客户端也得是 7.17.0,不然会不兼容。

<!-- ES高level客户端 -->

<dependency>

    <groupId>org.elasticsearch.client</groupId>

    <artifactId>elasticsearch-rest-high-level-client</artifactId>

    <version>7.17.0</version>

</dependency>

<!-- ES核心包 -->

<dependency>

    <groupId>org.elasticsearch</groupId>

    <artifactId>elasticsearch</artifactId>

    <version>7.17.0</version>

</dependency>

<!-- Jackson:转JSON用 -->

<dependency>

    <groupId>com.fasterxml.jackson.core</groupId>

    <artifactId>jackson-databind</artifactId>

    <version>2.13.5</version>

</dependency>

2. 第二步:建客户端(单例模式)

客户端不用每次都新建,用单例模式建一个就行,不然连接太多会出问题。

import org.elasticsearch.client.RestHighLevelClient;

import org.springframework.stereotype.Component;

import java.io.IOException;

@Component

public class EsClientUtil {

    // 单例客户端

    private static RestHighLevelClient client;

    // 初始化客户端

    static {

        client = new RestHighLevelClient(

                RestClient.builder(

                        new HttpHost("localhost", 9200, "http")  // 改成你的ES地址

                )

        );

    }

    // 获取客户端

    public RestHighLevelClient getClient() {

        return client;

    }

    // 项目关闭时关闭客户端

    public void closeClient() throws IOException {

        if (client != null) {

            client.close();

        }

    }

}

3. 第三步:实现商品检索功能

需求:根据商品名关键词和价格区间搜商品,还要分页。

(1)先建个商品实体类

import lombok.Data;

import java.util.Date;

@Data  // 用Lombok省点代码,不想用也可以手动写getter/setter

public class Product {

    private Integer productId;

    private String productName;

    private Float price;

    private Date createTime;

}

(2)写检索服务

import com.fasterxml.jackson.databind.ObjectMapper;

import org.elasticsearch.action.search.SearchRequest;

import org.elasticsearch.action.search.SearchResponse;

import org.elasticsearch.client.RestHighLevelClient;

import org.elasticsearch.common.unit.Fuzziness;

import org.elasticsearch.index.query.BoolQueryBuilder;

import org.elasticsearch.index.query.QueryBuilders;

import org.elasticsearch.search.builder.SearchSourceBuilder;

import org.elasticsearch.search.sort.SortOrder;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;

import java.io.IOException;

import java.util.ArrayList;

import java.util.List;

@Service

public class ProductSearchService {

    @Autowired

    private EsClientUtil esClientUtil;

    private final ObjectMapper objectMapper = new ObjectMapper();

    private final String INDEX_NAME = "product_index";  // 索引名

    // 商品检索方法

    public List<Product> searchProducts(String keyword, Float minPrice, Float maxPrice,

                                      Integer pageNum, Integer pageSize) throws IOException {

        // 1. 建搜索请求

        SearchRequest searchRequest = new SearchRequest(INDEX_NAME);

        // 2. 拼查询条件

        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();

        // 关键词模糊匹配(允许1个字符输错,比如“华为”输成“华维”也能搜到)

        if (keyword != null && !keyword.isEmpty()) {

            boolQuery.must(QueryBuilders.matchQuery("product_name", keyword)

                    .fuzziness(Fuzziness.ONE));

        }

        // 价格区间过滤

        if (minPrice != null && maxPrice != null) {

            boolQuery.filter(QueryBuilders.rangeQuery("price")

                    .gte(minPrice)  // 大于等于

                    .lte(maxPrice));  // 小于等于

        } else if (minPrice != null) {

            boolQuery.filter(QueryBuilders.rangeQuery("price").gte(minPrice));

        } else if (maxPrice != null) {

            boolQuery.filter(QueryBuilders.rangeQuery("price").lte(maxPrice));

        }

        // 3. 设分页和排序

        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        sourceBuilder.query(boolQuery);

        sourceBuilder.from((pageNum - 1) * pageSize);  // 起始位置

        sourceBuilder.size(pageSize);  // 每页条数

        sourceBuilder.sort("create_time", SortOrder.DESC);  // 按创建时间降序

        // 4. 执行搜索

        searchRequest.source(sourceBuilder);

        RestHighLevelClient client = esClientUtil.getClient();

        SearchResponse response = client.search(searchRequest, RequestOptions.DEFAULT);

        // 5. 解析结果,转成Product列表

        List<Product> productList = new ArrayList<>();

        response.getHits().forEach(hit -> {

            try {

                Product product = objectMapper.readValue(hit.getSourceAsString(), Product.class);

                productList.add(product);

            } catch (IOException e) {

                e.printStackTrace();

            }

        });

        return productList;

    }

}

(3)写个接口测试

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.GetMapping;

import org.springframework.web.bind.annotation.RequestParam;

import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

import java.util.List;

@RestController

public class ProductSearchController {

    @Autowired

    private ProductSearchService productSearchService;

    @GetMapping("/search/products")

    public List<Product> searchProducts(

            @RequestParam(required = false) String keyword,

            @RequestParam(required = false) Float minPrice,

            @RequestParam(required = false) Float maxPrice,

            @RequestParam(defaultValue = "1") Integer pageNum,

            @RequestParam(defaultValue = "10") Integer pageSize) throws IOException {

        return productSearchService.searchProducts(keyword, minPrice, maxPrice, pageNum, pageSize);

    }

}

启动项目后,访问这个地址就能搜商品了:

http://localhost:8080/search/products?keyword=手机&minPrice=4000&maxPrice=8000&pageNum=1&pageSize=5

四、避坑指南:我踩过的坑,你别再踩了

连接失败 “Connection refused”

先检查 ES 服务器开没开,地址和端口对不对,Linux 的话还要看看 9200 端口有没有开放(用firewall-cmd --zone=public --add-port=9200/tcp --permanent开放)。

字段类型不匹配报错

比如映射里定义 product_name 是 text,结果传了个数字进去,肯定报错。新增数据前,先对照映射检查字段类型。

Bulk API 报 JSON 错误

记住两点:每两行一组,最后空一行;请求体别格式化,紧凑排列。

Java 客户端版本不兼容

客户端版本和 ES 服务器版本必须一致,别图省事随便写个版本号。

五、最后总结

ES REST API 其实没那么难,关键是先搞懂 “索引 - 映射 - 文档” 的概念,再通过实际操作熟悉请求格式,最后用 Java 客户端集成到项目里。

我刚开始学的时候也觉得复杂,但多试几次就会发现规律 —— 比如所有 API 都围绕 “操作索引” 和 “操作文档” 展开,Java 代码里的查询条件也都是拼出来的。

如果大家在实操中遇到其他问题,欢迎在评论区交流,咱们一起解决~

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容