前言
前面两篇文章完成了
windows
环境下
elasticsearch
的安装,这篇文章学习了解一下
elasticsearch
的
API
以及简单的使用,算是入门级教程笔记
SpringBoot
整合
Elasticsearch
SpringBoot
Elasticsearch
整合环境
-
springboot
版本
2.0.9.RELEASE
-
elasticsearch
版本
5.6.16
Maven
依赖
Maven
主要依赖如下,其它的自行引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
application.properties
文件
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
,需要指定一个精度因子,比如
10
或
100
。
elasticsearch
会把真实值乘以这个因子后存储,取出时再还原
-
基本数据类型:
-
Date
:日期类型,
elasticsearch
可以对日期格式化为字符串存储,但是建议我们存储为毫秒值,存储为
long
,节省空间
-
-
index
:是否索引,布尔类型,默认是
true
-
store
:是否存储,布尔类型,默认是
false
-
analyzer
:分词器名称,这里的
ik_max_word
即使用ik分词器
-
Elasticsearch
与关系型数据库对比
Elasticsearch
关系型数据库(比如 Mysql) | 非关系型数据库(Elasticsearch) |
---|---|
数据库 Database | 索引库 Index |
表 Table | 类型 Type |
数据行 Row | 文档 Document |
数据列 Column | 字段 Field |
Elasticsearch
索引库相关操作
Elasticsearch
创建索引和映射
索引和映射的创建,需要使用
ElasticsearchTemplate
模板工具类,在需要的地方注入即可
@Autowired
private ElasticsearchTemplate elasticsearchTemplate;
@Override
public void createIndexAndMapping() {
// 创建索引,会根据 Item 类的 @Document 注解信息来创建
elasticsearchTemplate.createIndex(Item.class);
// 配置映射,会根据 Item 类中的 id、Field 等字段来自动完成映射
elasticsearchTemplate.putMapping(Item.class);
}
索引信息如下
删除索引
索引的删除,同样需要使用
ElasticsearchTemplate
模板工具类
@Override
public void deleteIndex() {
elasticsearchTemplate.deleteIndex(Item.class);
// 根据索引名字删除
elasticsearchTemplate.deleteIndex("item1");
}
Elasticsearch
中数据的
CRUD
操作
Elasticsearch
CRUD
dao
层
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
层
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
@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
查询数据更多方式
Elasticsearch
继承
ElasticsearchRepository
ElasticsearchRepository
-
自定义接口只要继承
ElasticsearchRepository
接口即可,默认会提供很多实现,比如
CRUD
和搜索相关的实现。类似于
Spring Data JPA
读取数据
方法名符合
Spring Data
规则
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-head
插件页面查询新增的数据,如下
查询接口
使用
postman
测试接口
http://localhost:8080/es/selectAll
,结果如下
根据价格区间查询
使用
postman
测试接口
http://localhost:8080/es/selectByPrice
,注意查看上述添加的两条数据的
price
字段,结果如下
根据标题中的关键字查询
根据文档对象
Item
中的
title
字段来查询,使用
postman
测试接口
http://localhost:8080/es/customQuery
,结果如下
推荐参考:
https://blog.csdn.net/weixin_43814195/article/details/85281287
推荐参考:
https://blog.csdn.net/qq_38011415/article/details/112241548