Elasticsearch入门之SpringBoot整合Elasticsearch

目录

  • 前言
  • `SpringBoot` 整合 `Elasticsearch`
    • `Maven` 依赖
    • `application.properties` 文件
    • 文档对象
      • `Elasticsearch` 与关系型数据库对比
    • `Elasticsearch` 索引库相关操作
      • 创建索引和映射
      • 删除索引
    • `Elasticsearch` 中数据的 `CRUD` 操作
      • `dao` 层
      • `Service` 层
      • `Controller` 层
  • `Elasticsearch` 查询数据更多方式
    • 继承 `ElasticsearchRepository`
    • 方法名符合 `Spring Data` 规则
      • 方法名命名规则示例
    • 高级查询(自定义查询)
  • 接口测试
    • 添加接口
    • 查询接口
    • 根据价格区间查询
    • 根据标题中的关键字查询

前言

前面两篇文章完成了 windows 环境下 elasticsearch 的安装,这篇文章学习了解一下 elasticsearchAPI 以及简单的使用,算是入门级教程笔记

SpringBoot 整合 Elasticsearch

整合环境

  • springboot 版本 2.0.9.RELEASE
  • elasticsearch 版本 5.6.16

Maven 依赖

主要依赖如下,其它的自行引入

<dependency>
	<groupId>org.springframework.bootgroupId>
	<artifactId>spring-boot-starter-data-elasticsearchartifactId>
dependency>

application.properties 文件

server.port=8080

# 程序连接 elasticsearch 的端口号是9300
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
spring.data.elasticsearch.cluster-name=elasticsearch
spring.data.elasticsearch.repositories.enabled=true

文档对象

spring-boot-starter-data-elasticsearch 提供了面向对象的方式操作 elasticsearch

@Data
@AllArgsConstructor
@NoArgsConstructor
@Document(indexName = "item", type = "docs")
public class Item implements Serializable {

	@Id
	private Long id;

	@Field(type = FieldType.Text, analyzer = "ik_max_word")
	private String title; // 标题

	@Field(type = FieldType.Keyword)
	private String category;// 分类

	@Field(type = FieldType.Keyword)
	private String brand; // 品牌

	@Field(type = FieldType.Double)
	private Double price; // 价格

	@Field(index = false, type = FieldType.Keyword)
	private String images; // 图片地址
}
  • @Document:作用在类,标记实体类为文档对象
    • indexName:对应索引库名称
    • type:对应在索引库中的类型
    • shards:分片数量,默认 5
    • replicas:副本数量,默认 1
  • @Id:作用在成员变量,标记一个字段作为 id 主键
  • @Field:作用在成员变量,标记为文档的字段,并指定字段映射属性
    • type:字段类型,是枚举:FieldType,可以是 text、long、short、date、integer、object
      • text:存储数据时候,会自动分词,并生成索引
      • keyword:存储数据时候,不会分词建立索引
      • Numerical:数值类型,分两类
        • 基本数据类型:long、interger、short、byte、double、float、half_float
        • 浮点数的高精度类型:scaled_float,需要指定一个精度因子,比如 10100elasticsearch 会把真实值乘以这个因子后存储,取出时再还原
      • Date:日期类型,elasticsearch 可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为 long,节省空间
    • index:是否索引,布尔类型,默认是 true
    • store:是否存储,布尔类型,默认是 false
    • analyzer:分词器名称,这里的 ik_max_word 即使用ik分词器

Elasticsearch 与关系型数据库对比

关系型数据库(比如 Mysql) 非关系型数据库(Elasticsearch)
数据库 Database 索引库 Index
表 Table 类型 Type
数据行 Row 文档 Document
数据列 Column 字段 Field

Elasticsearch 索引库相关操作

创建索引和映射

索引和映射的创建,需要使用 ElasticsearchTemplate 模板工具类,在需要的地方注入即可

@Autowired
private ElasticsearchTemplate elasticsearchTemplate;

@Override
public void createIndexAndMapping() {
    // 创建索引,会根据 Item 类的 @Document 注解信息来创建
    elasticsearchTemplate.createIndex(Item.class);
    // 配置映射,会根据 Item 类中的 id、Field 等字段来自动完成映射
    elasticsearchTemplate.putMapping(Item.class);
}

索引信息如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第1张图片

删除索引

索引的删除,同样需要使用 ElasticsearchTemplate 模板工具类

@Override
public void deleteIndex() {
    elasticsearchTemplate.deleteIndex(Item.class);
    // 根据索引名字删除
    elasticsearchTemplate.deleteIndex("item1");
}

Elasticsearch 中数据的 CRUD 操作

dao

Spring Data 的强大之处,就在于你不用写任何 DAO 处理,自动根据方法名或类的信息进行 CRUD 操作。只要你定义一个接口,然后继承 Repository 提供的一些子接口,就能具备各种基本的 CRUD 功能

我们看到有一个 ElasticsearchCrudRepository 接口

@NoRepositoryBean
public interface ElasticsearchRepository<T, ID extends Serializable> extends ElasticsearchCrudRepository<T, ID> {

	<S extends T> S index(S entity);

	Iterable<T> search(QueryBuilder query);

	Page<T> search(QueryBuilder query, Pageable pageable);

	Page<T> search(SearchQuery searchQuery);

	Page<T> searchSimilar(T entity, String[] fields, Pageable pageable);

	void refresh();

	Class<T> getEntityClass();
}

所以,我们只需要定义接口,然后继承它就 OK 了,是不是和 Spring Data JPA 类似

// 泛型的参数分别是实体类型和主键类型
public interface ItemRepository extends ElasticsearchRepository<Item, Long> {
	
	// 自定义的方法
    List<Item> findByPriceBetween(double price1, double price2);

	// 自定义的方法
    List<Item> findByTitleLike(String keyword);
}

Service

@Slf4j
@Service
public class ItemServiceImpl implements ItemService {

	@Autowired
	private ItemRepository itemRepository;

	@Override
	public void saveAll(List<Item> list) {
		itemRepository.saveAll(list);
	}

	@Override
	public List<Item> findAll() {
		List<Item> items = new ArrayList<>();
		Iterable<Item> itemIterable = itemRepository.findAll();
		itemIterable.forEach(cc -> log.info("--------" + cc + "--------"));
		itemIterable.forEach(items::add);
		return items;
	}

	// 根据价格区间查询
	@Override
	public List<Item> findByPriceBetween(double price1, double price2) {
		return itemRepository.findByPriceBetween(price1, price2);
	}

	// 根据标题(实体类中的字段)中的关键字查询
	@Override
	public List<Item> findByTitleLike(String keyword) {
		return itemRepository.findByTitleLike(keyword);
	}
}

Controller

@Controller
@RequestMapping(path = {"/es"})
public class EsController {

    @Autowired
    private ItemService itemService;

    @RequestMapping(path = {"/add"}, method = {RequestMethod.POST})
    @ResponseBody
    public ResponseMap add() {
        List<Item> list = new ArrayList<>();
        list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.baidu.com/13123.jpg"));
        list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.baidu.com/13123.jpg"));
        try {
            itemService.saveAll(list);
            return new ResponseMap().success().message("添加数据成功");
        } catch (Exception e) {
            return new ResponseMap().error().message("添加数据失败");
        }
    }

    @RequestMapping(path = {"/selectAll"}, method = {RequestMethod.GET})
    @ResponseBody
    public ResponseMap selectAll() {
        List<Item> list = itemService.findAll();
        // 如果List集合不包含任何元素,返回true
        if (list.isEmpty()) {
            return new ResponseMap().error().message("查询数据失败");
        }
        return new ResponseMap().success().message("查询数据成功").data(list);
    }

    // 根据价格区间查询
    @RequestMapping(path = {"/selectByPrice"}, method = {RequestMethod.POST})
    @ResponseBody
    public ResponseMap selectByPrice(double price1, double price2) {
        List<Item> list = itemService.findByPriceBetween(price1, price2);
        if (list.isEmpty()) {
            return new ResponseMap().error().message("查询数据失败,可能没有你想要的数据");
        }
        return new ResponseMap().success().message("查询数据成功").data(list);
    }


    // 根据标题中(实体类中的字段)的关键字查询
    @RequestMapping(path = {"/customQuery"}, method = {RequestMethod.GET})
    @ResponseBody
    public ResponseMap customQuery(@RequestParam(name = "keyword") String keyword) {
        List<Item> list = itemService.findByTitleLike(keyword);
        if (list.isEmpty()) {
            return new ResponseMap().error().message("查询数据失败,可能没有你想要的数据");
        }
        return new ResponseMap().success().message("查询数据成功").data(list);
    }
}

Elasticsearch 查询数据更多方式

继承 ElasticsearchRepository

  • 自定义接口只要继承 ElasticsearchRepository 接口即可,默认会提供很多实现,比如 CRUD 和搜索相关的实现。类似于 Spring Data JPA 读取数据

方法名符合 Spring Data 规则

  • 自定义接口中声明方法:无需实现类,Spring Data 会根据方法名,自动生成实现类,方法名必须符合下面的规则

表格内容摘自 Spring Data Elasticsearch 官网

关键字 方法命名 Elasticsearch 查询DSL语法示例
And findByNameAndPrice { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }}
Or findByNameOrPrice { “query” : { “bool” : { “should” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } }, { “query_string” : { “query” : “?”, “fields” : [ “price” ] } } ] } }}
Is findByName { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }}
Not findByNameNot { “query” : { “bool” : { “must_not” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] } } ] } }}
Between findByPriceBetween { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
LessThan findByPriceLessThan { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : false } } } ] } }}
LessThanEqual findByPriceLessThanEqual { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
GreaterThan findByPriceGreaterThan { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : false, “include_upper” : true } } } ] } }}
GreaterThanEqual findByPriceGreaterThan { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }}
Before findByPriceBefore { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : null, “to” : ?, “include_lower” : true, “include_upper” : true } } } ] } }}
After findByPriceAfter { “query” : { “bool” : { “must” : [ {“range” : {“price” : {“from” : ?, “to” : null, “include_lower” : true, “include_upper” : true } } } ] } }}
Like findByNameLike { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
StartingWith findByNameStartingWith { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?*”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
EndingWith findByNameEndingWith { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “*?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
Contains/Containing findByNameContaining { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “?”, “fields” : [ “name” ] }, “analyze_wildcard”: true } ] } }}
In (when annotated as FieldType.Keyword) findByNameIn(Collectionnames) { “query” : { “bool” : { “must” : [ {“bool” : {“must” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }}
In findByNameIn(Collectionnames) { “query”: {“bool”: {“must”: [{“query_string”:{“query”: “”?" “?”", “fields”: [“name”]}}]}}}
NotIn (when annotated as FieldType.Keyword) findByNameNotIn(Collectionnames) { “query” : { “bool” : { “must” : [ {“bool” : {“must_not” : [ {“terms” : {“name” : ["?","?"]}} ] } } ] } }}
NotIn findByNameNotIn(Collectionnames) {“query”: {“bool”: {“must”: [{“query_string”: {“query”: “NOT(”?" “?”)", “fields”: [“name”]}}]}}}
Near findByStoreNear Not Supported Yet !
True findByAvailableTrue { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }}
False findByAvailableFalse { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “false”, “fields” : [ “available” ] } } ] } }}
OrderBy findByAvailableTrueOrderByNameDesc { “query” : { “bool” : { “must” : [ { “query_string” : { “query” : “true”, “fields” : [ “available” ] } } ] } }, “sort”:[{“name”:{“order”:“desc”}}] }

方法名命名规则示例

public interface EmployeeInfoRepository extends ElasticsearchRepository<EmployeeInfo, Long> {

    // 精确查找 方法名规则:finByxxx
    List<EmployeeInfo> findByName(String name);

    // AND 语句查询
    List<EmployeeInfo> findByNameAndAge(String name, Integer age);

    // OR 语句查询
    List<EmployeeInfo> findByNameOrAge(String name, Integer age);

    // 分页查询员工信息
    Page<EmployeeInfo> findByName(String name, Pageable page);

    // NOT 语句查询
    Page<EmployeeInfo> findByNameNot(String name, Pageable page);

    // LIKE 语句查询
    Page<EmployeeInfo> findByNameLike(String name, Pageable page);
}

高级查询(自定义查询)

  • matchQuery:词条匹配,先分词然后在调用termQuery进行匹配
  • TermQuery:词条匹配,不分词
  • wildcardQuery:通配符匹配
  • fuzzyQuery:模糊匹配
  • rangeQuery:范围匹配
  • booleanQuery:布尔查询

自定义查询详情参考: https://blog.csdn.net/weixin_43814195/article/details/85281287

接口测试

添加接口

使用 postman 测试接口 http://localhost:8080/es/add,结果如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第2张图片
elasticSearch-head 插件页面查询新增的数据,如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第3张图片

查询接口

使用 postman 测试接口 http://localhost:8080/es/selectAll,结果如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第4张图片

根据价格区间查询

使用 postman 测试接口 http://localhost:8080/es/selectByPrice,注意查看上述添加的两条数据的 price 字段,结果如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第5张图片

根据标题中的关键字查询

根据文档对象 Item 中的 title 字段来查询,使用 postman 测试接口 http://localhost:8080/es/customQuery,结果如下

Elasticsearch入门之SpringBoot整合Elasticsearch_第6张图片
推荐参考:https://blog.csdn.net/weixin_43814195/article/details/85281287
推荐参考:https://blog.csdn.net/qq_38011415/article/details/112241548

你可能感兴趣的:(中间件,#,elasticsearch,elasticsearch)