5.Spring Data Elasticsearch
Elasticsearch提供的Java客户端有一些不太方便的地方:
- 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的
- 需要自己把对象序列化为json存储
- 查询到结果也需要自己反序列化为对象
因此,我们这里就不讲解原生的Elasticsearch客户端API了。
而是学习Spring提供的套件:Spring Data Elasticsearch
5.1.简介
Spring Data Elasticsearch是Spring Data项目下的一个子模块。
查看 Spring Data的官网:http://projects.spring.io/spring-data/
Spring Data 是的使命是给各种数据访问提供统一的编程接口,不管是关系型数据库(如MySQL),还是非关系数据库(如Redis),或者类似Elasticsearch这样的索引数据库。从而简化开发人员的代码,提高开发效率。
包含很多不同数据操作的模块:
Spring Data Elasticsearch的页面:https://projects.spring.io/spring-data-elasticsearch/
特征:
- 支持Spring的基于
@Configuration
的java配置方式,或者XML配置方式 - 提供了用于操作ES的便捷工具类**
ElasticsearchTemplate
**。包括实现文档到POJO之间的自动智能映射。 - 利用Spring的数据转换服务实现的功能丰富的对象映射
- 基于注解的元数据映射方式,而且可扩展以支持更多不同的数据格式
- 根据持久层接口自动生成对应实现方法,无需人工编写基本操作代码(类似mybatis,根据接口自动得到实现)。当然,也支持人工定制查询
5.2.创建Demo工程
我们新建一个demo,学习Elasticsearch
pom依赖:
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion>
<span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>com.leyou.demo<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>elasticsearch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>0.0.1-SNAPSHOT<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>packaging</span><span class="token punctuation">></span></span>jar<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>packaging</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>name</span><span class="token punctuation">></span></span>elasticsearch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>name</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>description</span><span class="token punctuation">></span></span>Demo project for Spring Boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>description</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>parent</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-parent<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>version</span><span class="token punctuation">></span></span>2.0.2.RELEASE<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>relativePath</span><span class="token punctuation">/></span></span> <span class="token comment"><!-- lookup parent from repository --></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>parent</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>properties</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span>UTF-8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project.build.sourceEncoding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>project.reporting.outputEncoding</span><span class="token punctuation">></span></span>UTF-8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>project.reporting.outputEncoding</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>java.version</span><span class="token punctuation">></span></span>1.8<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>java.version</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>properties</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependencies</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-data-elasticsearch<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-starter-test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>scope</span><span class="token punctuation">></span></span>test<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>scope</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependency</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>dependencies</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>build</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>groupId</span><span class="token punctuation">></span></span>org.springframework.boot<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>groupId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"><</span>artifactId</span><span class="token punctuation">></span></span>spring-boot-maven-plugin<span class="token tag"><span class="token tag"><span class="token punctuation"></</span>artifactId</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plugin</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>plugins</span><span class="token punctuation">></span></span> <span class="token tag"><span class="token tag"><span class="token punctuation"></</span>build</span><span class="token punctuation">></span></span>
</project>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
application.yml文件配置:
spring:
data:
elasticsearch:
cluster-name: elasticsearch
cluster-nodes: 192.168.56.101:9300
- 1
- 2
- 3
- 4
- 5
5.3.索引操作
5.3.1.创建索引和映射
实体类
首先我们准备好实体类:
public class Item {
Long id;
String title; //标题
String category;// 分类
String brand; // 品牌
Double price; // 价格
String images; // 图片地址
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
映射
Spring Data通过注解来声明字段的映射属性,有下面的三个注解:
@Document
作用在类,标记实体类为文档对象,一般有两个属性- indexName:对应索引库名称
- type:对应在索引库中的类型
- shards:分片数量,默认5
- replicas:副本数量,默认1
@Id
作用在成员变量,标记一个字段作为id主键@Field
作用在成员变量,标记为文档的字段,并指定字段映射属性:- type:字段类型,是是枚举:FieldType
- index:是否索引,布尔类型,默认是true
- store:是否存储,布尔类型,默认是false
- analyzer:分词器名称
示例:
@Document(indexName = "item",type = "docs", shards = 1, replicas = 0) public class Item { @Id private Long id;
<span class="token annotation punctuation">@Field</span><span class="token punctuation">(</span>type <span class="token operator">=</span> FieldType<span class="token punctuation">.</span>Text<span class="token punctuation">,</span> analyzer <span class="token operator">=</span> <span class="token string">"ik_max_word"</span><span class="token punctuation">)</span> <span class="token keyword">private</span> String title<span class="token punctuation">;</span> <span class="token comment">//标题</span> <span class="token annotation punctuation">@Field</span><span class="token punctuation">(</span>type <span class="token operator">=</span> FieldType<span class="token punctuation">.</span>Keyword<span class="token punctuation">)</span> <span class="token keyword">private</span> String category<span class="token punctuation">;</span><span class="token comment">// 分类</span> <span class="token annotation punctuation">@Field</span><span class="token punctuation">(</span>type <span class="token operator">=</span> FieldType<span class="token punctuation">.</span>Keyword<span class="token punctuation">)</span> <span class="token keyword">private</span> String brand<span class="token punctuation">;</span> <span class="token comment">// 品牌</span> <span class="token annotation punctuation">@Field</span><span class="token punctuation">(</span>type <span class="token operator">=</span> FieldType<span class="token punctuation">.</span>Double<span class="token punctuation">)</span> <span class="token keyword">private</span> Double price<span class="token punctuation">;</span> <span class="token comment">// 价格</span> <span class="token annotation punctuation">@Field</span><span class="token punctuation">(</span>index <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">,</span> type <span class="token operator">=</span> FieldType<span class="token punctuation">.</span>Keyword<span class="token punctuation">)</span> <span class="token keyword">private</span> String images<span class="token punctuation">;</span> <span class="token comment">// 图片地址</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
创建索引
ElasticsearchTemplate中提供了创建索引的API:
@Test
public void testCreate(){
//创建索引库
elasticsearchTemplate.createIndex(Item.class);
}
- 1
- 2
- 3
- 4
- 5
- 可以根据类的信息自动生成,也可以手动指定indexName和Settings
映射
映射相关的API:
@Test
public void testCreate(){
//创建索引库
elasticsearchTemplate.createIndex(Item.class);
//映射关系
elasticsearchTemplate.putMapping(Item.class);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 一样,可以根据类的字节码信息(注解配置)来生成映射,或者手动编写映射
我们这里采用类的字节码信息创建索引并映射:
@Test
public void createIndex() {
// 创建索引,会根据Item类的@Document注解信息来创建
esTemplate.createIndex(Item.class);
// 配置映射,会根据Item类中的id、Field等字段来自动完成映射
esTemplate.putMapping(Item.class);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
结果:
GET /item
{
"item": {
"aliases": {},
"mappings": {
"docs": {
"properties": {
"brand": {
"type": "keyword"
},
"category": {
"type": "keyword"
},
"images": {
"type": "keyword",
"index": false
},
"price": {
"type": "double"
},
"title": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
},
"settings": {
"index": {
"refresh_interval": "1s",
"number_of_shards": "1",
"provided_name": "item",
"creation_date": "1525405022589",
"store": {
"type": "fs"
},
"number_of_replicas": "0",
"uuid": "4sE9SAw3Sqq1aAPz5F6OEg",
"version": {
"created": "6020499"
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
5.3.2.删除索引
删除索引的API:
可以根据类名或索引名删除。
示例:
@Test
public void deleteIndex() {
esTemplate.deleteIndex("heima");
}
- 1
- 2
- 3
- 4
5.4.新增文档数据
5.4.1.Repository接口
Spring Data 的强大之处,就在于你不用写任何DAO处理,自动根据方法名或类的信息进行CRUD操作。只要你定义一个接口,然后继承Repository提供的一些子接口,就能具备各种基本的CRUD功能。
来看下Repository的继承关系:
我们看到有一个ElasticsearchCrudRepository接口:
所以,我们只需要定义接口,然后继承它就OK了。
public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
}
- 1
- 2
接下来,我们测试新增数据:
5.4.2.新增一个对象
@Autowired
private ItemRepository itemRepository;
@Test
public void index() {
Item item = new Item(1L, “小米手机7”, " 手机",
“小米”, 3499.00, “http://image.leyou.com/13123.jpg”);
itemRepository.save(item);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
去页面查询看看:
{
"took": 0,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 1,
"hits": [
{
"_index": "item",
"_type": "docs",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"title": "小米手机7",
"category": " 手机",
"brand": "小米",
"price": 3499,
"images": "http://image.leyou.com/13123.jpg"
}
}
}
]
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
5.4.3.批量新增
代码:
@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(2L, "坚果手机R1", " 手机", "锤子", 3699.00, "http://image.leyou.com/123.jpg"));
list.add(new Item(3L, "华为META10", " 手机", "华为", 4499.00, "http://image.leyou.com/3.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
再次去页面查询:
{
"took": 5,
"timed_out": false,
"_shards": {
"total": 1,
"successful": 1,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 1,
"hits": [
{
"_index": "item",
"_type": "docs",
"_id": "2",
"_score": 1,
"_source": {
"id": 2,
"title": "坚果手机R1",
"category": " 手机",
"brand": "锤子",
"price": 3699,
"images": "http://image.leyou.com/13123.jpg"
}
},
{
"_index": "item",
"_type": "docs",
"_id": "3",
"_score": 1,
"_source": {
"id": 3,
"title": "华为META10",
"category": " 手机",
"brand": "华为",
"price": 4499,
"images": "http://image.leyou.com/13123.jpg"
}
},
{
"_index": "item",
"_type": "docs",
"_id": "1",
"_score": 1,
"_source": {
"id": 1,
"title": "小米手机7",
"category": " 手机",
"brand": "小米",
"price": 3499,
"images": "http://image.leyou.com/13123.jpg"
}
}
]
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
5.4.4.修改
修改和新增是同一个接口,区分的依据就是id,这一点跟我们在页面发起PUT请求是类似的。
5.5.查询
5.5.1.基本查询
ElasticsearchRepository提供了一些基本的查询方法:
我们来试试查询所有:
@Test
public void query(){
// 查询全部,并安装价格降序排序
Iterable<Item> items = this.itemRepository.findAll(Sort.by("price").descending());
for (Item item : items) {
System.out.println("item = " + item);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
结果:
5.5.2.自定义方法
Spring Data 的另一个强大功能,是根据方法名称自动实现功能。
比如:你的方法名叫做:findByTitle,那么它就知道你是根据title查询,然后自动帮你完成,无需写实现类。
当然,方法名称要符合一定的约定:
Keyword | Sample | Elasticsearch Query String |
---|---|---|
And | findByNameAndPrice | {"bool" : {"must" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Or | findByNameOrPrice | {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"price" : "?"}} ]}} |
Is | findByName | {"bool" : {"must" : {"field" : {"name" : "?"}}}} |
Not | findByNameNot | {"bool" : {"must_not" : {"field" : {"name" : "?"}}}} |
Between | findByPriceBetween | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
LessThanEqual | findByPriceLessThan | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
GreaterThanEqual | findByPriceGreaterThan | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Before | findByPriceBefore | {"bool" : {"must" : {"range" : {"price" : {"from" : null,"to" : ?,"include_lower" : true,"include_upper" : true}}}}} |
After | findByPriceAfter | {"bool" : {"must" : {"range" : {"price" : {"from" : ?,"to" : null,"include_lower" : true,"include_upper" : true}}}}} |
Like | findByNameLike | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
StartingWith | findByNameStartingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "?*","analyze_wildcard" : true}}}}} |
EndingWith | findByNameEndingWith | {"bool" : {"must" : {"field" : {"name" : {"query" : "*?","analyze_wildcard" : true}}}}} |
Contains/Containing | findByNameContaining | {"bool" : {"must" : {"field" : {"name" : {"query" : "**?**","analyze_wildcard" : true}}}}} |
In | findByNameIn(Collection<String>names) | {"bool" : {"must" : {"bool" : {"should" : [ {"field" : {"name" : "?"}}, {"field" : {"name" : "?"}} ]}}}} |
NotIn | findByNameNotIn(Collection<String>names) | {"bool" : {"must_not" : {"bool" : {"should" : {"field" : {"name" : "?"}}}}}} |
Near | findByStoreNear | Not Supported Yet ! |
True | findByAvailableTrue | {"bool" : {"must" : {"field" : {"available" : true}}}} |
False | findByAvailableFalse | {"bool" : {"must" : {"field" : {"available" : false}}}} |
OrderBy | findByAvailableTrueOrderByNameDesc | {"sort" : [{ "name" : {"order" : "desc"} }],"bool" : {"must" : {"field" : {"available" : true}}}} |
例如,我们来按照价格区间查询,定义这样的一个方法:
public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
<span class="token comment">/** * 根据价格区间查询 * @param price1 * @param price2 * @return */</span> List<span class="token generics function"><span class="token punctuation"><</span>Item<span class="token punctuation">></span></span> <span class="token function">findByPriceBetween</span><span class="token punctuation">(</span><span class="token keyword">double</span> price1<span class="token punctuation">,</span> <span class="token keyword">double</span> price2<span class="token punctuation">)</span><span class="token punctuation">;</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
然后添加一些测试数据:
@Test
public void indexList() {
List<Item> list = new ArrayList<>();
list.add(new Item(1L, "小米手机7", "手机", "小米", 3299.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(2L, "坚果手机R1", "手机", "锤子", 3699.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(3L, "华为META10", "手机", "华为", 4499.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(4L, "小米Mix2S", "手机", "小米", 4299.00, "http://image.leyou.com/13123.jpg"));
list.add(new Item(5L, "荣耀V10", "手机", "华为", 2799.00, "http://image.leyou.com/13123.jpg"));
// 接收对象集合,实现批量新增
itemRepository.saveAll(list);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
不需要写实现类,然后我们直接去运行:
@Test
public void testFindBy(){
List<Item> list = itemRepository.findByPriceBetween(2000d, 4000d);
for (Item item : list) {
System.out.println("item = "+item);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
结果:
5.5.3.自定义查询
先来看最基本的match query:
@Test
public void search(){
// 构建查询条件
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 添加基本分词查询
queryBuilder.withQuery(QueryBuilders.matchQuery("title", "小米手机"));
// 搜索,获取结果
Page<Item> items = this.itemRepository.search(queryBuilder.build());
// 总条数
long total = items.getTotalElements();
System.out.println("total = " + total);
for (Item item : items) {
System.out.println(item);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
-
NativeSearchQueryBuilder:Spring提供的一个查询条件构建器,帮助构建json格式的请求体
-
QueryBuilders.matchQuery(“title”, “小米手机”):利用QueryBuilders来生成一个查询。QueryBuilders提供了大量的静态方法,用于生成各种不同类型的查询:
-
Page<item>
:默认是分页查询,因此返回的是一个分页的结果对象,包含属性:-
totalElements:总条数
-
totalPages:总页数
-
Iterator:迭代器,本身实现了Iterator接口,因此可直接迭代得到当前页的数据
-
其它属性:
-
结果:
5.5.4.分页查询
利用NativeSearchQueryBuilder
可以方便的实现分页:
@Test public void searchByPage(){ // 构建查询条件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 添加基本分词查询 queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机")); // 分页: int page = 0; int size = 2; queryBuilder.withPageable(PageRequest.of(page,size));
<span class="token comment">// 搜索,获取结果</span> Page<span class="token generics function"><span class="token punctuation"><</span>Item<span class="token punctuation">></span></span> items <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>itemRepository<span class="token punctuation">.</span><span class="token function">search</span><span class="token punctuation">(</span>queryBuilder<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 总条数</span> <span class="token keyword">long</span> total <span class="token operator">=</span> items<span class="token punctuation">.</span><span class="token function">getTotalElements</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"总条数 = "</span> <span class="token operator">+</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 总页数</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"总页数 = "</span> <span class="token operator">+</span> items<span class="token punctuation">.</span><span class="token function">getTotalPages</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 当前页</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"当前页:"</span> <span class="token operator">+</span> items<span class="token punctuation">.</span><span class="token function">getNumber</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 每页大小</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"每页大小:"</span> <span class="token operator">+</span> items<span class="token punctuation">.</span><span class="token function">getSize</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Item item <span class="token operator">:</span> items<span class="token punctuation">)</span> <span class="token punctuation">{</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
结果:
可以发现,Elasticsearch中的分页是从第0页开始。
5.5.5.排序
排序也通用通过NativeSearchQueryBuilder
完成:
@Test public void searchAndSort(){ // 构建查询条件 NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 添加基本分词查询 queryBuilder.withQuery(QueryBuilders.termQuery("category", "手机"));
<span class="token comment">// 排序</span> queryBuilder<span class="token punctuation">.</span><span class="token function">withSort</span><span class="token punctuation">(</span>SortBuilders<span class="token punctuation">.</span><span class="token function">fieldSort</span><span class="token punctuation">(</span><span class="token string">"price"</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">order</span><span class="token punctuation">(</span>SortOrder<span class="token punctuation">.</span>ASC<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 搜索,获取结果</span> Page<span class="token generics function"><span class="token punctuation"><</span>Item<span class="token punctuation">></span></span> items <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>itemRepository<span class="token punctuation">.</span><span class="token function">search</span><span class="token punctuation">(</span>queryBuilder<span class="token punctuation">.</span><span class="token function">build</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 总条数</span> <span class="token keyword">long</span> total <span class="token operator">=</span> items<span class="token punctuation">.</span><span class="token function">getTotalElements</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"总条数 = "</span> <span class="token operator">+</span> total<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">for</span> <span class="token punctuation">(</span>Item item <span class="token operator">:</span> items<span class="token punctuation">)</span> <span class="token punctuation">{</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span>item<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
结果:
5.6.聚合
5.6.1.聚合为桶
桶就是分组,比如这里我们按照品牌brand进行分组:
@Test
public void testAgg(){
NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder();
// 不查询任何结果
queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null));
// 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand
queryBuilder.addAggregation(
AggregationBuilders.terms("brands").field("brand"));
// 2、查询,需要把结果强转为AggregatedPage类型
AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build());
// 3、解析
// 3.1、从结果中取出名为brands的那个聚合,
// 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型
StringTerms agg = (StringTerms) aggPage.getAggregation("brands");
// 3.2、获取桶
List<StringTerms.Bucket> buckets = agg.getBuckets();
// 3.3、遍历
for (StringTerms.Bucket bucket : buckets) {
// 3.4、获取桶中的key,即品牌名称
System.out.println(bucket.getKeyAsString());
// 3.5、获取桶中的文档数量
System.out.println(bucket.getDocCount());
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
显示的结果:
关键API:
-
AggregationBuilders
:聚合的构建工厂类。所有聚合都由这个类来构建,看看他的静态方法: -
AggregatedPage
:聚合查询的结果类。它是Page<T>
的子接口:AggregatedPage
在Page
功能的基础上,拓展了与聚合相关的功能,它其实就是对聚合结果的一种封装,大家可以对照聚合结果的JSON结构来看。而返回的结果都是Aggregation类型对象,不过根据字段类型不同,又有不同的子类表示
我们看下页面的查询的JSON结果与Java类的对照关系:
5.6.2.嵌套聚合,求平均值
代码:
@Test public void testSubAgg(){ NativeSearchQueryBuilder queryBuilder = new NativeSearchQueryBuilder(); // 不查询任何结果 queryBuilder.withSourceFilter(new FetchSourceFilter(new String[]{""}, null)); // 1、添加一个新的聚合,聚合类型为terms,聚合名称为brands,聚合字段为brand queryBuilder.addAggregation( AggregationBuilders.terms("brands").field("brand") .subAggregation(AggregationBuilders.avg("priceAvg").field("price")) // 在品牌聚合桶内进行嵌套聚合,求平均值 ); // 2、查询,需要把结果强转为AggregatedPage类型 AggregatedPage<Item> aggPage = (AggregatedPage<Item>) this.itemRepository.search(queryBuilder.build()); // 3、解析 // 3.1、从结果中取出名为brands的那个聚合, // 因为是利用String类型字段来进行的term聚合,所以结果要强转为StringTerm类型 StringTerms agg = (StringTerms) aggPage.getAggregation("brands"); // 3.2、获取桶 List<StringTerms.Bucket> buckets = agg.getBuckets(); // 3.3、遍历 for (StringTerms.Bucket bucket : buckets) { // 3.4、获取桶中的key,即品牌名称 3.5、获取桶中的文档数量 System.out.println(bucket.getKeyAsString() + ",共" + bucket.getDocCount() + "台");
<span class="token comment">// 3.6.获取子聚合结果:</span> InternalAvg avg <span class="token operator">=</span> <span class="token punctuation">(</span>InternalAvg<span class="token punctuation">)</span> bucket<span class="token punctuation">.</span><span class="token function">getAggregations</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">asMap</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token string">"priceAvg"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> System<span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"平均售价:"</span> <span class="token operator">+</span> avg<span class="token punctuation">.</span><span class="token function">getValue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
结果:
</div>
<link href="https://csdnimg.cn/release/phoenix/mdeditor/markdown_views-60ecaf1f42.css" rel="stylesheet">
<div class="more-toolbox">
<div class="left-toolbox">
<ul class="toolbox-list">
<li class="tool-item tool-active is-like "><a href="javascript:;"><svg class="icon" aria-hidden="true">
<use xlink:href="#csdnc-thumbsup"></use>
</svg><span class="name">点赞</span>
<span class="count"></span>
</a></li>
<li class="tool-item tool-active is-collection "><a href="javascript:;" data-report-click="{"mod":"popu_824"}"><svg class="icon" aria-hidden="true">
<use xlink:href="#icon-csdnc-Collection-G"></use>
</svg><span class="name">收藏</span></a></li>
<li class="tool-item tool-active is-share"><a href="javascript:;" data-report-click="{"mod":"1582594662_002"}"><svg class="icon" aria-hidden="true">
<use xlink:href="#icon-csdnc-fenxiang"></use>
</svg>分享</a></li>
<!--打赏开始-->
<!--打赏结束-->
<li class="tool-item tool-more">
<a>
<svg t="1575545411852" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5717" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M179.176 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5718"></path><path d="M509.684 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5719"></path><path d="M846.175 499.222m-113.245 0a113.245 113.245 0 1 0 226.49 0 113.245 113.245 0 1 0-226.49 0Z" p-id="5720"></path></svg>
</a>
<ul class="more-box">
<li class="item"><a class="article-report">文章举报</a></li>
</ul>
</li>
</ul>
</div>
</div>
<div class="person-messagebox">
<div class="left-message"><a href="https://blog.csdn.net/id5555">
<img src="https://profile.csdnimg.cn/8/6/D/3_id5555" class="avatar_pic" username="id5555">
<img src="https://g.csdnimg.cn/static/user-reg-year/1x/3.png" class="user-years">
</a></div>
<div class="middle-message">
<div class="title"><span class="tit"><a href="https://blog.csdn.net/id5555" data-report-click="{"mod":"popu_379"}" target="_blank">smallmartial</a></span>
</div>
<div class="text"><span>发布了94 篇原创文章</span> · <span>获赞 8</span> · <span>访问量 1万+</span></div>
</div>
<div class="right-message">
<a href="https://im.csdn.net/im/main.html?userName=id5555" target="_blank" class="btn btn-sm btn-red-hollow bt-button personal-letter">私信
</a>
<a class="btn btn-sm bt-button personal-watch" data-report-click="{"mod":"popu_379"}">关注</a>
</div>
</div>
</div>
</article>