10-ES客户端索引相关操作

发布时间 2023-09-23 12:21:12作者: 爱杨帅

新建业务包

├─config	    # 配置文件
├─controller	# 控制器
├─entity	    # 实体映射
│  └─response	# 响应实体
└─service	    # 相关业务

在 response 包下,新建两个类,分别是 ResultCode(interface), ResponseResult.java:

ResultCode.java:

/**
 * @author BNTang
 */
public interface ResultCode {
    /**
     * 成功状态码
     */
    Integer SUCCESS = 20000;
    /**
     * 失败的状态码
     */
    Integer ERROR = 20001;
}

ResponseResult.java:

@Data
public class ResponseResult {
    private ResponseResult() {
    }

    @ApiModelProperty(value = "是否成功")
    private Boolean success;

    @ApiModelProperty(value = "状态码")
    private Integer code;

    @ApiModelProperty(value = "返回消息")
    private String message;

    @ApiModelProperty(value = "返回的数据")
    private Map<String, Object> RESPONSE_DATA_MAP = new HashMap<>();

    /**
     * 提供工具方法
     */
    public static ResponseResult ok() {
        ResponseResult responseResult = new ResponseResult();
        responseResult.setSuccess(true);
        responseResult.setCode(ResultCode.SUCCESS);
        responseResult.setMessage("成功");
        return responseResult;
    }

    public static ResponseResult error() {
        ResponseResult responseResult = new ResponseResult();
        responseResult.setSuccess(false);
        responseResult.setCode(ResultCode.ERROR);
        responseResult.setMessage("失败");
        return responseResult;
    }

    public ResponseResult success(Boolean success) {
        this.setSuccess(success);
        return this;
    }

    public ResponseResult message(String message) {
        this.setMessage(message);
        return this;
    }

    public ResponseResult code(Integer code) {
        this.setCode(code);
        return this;
    }

    public ResponseResult data(String key, Object value) {
        this.RESPONSE_DATA_MAP.put(key, value);
        return this;
    }

    public ResponseResult data(Map<String, Object> map) {
        this.setRESPONSE_DATA_MAP(map);
        return this;
    }
}

然后接下来就可以来看本文需要介绍的索引相关的操作内容了:

索引操作

查看索引是否存在

首先在 service 包当中新建一个 IElasticSearchService.java 接口,在当中添加一个判断索引是否存在的约束方法如下:

/**
 * @author BNTang
 * @version V1.0
 * @project SpringBoot-ElasticSearch-Pro
 * @date Created in 2022/3/5 /005 18:41
 * @description elasticsearch 接口
 **/
public interface IElasticSearchService {
    /**
     * 查看索引是否存在
     *
     * @param indexName 索引名称
     * @return boolean true,代表存在,false,代表不存在
     */
    boolean seeIndexIsNoExists(String indexName);
}

还是在 service 包当中创建所对应的实现类来实现这个判断索引是否存在的方法, 新建一个 ElasticSearchServiceImpl.java:

/**
 * @author BNTang
 * @version V1.0
 * @project SpringBoot-ElasticSearch-Pro
 * @date Created in 2022/3/5 /005 18:45
 * @description
 **/
@Service
public class ElasticSearchServiceImpl implements IElasticSearchService {
    private final Log logger = LogFactory.getLog(ElasticSearchServiceImpl.class);

    private ElasticsearchClient elasticsearchClient;

    @Autowired
    public void setElasticsearchClient(ElasticsearchClient elasticsearchClient) {
        this.elasticsearchClient = elasticsearchClient;
    }

    @Override
    public boolean seeIndexIsNoExists(String indexName) {
        ExistsRequest existsRequest = new ExistsRequest.Builder().index(indexName)
                .build();

        try {
            BooleanResponse booleanResponse = this.elasticsearchClient.indices()
                    .exists(existsRequest);
            return booleanResponse.value();
        } catch (IOException e) {
            logger.error("see Index Is No Exists error", e);
        }

        return false;
    }
}

然后,在 controller 包当中创建一个控制器用来测试用,ElasticSearchController.java:

/**
 * @author BNTang
 * @version V1.0
 * @project SpringBoot-ElasticSearch-Pro
 * @date Created in 2022/3/5 /005 18:48
 * @description
 **/
@Api("ElasticSearch Api Controller")
@RequestMapping("elasticSearch-controller")
@RestController
public class ElasticSearchController {

    private ElasticSearchServiceImpl elasticSearchService;

    @Autowired
    public void setElasticSearchService(ElasticSearchServiceImpl elasticSearchService) {
        this.elasticSearchService = elasticSearchService;
    }

    @ApiOperation("查看索引是否存在")
    @GetMapping("seeIndexIsNoExists")
    public boolean seeIndexIsNoExists(String indexName) {
        return this.elasticSearchService.seeIndexIsNoExists(indexName);
    }
}

然后就是启动工程,访问 swagger,进行接口调试即可,这一步博主不贴出来了自行测试。

创建索引

在 IElasticSearchService.java 当中添加如下约束方法:

/**
 * 创建索引
 *
 * @param indexName   索引名称
 * @param numOfShards 分片数
 * @param properties  属性
 * @return boolean true,代表创建成功,false,代表创建失败
 */
boolean createIndex(String indexName, int numOfShards, Map<String, Property> properties);

在 ElasticSearchServiceImpl.java 实现类当中进行实现:

@Override
public boolean createIndex(String indexName, int numOfShards, Map<String, Property> properties) {
    // 创建的映射处理
    TypeMapping typeMapping = new TypeMapping.Builder()
            .properties(properties)
            .build();

    IndexSettings indexSettings = new IndexSettings.Builder()
            .numberOfShards(String.valueOf(numOfShards))
            .build();

    CreateIndexRequest createIndexRequest = new CreateIndexRequest.Builder()
            .index(indexName)
            .mappings(typeMapping)
            .settings(indexSettings)
            .build();

    try {
        return Optional
                .ofNullable(this.elasticsearchClient.indices().create(createIndexRequest).acknowledged())
                .orElse(Boolean.FALSE);
    } catch (IOException e) {
        logger.error("create Index error", e);
    }

    return false;
}

控制器:

@ApiOperation("创建索引")
@GetMapping("createIndex")
public ResponseResult createIndex(String indexName) {
    Map<String, Property> propertyMap = new HashMap<>();
    propertyMap.put("id", new Property(new LongNumberProperty.Builder().index(true).store(true).build()));
    propertyMap.put("name", new Property(new TextProperty.Builder().index(true).analyzer("ik_max_word").store(true).build()));

    if (this.elasticSearchService.createIndex(indexName, 1, propertyMap)) {
        return ResponseResult.ok();
    }
    return ResponseResult.error();
}

然后就是启动工程,访问 swagger,进行接口调试即可,这一步博主不贴出来了自行测试。

删除索引

接口:

/**
 * 删除索引
 *
 * @param indexNameList 索引名称列表
 * @return boolean
 */
boolean deleteIndex(List<String> indexNameList);

实现类:

@Override
public boolean deleteIndex(List<String> indexNameList) {
    DeleteIndexRequest deleteIndexRequest = new DeleteIndexRequest.Builder()
            .index(indexNameList)
            .build();

    try {
        return this.elasticsearchClient.indices()
                .delete(deleteIndexRequest).acknowledged();
    } catch (IOException e) {
        logger.error("delete Index error", e);
    }

    return false;
}

控制器:

@ApiOperation("删除索引")
@PostMapping("deleteIndex")
public ResponseResult deleteIndex(@RequestParam List<String> indexNameList) {
    if (this.elasticSearchService.deleteIndex(indexNameList)) {
        return ResponseResult.ok();
    }
    return ResponseResult.error();
}

根据 ID 查询(批量OR单个)

更改 IElasticSearchService.java 接口新增泛型:

public interface IElasticSearchService<T>

然后在新增两个查询数据的方法约束:

/**
 * 根据id获取文档
 *
 * @param index index
 * @param id    id
 * @param clazz clazz 把查询的结果封装成对象的实体
 * @return T
 */
T getById(String index, String id, Class<T> clazz);

/**
 * 根据id列表获取文档
 *
 * @param index  index
 * @param idList id
 * @param clazz  clazz
 * @return List<T>
 */
List<T> getByIdList(String index, List<String> idList, Class<T> clazz);

修改 ElasticSearchServiceImpl.java 实现类,也需要添加所对应的泛型:

public class ElasticSearchServiceImpl<T> implements IElasticSearchService<T>

实现根据ID查询的业务方法,可以批量也可以单个:

@Override
public T getById(String index, String id, Class<T> clazz) {
    GetRequest getRequest = new GetRequest.Builder()
            .index(index)
            .id(id)
            .build();
    try {
        return this.elasticsearchClient.get(getRequest, clazz)
                .source();
    } catch (IOException e) {
        logger.error("get By Id error", e);
    }
    return null;
}

@Override
public List<T> getByIdList(String index, List<String> idList, Class<T> clazz) {
    try {
        List<T> tList = new ArrayList<>(idList.size());
        for (String id : idList) {

            GetRequest getRequest = new GetRequest.Builder()
                    .index(index)
                    .id(id)
                    .build();

            T source = this.elasticsearchClient.get(getRequest, clazz)
                    .source();

            tList.add(source);
        }

        return tList;
    } catch (IOException e) {
        logger.error("get By Id List error", e);
    }
    return null;
}

然后在控制器调用之前首先在新建一个实体类用来封装从ES当中查询出来的数据新建 Goods.java:

/**
 * @author BNTang
 * @version V1.0
 * @project SpringBoot-ElasticSearch-Pro
 * @date Created in 2022/4/3 /003 23:20
 * @description
 **/
@Data
public class Goods implements Serializable {
    /**
     * id
     */
    private String id;
    /**
     * 名字
     */
    private String name;
    /**
     * 价格
     */
    private BigDecimal price;
    /**
     * 描述
     */
    private String description;
    /**
     * 创建日期
     */
    private String create_date;
}

修改控制器进行使用,测试一下根据 ID 查询出来的结果和批量的接口即可,这里博主不再赘述,只是贴出所对应的控制器代码,修改 ElasticSearchController.java:

private ElasticSearchServiceImpl<Goods> elasticSearchService;

@Autowired
public void setElasticSearchService(ElasticSearchServiceImpl<Goods> elasticSearchService) {
    this.elasticSearchService = elasticSearchService;
}
@ApiOperation(value = "根据id获取文档数据")
@GetMapping("/api/v1/getById/{index}/{id}")
public ResponseResult getById(@PathVariable String index, @PathVariable String id) {
    return ResponseResult.ok().data("goods", this.elasticSearchService.getById(index, id, Goods.class));
}

@ApiOperation(value = "根据id数组获取文档数据")
@PostMapping("/api/v1/getByIdList")
public ResponseResult getByIdList(@RequestParam(value = "ids") List<String> ids,
                                  @RequestParam(value = "index") String index) {
    return ResponseResult.ok().data("goods", this.elasticSearchService.getByIdList(index, ids, Goods.class));
}

image-20220404174103200

分页查询

修改接口添加分页查询约束方法:

/**
 * 分页查询
 *
 * @param index    index
 * @param pageNo   pageNo
 * @param pageSize pageSize
 * @param clazz    clazz
 * @return 查询结果
 */
List<T> searchByPages(String index, Integer pageNo, Integer pageSize, Class<T> clazz);

实现类:

@Override
public List<T> searchByPages(String index, Integer pageNo, Integer pageSize, Class<T> clazz) {
    SearchRequest searchRequest = new SearchRequest.Builder()
            .index(Collections.singletonList(index))
            .from(pageNo)
            .size(pageSize)
            .build();

    List<T> res = new ArrayList<>();

    try {
        SearchResponse<T> searchResponse = this.elasticsearchClient.search(searchRequest, clazz);
        HitsMetadata<T> hitsMetadata = searchResponse.hits();
        hitsMetadata.hits().forEach(action -> res.add(action.source()));
        return res;
    } catch (IOException e) {
        logger.error("search By Pages error", e);
    }

    return null;
}

控制器:

@ApiOperation(value = "分页查询文档")
@PostMapping("/getAll")
public ResponseResult getAll(String index, int pageNo, int pageSize) {
    return ResponseResult.ok().data("goods", this.elasticSearchService.searchByPages(index, pageNo, pageSize, Goods.class));
}

分页条件查询

接口:

/**
 * 分页条件查询
 *
 * @param index    index
 * @param pageNo   当前页
 * @param pageSize 每页多少条数据
 * @param clazz    clazz  封装的实现
 * @return 查询结果
 */
List<T> searchByQuery(String index, String queryString, Integer pageNo, Integer pageSize, Class<T> clazz);

实现类:

@Override
public List<T> searchByQuery(String index, String queryString, Integer pageNo, Integer pageSize, Class<T> clazz) {
    // 1.构建查询的对象
    QueryStringQuery stringQuery = new QueryStringQuery.Builder()
            .fields("name", "description")
            .query(queryString)
            .build();

    Query query = new Query.Builder()
            .queryString(stringQuery)
            .build();

    // 2.搜索
    SearchRequest searchRequest = new SearchRequest.Builder()
            .index(index)
            .from(pageNo)
            .size(pageSize)
            .query(query)
            .build();

    try {
        return this.elasticsearchClient.search(searchRequest, clazz)
                .hits().hits().stream()
                .map(Hit::source)
                .collect(Collectors.toList());
    } catch (IOException e) {
        logger.error("search By Query error", e);
    }
    return null;
}

控制器:

@ApiOperation(value = "分页条件查询")
@PostMapping("/querySearch")
public ResponseResult getAll(String index, String queryString, Integer pageNo, Integer pageSize) {
    return ResponseResult.ok().data("goods", this.elasticSearchService.searchByQuery(index, queryString, pageNo, pageSize, Goods.class));
}

image-20220404220235892

分页条件查询高亮

接口:

/**
 * 分页条件高亮查询
 *
 * @param index    index
 * @param pageNo   当前页
 * @param pageSize 每页多少条数据
 * @param clazz    clazz  封装的实现
 * @return 查询结果
 */
List<T> searchByQueryHighlight(String index, String queryString, Integer pageNo, Integer pageSize, Class<T> clazz);

实现类:

@Override
public List<T> searchByQueryHighlight(String index, String queryString, Integer pageNo, Integer pageSize, Class<T> clazz) {
    // 1.构建查询的对象
    QueryStringQuery stringQuery = new QueryStringQuery.Builder()
            .fields("name", "description")
            .query(queryString)
            .build();

    Query query = new Query.Builder()
            .queryString(stringQuery)
            .build();

    // 高亮显示
    HighlightField highlightField = new HighlightField.Builder()
            .matchedFields("name")
            .preTags("<span style=\"color:red\">")
            .postTags("</span>")
            .build();

    Highlight highlight = new Highlight.Builder()
            .fields("name", highlightField)
            .requireFieldMatch(false)
            .build();

    // 2.搜索请求
    SearchRequest searchRequest = new SearchRequest.Builder()
            .index(index)
            .from(pageNo)
            .size(pageSize)
            .query(query)
            .highlight(highlight)
            .build();

    try {
        return this.elasticsearchClient.search(searchRequest, clazz)
                .hits()
                .hits()
                .stream()
                .map(mapper -> {
                    String name = mapper.highlight()
                            .get("name")
                            .get(0);

                    Goods goods = (Goods) mapper.source();
                    Goods anElse = Optional.ofNullable(goods)
                            .orElse(new Goods());
                    anElse.setName(name);
                    return mapper.source();
                }).collect(Collectors.toList());
    } catch (IOException e) {
        logger.error("search By Query Highlight error", e);
    }

    return null;
}

控制器:

@ApiOperation(value = "分页条件查询高亮")
@PostMapping("/api/v1/searchByQueryHighlight")
public ResponseResult searchByQueryHighlight(String index, String queryString, Integer pageNo, Integer pageSize) {
    return ResponseResult.ok().data("goods", this.elasticSearchService.searchByQueryHighlight(index, queryString, pageNo, pageSize, Goods.class));
}

image-20220405002735107