BOOT 整合 ElasticSearch
Posted 小影~
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BOOT 整合 ElasticSearch相关的知识,希望对你有一定的参考价值。
整合前先理解几个概念 与关键字
- 索引<Index> 一组相似文档的集合
一个索引就是一个拥有几分相似特征的文档的集合。比如说,你可以有一个商品数据的索引,一个订单数据的索引,还有一个用户数据的索引。一个索引由一个名字来标识(必须全部是小写字母的),并且当我们要对这个索引中的文档进行索引、搜索、更新和删除的时候,都要使用到这个名字。
- 映射<Mapping> 用来定义索引存储文档的结构:字段、类型等
映射是定义一个文档和它所包含的字段如何被存储和索引的过程。在默认配置下,ES可以根据插入的数据自动地创建mapping,也可以手动创建mapping。 mapping中主要包括字段名、字段类型等
-
文档<Document> 索引中一条记录,可以被索引的最小单元
文档是索引中存储的一条条数据。一条文档是一个可被索引的最小单元。ES中的文档采用了轻量级的JSON格式数据来表示。
-
分片<shards>
Elasticsearch提供了将索引划分成多份的能力,这些份就叫做分片。当你创建一个索引的时候,你可以指定你想要的分片的数量。每个分片本身也是一个功能完善并且独立的“索引”,这个“索引”可以被放置 到集群中的任何节点上。
- 复制<replicas>
Index的分片中一份或多份副本。
关键字对比
ElasticSearch |
对比 |
must |
相当于mysql的and |
should |
相当于MySQL的or |
must_not |
相当于MySQL的not不等于 |
gt | 大于 |
git | 大于等于 |
lt | 小于 |
lte | 小于等于 |
match |
分词查询 |
term |
完全匹配 |
fuzzy | 误拼写查询 |
range | 范围查询 |
wildcard | 模糊查询 |
exists | 排除null |
match_phrase | 短语匹配 |
match_phrase和match区别 | match是分词匹配(将输入的词进行分词),match_phrase是短语匹配(输入文本进行查找) |
ES存储类型:
文本类型 - text
在Elasticsearch 5.4 版本开始, text取代了需要分词的string, 当一个字段需要用于全文搜索(会被分词), 比如产品名称、产品描述信息, 就应该使用text类型.
text的内容会被分词, 可以设置是否需要存储: “index”: “true|false” 。text类型的字段不能用于排序, 也很少用于聚合.
PUT website
"mappings":
"blog":
"properties":
"summary": "type": "text", "index": "true"
关键字类型 - keyword
在Elasticsearch 5.4 版本开始, keyword取代了不需要分词的string.—— 当一个字段需要按照精确值进行过滤、排序、聚合等操作时, 就应该使用keyword类型.
keyword的内容不会被分词, 可以设置是否需要存储: “index”: “true|false”.
PUT website
"mappings":
"blog":
"properties":
"tags": "type": "keyword", "index": "true"
数字类型 - 8种
类型 |
说明 |
byte |
有符号的8位整数, 范围: [-128 ~ 127] |
short |
有符号的16位整数, 范围: [-32768 ~ 32767] |
integer |
有符号的32位整数, 范围: [−231 ~ 231-1] |
long |
有符号的64位整数, 范围: [−263 ~ 263-1] |
float |
32位单精度浮点数 |
double |
64位双精度浮点数 |
half_float |
16位半精度IEEE 754浮点类型 |
scaled_float |
缩放类型的的浮点数, 比如price字段只需精确到分, 57.34缩放因子为100, 存储结果为5734 |
使用注意事项:
尽可能选择范围小的数据类型, 字段的长度越短, 索引和搜索的效率越高;优先考虑使用带缩放因子的浮点类型.
PUT shop
"mappings":
"book":
"properties":
"name": "type": "text",
"quantity": "type": "integer", // integer类型
"price":
"type": "scaled_float", // scaled_float类型
"scaling_factor": 100
日期类型 - date
JSON没有日期数据类型, 所以在ES中, 日期可以是:包含格式化日期的字符串, “2018-10-01”, 或"2018/10/01 12:10:30"
代表时间毫秒数的长整型数字。代表时间秒数的整数.
如果时区未指定, 日期将被转换为UTC格式, 但存储的却是长整型的毫秒值.
可以自定义日期格式, 若未指定, 则使用默认格式: strict_date_optional_time||epoch_millis
// 添加映射
PUT website
"mappings":
"blog":
"properties":
"pub_date": "type": "date" // 日期类型
// 添加数据
PUT website/blog/11
"pub_date": "2018-10-10"
PUT website/blog/12
"pub_date": "2018-10-10T12:00:00Z" // Solr中默认使用的日期格式
PUT website/blog/13
"pub_date": "1589584930103" // 时间的毫秒值
(2) 多种日期格式:
多个格式使用双竖线||分隔, 每个格式都会被依次尝试, 直到找到匹配的.第一个格式用于将时间毫秒值转换为对应格式的字符串.
/ 添加映射
PUT website
"mappings":
"blog":
"properties":
"date":
"type": "date", // 可以接受如下类型的格式
"format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"
布尔类型 - boolean
可以接受表示真、假的字符串或数字:
真值: true, “true”, “on”, “yes”, “1”…
假值: false, “false”, “off”, “no”, “0”, “”(空字符串), 0.0, 0
二进制型 - binary
二进制类型是Base64编码字符串的二进制值, 不以默认的方式存储, 且不能被搜索. 有2个设置项:
(1) doc_values: 该字段是否需要存储到磁盘上, 方便以后用来排序、聚合或脚本查询. 接受true和false(默认);
(2) store: 该字段的值是否要和_source分开存储、检索, 意思是除了_source中, 是否要单独再存储一份. 接受true或false(默认).
// 添加映射
PUT website
"mappings":
"blog":
"properties":
"blob": "type": "binary" // 二进制
// 添加数据
PUT website/blog/1
"title": "Some binary blog",
"blob": "hED903KSrA084fRiD5JLgY=="
注意: Base64编码的二进制值不能嵌入换行符\\n, 逗号(0x2c)等符号.
范围类型 - range
range过滤允许我们按照制定范围查找一批数据
复杂数据类型Array
ES中没有专门的数组类型, 直接使用[]定义即可;
数组中所有的值必须是同一种数据类型, 不支持混合数据类型的数组:
① 字符串数组: [“one”, “two”]; ② 整数数组: [1, 2]; ③ 由数组组成的数组: [1, [2, 3]], 等价于[1, 2, 3]; ④ 对象数组: [“name”: “Tom”, “age”: 20, “name”: “Jerry”, “age”: 18].
注意:
- 动态添加数据时, 数组中第一个值的类型决定整个数组的类型;
- 不支持混合数组类型, 比如[1, “abc”];
- 数组可以包含null值, 空数组[]会被当做missing field —— 没有值的字段.
复杂数据类型Object
JSON文档是分层的: 文档可以包含内部对象, 内部对象也可以包含内部对象.
复杂数据类型嵌套类型 - nested
嵌套类型是对象数据类型的一个特例, 可以让array类型的对象被独立索引和搜索.
专门数据类型IP类型
IP类型的字段用于存储IPv4或IPv6的地址, 本质上是一个长整型字段.
添加映射
PUT employee
"mappings":
"customer":
"properties":
"ip_addr": "type": "ip"
添加数据
PUT employee/customer/1
"ip_addr": "192.168.1.1"
计数数据类型 - token_counttoken_count类型用于统计字符串中的单词数量.
添加映射
PUT employee
"mappings":
"customer":
"properties":
"name":
"type": "text",
"fields":
"length":
"type": "token_count",
"analyzer": "standard"
添加数据:
PUT employee/customer/1
"name": "John Snow"
PUT employee/customer/2
"name": "Tyrion Lannister"
查询数据
GET employee/customer/_search
"query":
"term": "name.length": 2
整合SpringBoot
整合前熟悉几个重要的对象与构造器
SearchRequest | 查询请求对象 |
SearchResponse | 查询响应对象 |
SearchSourceBuilder | 查询源构建器 |
MatchQueryBuilder | 匹配查询构建器 |
BoolQueryBuilder |
布尔查询构建器 |
TermQueryBuilder |
词条查想构建器 |
QueryBuilders |
查询构建器工厂 |
AggregationBuilders |
聚合构建器工厂 |
TremsAggregationBuilder |
词条聚合构造器 |
Aggregations |
分组结果封装 |
Terms.Bucket |
桶 |
Spring boot中主要有Java REST Client
、spring-data-elasticsearch
两种方式可以整合ES 本文使用spring-data-elasticsearch
<!--es-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
</dependency>
配置文件
@Configuration
public class RestClientConfig extends AbstractElasticsearchConfiguration
@Override
@Bean
public RestHighLevelClient elasticsearchClient()
final ClientConfiguration clientConfiguration = ClientConfiguration.builder()
.connectedTo("localhost:9200")
.build();
return RestClients.create(clientConfiguration).rest();
创建索引 并 映射 & 删除索引
put /products #products索引名称
"mappings":
"properties":
"title": // 字段名称
"type": "keyword" // 类型
,
"price": // 字段名称
"type": "double" // 类型
,
"created_time": // 字段名称
"type": "date" // 类型
,
"description": // 字段名称
"type": "text", // 类型
"analyzer": "ik_max_word" // 开启分词器
#删除索引
DELETE /products
Boot
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 创建索引 创建映射
*/
@Test
void indexAndMapping() throws IOException
CreateIndexRequest createIndexRequest = new CreateIndexRequest("products");
createIndexRequest.mapping("\\n" +
" \\"properties\\": \\n" +
" \\"title\\":\\n" +
" \\"type\\": \\"keyword\\"\\n" +
" ,\\n" +
" \\"price\\":\\n" +
" \\"type\\": \\"double\\"\\n" +
" ,\\n" +
" \\"created_time\\":\\n" +
" \\"type\\": \\"date\\"\\n" +
" ,\\n" +
" \\"description\\":\\n" +
" \\"type\\": \\"text\\",\\n" +
" \\"analyzer\\":\\"ik_max_word\\"\\n" +
" \\n" +
" \\n" +
" ", XContentType.JSON);//指定映射参数1:指定映射json结构 参数2:指定数据类型
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(createIndexRequest, RequestOptions.DEFAULT);
System.out.println("创建状态: "+createIndexResponse.isAcknowledged());
restHighLevelClient.close();//关闭资源
/**
* 删除索引
*/
@Test
public void testDeleteIndex() throws IOException
//参数 1: 删除索引对象 参数 2:请求配置对象
AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(new DeleteIndexRequest("products"), RequestOptions.DEFAULT);
System.out.println(acknowledgedResponse.isAcknowledged());
添加 & 修改 数据
/**
* @author 小影
* @create 2022-05-09 9:51
* @describe:商品
*/
@Data
public class Product
private Integer id;
// 标题
private String title;
// 价格
private Double price;
// 创建时间
private LocalDateTime createTime;
// 描述
private String description;
/**
* 添加数据(文档)
* @throws IOException
*/
@Test
void testCreate() throws IOException
// param:索引名称
IndexRequest indexRequest = new IndexRequest("products");
// 要存储数据
Product product = new Product();
product.setId(1);
product.setTitle("蜜汁小汉堡");
product.setCreateTime(LocalDateTime.now());
product.setPrice(3.0);
product.setDescription("老八秘制小汉堡,好吃又管饱");
indexRequest.id(product.getId().toString());// 存储ID,不设置默认随机字符串
indexRequest.source(JSONUtil.toJsonStr(product), XContentType.JSON);//指定文档数据
//参数 1: 索引请求对象 参数 2:请求配置对象
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse.status());// CREATED:成功
修改和添加是一样的,传入的参数中的ID在es中存在即为修改,不同的是修改成功indexResponse.status()返回的 是OK,如果是添加返回的是CREATE
删除数据
/**
* 删除数据(文档)
*/
@Test
public void testDelete() throws IOException
//参数 1: 删除请求对象 参数 2: 请求配置对象
DeleteRequest deleteRequest = new DeleteRequest("products", "5W25poAB9vN9HF984Etg");
DeleteResponse deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println(deleteResponse.status());// 成功返回OK
根据ID查询数据
/**
* 基于id 查询数据(文档)
*/
@Test
public void testQueryById() throws IOException
GetRequest getRequest = new GetRequest("products", "1");
//参数 1: 查询请求对象 参数 2:请求配置对象 返回值: 查询响应对象
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
System.out.println("id: "+getResponse.getId());
System.out.println("source: "+getResponse.getSourceAsString());
查询所有数据
/**
* 查询所有数据(文档)
* 查询所有matchAllQuery
*/
@Test
public void testMatchAll() throws IOException
SearchRequest searchRequest = new SearchRequest("products"); //指定搜索索引
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象
sourceBuilder.query(QueryBuilders.matchAllQuery());//查询所有
searchRequest.source(sourceBuilder);//指定查询条件
SearchResponse searchResponse = restHighLevelClient.search(searchRequest,RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象
System.out.println("总条数: " + searchResponse.getHits().getTotalHits().value);
System.out.println("最大得分: " + searchResponse.getHits().getMaxScore());
//获取结果
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits)
String id = hit.getId();
System.out.println("id: "+ id +" source: "+hit.getSourceAsString());
不同条件查询(关键字、范围、前缀、通配符、多个ID、多个字段)
注意:es只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。
分词器下载地址,切记要与es版本一致:Releases · medcl/elasticsearch-analysis-ik · GitHub
/**
* 不同条件查询 term(关键词查询)
*/
@Test
public void testQuery() throws IOException
//1.term 关键词 只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。
//query(QueryBuilders.termQuery("description","小汉堡"));
//2.range 范围 价格在0-6之间的数据
//query(QueryBuilders.rangeQuery("price").gt(0).lte(6));
//3.prefix 前缀
//query(QueryBuilders.prefixQuery("title","s蜜汁"));
//4.wildcard 通配符查询 ? 一个字符 * 任意多个字符
//query(QueryBuilders.wildcardQuery("title","*小汉堡*"));
//5.ids 多个指定 id 查询
//query(QueryBuilders.idsQuery().addIds("1").addIds("2"));
//6.multi_match 多字段查询
query(QueryBuilders.multiMatchQuery("豆腐","title","description"));
public void query(QueryBuilder queryBuilder) throws IOException
SearchRequest searchRequest = new SearchRequest("products");//指定搜索索引
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象
sourceBuilder.query(queryBuilder);//指定查询条件
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象
System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);
System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits)
System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());
分页、排序、指定返回字段、高亮结果
/**
* 查询所有
* 分页查询 form 起始位置默认0开始 size 每页展示记录数
* 排序 sort
* 返回指定的字段 fetchSource 用来指定查询文档返回那些字段
* 高亮结果 highlighter
*/
@Test
public void testSearch() throws IOException
SearchRequest searchRequest = new SearchRequest("products");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
//创建高亮器
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder.requireFieldMatch(false).field("description").field("title").preTags("<span style=\'color:red;\'>").postTags("</span>");
sourceBuilder.query(QueryBuilders.termQuery("description","简简单单"))
.from(0) //起始位置 start = (page-1)*size
.size(10)//每页显示条数 默认返回 10条
.sort("price", SortOrder.ASC)//指定排序字段 参数 1: 根据哪个字段排序 参数 2:排序方式 [asc,desc]
.fetchSource(new String[],new String[]"create_time")//参数 1: 包含字段数组 参数 2:排除字段数组
.highlighter(highlightBuilder);//高亮搜索结果
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);
System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits)
System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());
//获取高亮字段
Map<String, HighlightField> highlightFields = hit.getHighlightFields();
if (highlightFields.containsKey("description"))
System.out.println("description高亮结果: "+highlightFields.get("description").fragments()[0]);
if (highlightFields.containsKey("title"))
System.out.println("title高亮结果: "+highlightFields.get("title").fragments()[0]);
过滤查询
过滤查询<filter query>,其实准确来说,ES中的查询操作分为2种: 查询(query)和过滤(filter)。查询即是之前提到的query查询,它 (查询)默认会计算每个返回文档的得分,然后根据得分排序。而过滤(filter)只会筛选出符合的文档,并不计算 得分,而且它可以缓存文档 。所以,单从性能考虑,过滤比查询更快。 换句话说过滤适合在大范围筛选数据,而查询则适合精确匹配数据。一般应用时, 应先使用过滤操作过滤数据, 然后使用查询匹配数据。
/**
* query : 查询精确查询 查询计算文档得分 并根据文档得分进行返回
* filter query : 过滤查询 用来在大量数据中筛选出本地查询相关数据 不会计算文档得分 经常使用 filter query 结果进行缓存
* 注意: 一旦使用 query 和 filterQuery es 优先执行 filter Query 然后再执行 query 所以使用filter query 查询会比较快
*/
@Test
public void testFilterQuery() throws IOException
SearchRequest searchRequest = new SearchRequest("products");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder
.query(QueryBuilders.termQuery("description","老八"))
.postFilter(QueryBuilders.idsQuery().addIds("1").addIds("2").addIds("3"));//用来指定过滤条件
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
System.out.println("符合条件总数: "+searchResponse.getHits().getTotalHits().value);
System.out.println("获取文档最大分数: "+searchResponse.getHits().getMaxScore());
SearchHit[] hits = searchResponse.getHits().getHits();
for (SearchHit hit : hits)
System.out.println("id: "+hit.getId()+" source: "+hit.getSourceAsString());
聚合查询 max(ParsedMax)、 min(ParsedMin) 、sum(ParsedSum)、 avg(ParsedAvg)
es语法:
先根据某个字段分组 此案例中根据价格分组
GET /products/_search
"query":
"term":
"title":
"value": "老八米饭"
,
"aggs":
"price_gooup":
"terms":
"field": "price"
之后就可以操作
求最大值
GET /products/_search
"aggs":
"price_max":
"max":
"field": "price"
求最小值
GET /products/_search
"aggs":
"price_min":
"min":
"field": "price"
求平均值
GET /products/_search
"aggs":
"price_avg":
"avg":
"field": "price"
....
/**
* max(ParsedMax) min(ParsedMin) sum(ParsedSum) avg(ParsedAvg) 聚合函数 桶中只有一个返回值
*/
@Test
public void testAggsFunction() throws IOException
SearchRequest searchRequest = new SearchRequest("products");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
sourceBuilder
.query(QueryBuilders.matchAllQuery()) //查询条件
//.aggregation(AggregationBuilders.sum("price_sum").field("price"))//用来设置聚合处理 sum
//.aggregation(AggregationBuilders.avg("price_avg").field("price")) //用来设置聚合处理 avg
.aggregation(AggregationBuilders.max("price_min").field("price")) //max
.size(0);//默认查询所有进行聚合
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
Aggregations aggregations = searchResponse.getAggregations();
ParsedMax parsedMax = aggregations.get("price_min");
System.out.println(parsedMax.getValue());
nested嵌套对象 关联查询
需求:通过传递一系列评论来存储博客文章及其所有评论。数据结构如下:
"title": "牛人集结地",
"body": "随便评论",
"comments": [
"name": "张三",
"age": 33,
"rating": 6,
"comment": "找我牛逼",
"commented_on": "22 Nov 2022"
,
"name": "老八",
"age": 33,
"rating": 9,
"comment": "吃俩柠檬整点蒜,简简单单一顿饭,美食界里我老八,今天给你们吃沙拉",
"commented_on": "25 Nov 2022"
,
"name": "吕小布",
"age": 35,
"rating": 6,
"comment": "踏遍青楼人未老,请用汇仁肾宝",
"commented_on": "21 Nov 2022"
]
#创建索引
PUT /blog_new
"mappings":
"properties":
"title":
"type":"text"
,
"body":
"type":"text"
,
"comments":
"type":"nested",
"properties":
"name":
"type": "text"
,
"comment":
"type": "text"
,
"age":
"type": "short"
,
"rating":
"type": "short"
,
"commented_on":
"type": "text"
/**
* 添加关联信息
*/
@Test
public void addParent() throws IOException
IndexRequest indexRequest = new IndexRequest("blog_new");
String id = UUID.randomUUID().toString();
Blog blog = new Blog();
blog.setBody("随便评论");
blog.setTitle("牛人集结地");
List<Comments> list = new ArrayList<>();
Comments comments1 = new Comments();
comments1.setName("张三");
comments1.setAge(33);
comments1.setRating(6);
comments1.setComment("找我牛逼");
comments1.setCommented_on("22 Nov 2022");
Comments comments2 = new Comments();
comments2.setName("老八");
comments2.setAge(33);
comments2.setRating(9);
comments2.setComment("吃俩柠檬整点蒜,简简单单一顿饭,美食界里我老八,今天给你们吃沙拉");
comments2.setCommented_on("25 Nov 2022");
Comments comments3 = new Comments();
comments3.setName("吕小布");
comments3.setAge(35);
comments3.setRating(6);
comments3.setComment("踏遍青楼人未老,请用汇仁肾宝");
comments3.setCommented_on("21 Nov 2022");
list.add(comments1);
list.add(comments2);
list.add(comments3);
blog.setComments(list);
String source = JSON.toJSONString(blog);
System.out.println("source = " + source);
indexRequest.id("W_DKq4ABHdN8KsLXQ2ZR");
indexRequest.source(source, XContentType.JSON);
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println(indexResponse.status());
查询:
@Test
public void testGet() throws IOException
SearchRequest request = new SearchRequest("blog_new"); // 初始化需要查询的索引
//查询student 字段下的hobby字段下的name字段的值 等于篮球的学生,这里特别注意第一层必须是你查询的字段的前一层字段
// 查询条件1 comment存在老八
QueryBuilder one = QueryBuilders.nestedQuery("comments", QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("comments.comment", "老八")), ScoreMode.Total);
// 并且年龄在33
QueryBuilder two = QueryBuilders.nestedQuery("comments", QueryBuilders.boolQuery()
.must(QueryBuilders.matchQuery("comments.age", "33")), ScoreMode.Total);
BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery(); // 初始化 最后组合成的条件
queryBuilder.must(one);
queryBuilder.must(two);
//初始化条件builer
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
//加入组合成的条件到builder
searchSourceBuilder.query(queryBuilder);
//设置查询的分页 第一个从0开始
searchSourceBuilder.from(0);
//每页的大小为2
searchSourceBuilder.size(2);
//初始化索引中加入 builder
request.source(searchSourceBuilder);
try
SearchResponse searchResponse = restHighLevelClient.search(request, RequestOptions.DEFAULT);
SearchHit[] hits = searchResponse.getHits().getHits();
//这里把数据转成实体
List<Blog> res = new ArrayList<>();
for (SearchHit hit : hits)
res.add(JSON.parseObject(hit.getSourceAsString(), Blog.class));
System.out.println(JSON.toJSONString(res));
catch (Exception e)
e.printStackTrace();
缺点:更新某个内容只能把这条数据,都替换,删除某个子对象也是
自己根据案例改的工具类
/**
* @author 小影
* @create 2022-05-09 14:54
* @describe:ElasticSearch工具类
*/
@Component
public class EsUtil
private static final Logger logger = LoggerFactory.getLogger(EsUtil.class);
@Resource
private RestHighLevelClient restHighLevelClient;
/**
* 判断一个索引是否存在
*
* @param indexName
* @return
*/
public boolean isExistsIndex(String indexName)
GetIndexRequest request = new GetIndexRequest();
try
boolean exists = restHighLevelClient.indices().exists(request.indices(indexName), RequestOptions.DEFAULT);
return exists;
catch (IOException e)
e.printStackTrace();
return false;
/**
* 删除索引
*
* @param indexName 索引名称
* @return
* @throws IOException
*/
public boolean DeleteIndex(String indexName)
//参数 1: 删除索引对象 参数 2:请求配置对象
AcknowledgedResponse acknowledgedResponse = null;
try
acknowledgedResponse = restHighLevelClient.indices().delete(new DeleteIndexRequest(indexName), RequestOptions.DEFAULT);
catch (IOException e)
e.printStackTrace();
return acknowledgedResponse.isAcknowledged() == true ? true : false;
/**
* 添加数据 & 修改数据
*
* @param indexName 索引名称
* @param id 数据id
* @param dataJson 要存储的数据JSON格式
* @return ID在es库中不存在即为添加, 存在修改
*/
public boolean createData(String indexName, String id, String dataJson)
try
// param:索引名称
IndexRequest indexRequest = new IndexRequest(indexName);
indexRequest.id(id);// 存储ID,不设置默认随机字符串
indexRequest.source(dataJson, XContentType.JSON);//指定文档数据
//参数 1: 索引请求对象 参数 2:请求配置对象
IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.err.println(indexResponse);
System.out.println(indexResponse.status());
if (indexResponse.status() == RestStatus.CREATED || indexResponse.status() == RestStatus.OK)
return true;
catch (IOException e)
e.printStackTrace();
return false;
/**
* 根据ID删除数据
*
* @param indexName 索引名称
* @param id ID
* @return
*/
public boolean deleteDataById(String indexName, String id)
DeleteResponse deleteResponse = null;
try
DeleteRequest deleteRequest = new DeleteRequest(indexName, id);
deleteResponse = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
catch (IOException e)
e.printStackTrace();
return deleteResponse.status() == RestStatus.OK;
/**
* 根据ID查询数据(文档)
*
* @param indexName
* @param id
* @return
*/
public String queryById(String indexName, String id)
try
GetRequest getRequest = new GetRequest(indexName, id);
//参数 1: 查询请求对象 参数 2:请求配置对象 返回值: 查询响应对象
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
return getResponse.getSourceAsString();
catch (IOException e)
e.printStackTrace();
return null;
/**
* 查询所有:查询、分页
*
* @param indexName 索引名称
* @param pageNo 页码
* @param pageSize 页大小
* @return
*/
public SearchHit[] queryAll(String indexName, Integer pageNo, Integer pageSize)
SearchResponse searchResponse = null;//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象
try
SearchRequest searchRequest = new SearchRequest(indexName); //指定搜索索引
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象
sourceBuilder.query(QueryBuilders.matchAllQuery())//查询所有
.from((pageNo - 1) * pageSize)
.size(pageSize);
searchRequest.source(sourceBuilder);//指定查询条件
searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
catch (IOException e)
e.printStackTrace();
return searchResponse.getHits().getHits();
/**
* 调用通用查询借鉴
*
* @throws IOException
*/
public void testQuery() throws IOException
//1.term 关键词 只能匹配单字符 不能搜索词语的问题 ES默认分词规则不能支持中文,通过安装IK Analysis for Elasticsearch支持中文分词。
//query(QueryBuilders.termQuery("description","小汉堡"));
//2.range 范围 价格在0-6之间的数据
//query(QueryBuilders.rangeQuery("price").gt(0).lte(6));
//3.prefix 前缀
//query(QueryBuilders.prefixQuery("title","s蜜汁"));
//4.wildcard 通配符查询 ? 一个字符 * 任意多个字符
//query(QueryBuilders.wildcardQuery("title","*小汉堡*"));
//5.ids 多个指定 id 查询
//query(QueryBuilders.idsQuery().addIds("1").addIds("2"));
//6.multi_match 多字段查询
query(QueryBuilders.multiMatchQuery("豆腐", "title", "description"));
/**
* 通用查询
*
* @param queryBuilder
* @throws IOException
*/
public SearchHit[] query(QueryBuilder queryBuilder) throws IOException
SearchRequest searchRequest = new SearchRequest("products");//指定搜索索引
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();//指定条件对象
sourceBuilder.query(queryBuilder);//指定查询条件
searchRequest.source(sourceBuilder);
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);//参数 1:搜索请求对象 参数2: 请求配置对象 返回值:查询结果对象
SearchHit[] hits = searchResponse.getHits().getHits();
return hits;
这是小编在开发学习使用和总结, 这中间或许也存在着不足,希望可以得到大家的理解和建议。如有侵权联系小编!
以上是关于BOOT 整合 ElasticSearch的主要内容,如果未能解决你的问题,请参考以下文章
Elasticsearch学习 spring boot整合Elasticsearch的原生方式
Spring Boot整合ElasticSearch和Mysql 附案例源码