ElasticSearch是一个基于Lucene的搜索服务器,其实就是对Lucene进行封装,提供了 REST API 的操作接口。ElasticSearch作为一个高度可拓展的开源全文搜索和分析引擎,可用于快速地对大数据进行存储,搜索和分析。
ElasticSearch主要特点:分布式、高可用、异步写入、多API、面向文档 。
ElasticSearch核心概念:近实时,集群,节点(保存数据),索引,分片(将索引分片),副本(分片可设置多个副本) 。它可以快速地储存、搜索和分析海量数据。
核心概念介绍
索引(index)
索引是一组相似文档的集合,可以理解一个索引就是一个目录,在这个目录下有众多文档;在新版的ES(6.0以上)中,已经去除了types的概念,所以也可以理解为索引就是一张关系型数据库的表(table);在新建索引时,索引名称必须是小写;es的精髓就是为了提高查询效率。
类型 (type)
在旧版本中,type可以理解为关系型数据库的表,其实在设计初期,是没有types的概念的,设计es的人为了和关系型数据库进行关联,特地加上了types的概念, 但是后来发现没有这个必要,所以在6.*版本进行弱化,也就说在6.*版本以后,一个索引下只能有一个type,而在7.*以后则去除了type的概念。
并且在存储的时候,一个索引下的所有type的数据都会存储在一个文件中,如果一个索引下有多个types,那无疑会降低搜索的效率,所以才会这么急切地要去除type。
document(文档)
代表着es中一条记录,和关系型数据库的rows是一样的, 不同的是,文档的数据是以json的格式进行表示;只要你的计算机磁盘空间足够,在一个索引中,你可以存储无限多的文档。
field(属性)
field 在关系型数据库中被称为字段(column),它俩之间的概念是一样的,每个field都有自己的数据类型,根据数据类型存储不同的数据,区别是关系型数据库必须先定义固定的数据类型和属性长度,而es可以不设定数据类型,在插入数据时会自动生成对应的数据类型;
mapping(映射)
mapping用来配置属性的默认值、数据类型、分析器、是否被索引等等,将一个属性用映射的方式做一些优化,可以提高检索效率和减少空间占用;
shareds (分片)
在es中, 一个索引下的数据是可以有无限大的,并且 它们都存储在一个文件中, 但是这有个问题,文件越大,就意味着搜索效率会降低,就需要将一个文件按照一定的规则拆分成多个文件;而分片就是做拆分用的,比如用户表中有10亿条数据,有各种年龄段的用户,在1 ~ 100岁之间;就可以用分片机制将 这些用户进行划分为5个片区,划分如下:
- 分片1:1-20岁
- 分片2:21-40岁
- 分片3:41-60岁
- 分片4:61-80岁
- 分片5:81-100岁
es的分片和mysql的分区类型,不同的是mysql不支持分布式分区,而es支持分布式集群分片,也就是说,es允许将不同的分片划分到不同的集群节点中;
分配(Allocation)
将分片分配给节点的过程,包括分配主分片或者副本,如果是副本,还包含从主分片复制数据的过程,这个过程由master节点完成;
副本分片的分配方式
es的数据分为主分片和副本分片,主分片都存储在master节点中,但是副本分片都在其他的node节点上, 这是因为es要保证高可用,万一master节点挂了,其他的子节点还照样可以提供查询数据的服务;
elasticsearch与JDK的版本对应
https://www.elastic.co/cn/support/matrix#matrix_jvm
SpringBoot集成elasticsearch
ES7.X版本较之前改动比较大,相应的springboot对它的支持改动也特别明显。
在spring-data-elasticsearch官网:https://docs.spring.io/spring-data/elasticsearch/docs/4.0.1.RELEASE/reference/html/#new-features
可以查看到版本匹配信息,如下:
以下演示我们使用的环境是:SpringBoot是2.3.3.RELEASE,es版本是7.10.1。
springboot-data-elasticsearch较之前最大的区别有两点:
1)配置文件
## 旧版本以spring.data.elasticsearch.开头;访问地址配置不用声明访问协议,监听es的tcp端口
spring.data.elasticsearch.cluster-nodes=localhost:9300
## 新版本以spring.elasticsearch.rest.开头;访问地址配置需要声明访问协议,直接监听es访问端口
spring.elasticsearch.rest.uris=http://localhost:9200
2)核心访问对象
旧版的核心访问对象是ElasticsearchTemplate;新版的核心访问对象是ElasticsearchRestTemplate;
另外,在新版中,不会对ElasticsearchTemplate做自动装配,如果还需要使用它,需要手动装配,当然,应该禁止这么做。如下:
@Bean(name = "elasticsearchTemplate")
public ElasticsearchTemplate initElasticsearchTemplate(Client client) {
ElasticsearchTemplate elasticsearchTemplate = new ElasticsearchTemplate(client);
return elasticsearchTemplate;
}
基本用法
引入相关依赖
<!-- elasticsearch -->
<!-- 2.1.x.RELEASE版本和2.3.3.RELEASE后的版本的自动化配置和API有些不同,我们这里直接使用新版本-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-elasticsearch</artifactId>
<version>2.3.3.RELEASE</version>
</dependency>
<!-- fastjson, 仅仅为了方便对象间的转换而已 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.1</version>
</dependency>
application.yml配置es
server:
port: 9999
# elasticsearch配置
spring:
elasticsearch:
rest:
# es访问地址,多个用英文逗号隔开
uris: http://192.168.198.155:9201,http://192.168.198.155:9202,http://192.168.198.155:9203
connection-timeout: 10 #连接超时时间,单位是s
read-timeout: 5 # 读超时时间,单位是s
核心操作对象ElasticsearchRestTemplate是自动装配的,所以接下来就可以直接访问了。
创建映射对象
和访问数据库一样,统统以对象的形式访问。
/**
* 更多注解详细说明参考官网:https://docs.spring.io/spring-data/elasticsearch/docs/4.0.1.RELEASE/reference/html/#elasticsearch.mapping.meta-model
* */
// @Document指定当前类是索引对象。indexName:索引名称;shards:创建索引时的分片数;replicas:创建索引时每个分片的备份数
@Document(indexName = "book", shards = 3, replicas = 2)
public class BookIndexMapping {
// @Id标记数据主键
@Id
private String id;
// @Field标记字段。name:映射es中的字段, type:字段类型
@Field(type = FieldType.Keyword)
private String title;
@Field(type = FieldType.Text)
private String content;
@Field(type = FieldType.Double)
private BigDecimal price;
// 如果java类型使用java.util.Date,反序列化会失败
@Field(type = FieldType.Date, format = DateFormat.date_hour_minute_second)
private LocalDateTime publishDate;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public BigDecimal getPrice() {
return price;
}
public void setPrice(BigDecimal price) {
this.price = price;
}
public LocalDateTime getPublishDate() {
return publishDate;
}
public void setPublishDate(LocalDateTime publishDate) {
this.publishDate = publishDate;
}
public BookIndexMapping() {
super();
}
}
创建索引即映射
/**
* 创建索引
*/
@Test
public void testCreateIndex() {
boolean exists = elasticsearchRestTemplate.indexOps(BookIndexMapping.class).exists();
// 如果索引已存在,删除索引
if (exists) {
// 删除索引
elasticsearchRestTemplate.indexOps(BookIndexMapping.class).delete();
}
// 创建索引
elasticsearchRestTemplate.indexOps(BookIndexMapping.class).create();
// 创建映射
Document mappings = elasticsearchRestTemplate.indexOps(BookIndexMapping.class).createMapping();
elasticsearchRestTemplate.indexOps(BookIndexMapping.class).putMapping(mappings);
System.out.println("---执行成功---");
}
新增文档数据
/**
* 新增文档数据
*/
@Test
public void testSaveDocument() {
BookIndexMapping entity = new BookIndexMapping();
entity.setTitle("高一英语(上册 人教版)");
entity.setContent("how are you! how old are you!");
entity.setPrice(new BigDecimal(23.9));
entity.setPublishDate(LocalDateTime.now());
elasticsearchRestTemplate.save(entity);
System.out.println("---插入成功---");
}
/**
* 新增文档数据:指定id
*/
@Test
public void testSaveDocumentById() {
BookIndexMapping entity = new BookIndexMapping();
entity.setId(UUID.randomUUID().toString());
entity.setTitle("大一英语(上册 人教版)");
entity.setContent("are you ok?");
entity.setPrice(new BigDecimal(13.92));
entity.setPublishDate(LocalDateTime.now());
elasticsearchRestTemplate.save(entity);
System.out.println("---插入成功---");
}
删除索引
/**
* 删除索引
*/
@Test
public void testDeleteIndex() {
boolean deleted = elasticsearchRestTemplate.indexOps(BookIndexMapping.class).delete();
System.out.println("是否删除成功 : " + deleted);
}
删除文档数据
/**
* 删除文档数据
*/
@Test
public void deleteDoc() {
// 返回被删除的数据id
String result = elasticsearchRestTemplate.delete("AEMtZoABXMkR2EYpy2Nv", BookIndexMapping.class);
System.out.println(result);
}
查询文档操作
/**
* 根据id获取文档数据
*/
@Test
public void getByIdTest() {
BookIndexMapping entity = elasticsearchRestTemplate.get("1vItZoAB7iqN_CIwy89J", BookIndexMapping.class);
System.out.println(JSON.toJSONString(entity));
}
/**
* query查询
*/
@Test
public void queryTest() {
// match查询,匹配content_字段
NativeSearchQuery query = new NativeSearchQueryBuilder().withQuery(QueryBuilders.matchQuery("content", "are")).build();
SearchHits<BookIndexMapping> list = elasticsearchRestTemplate.search(query, BookIndexMapping.class);
System.out.println(JSON.toJSONString(list));
}
/**
* filter查询
*/
@Test
public void filterTest() {
// match查询,匹配content_字段
NativeSearchQuery query = new NativeSearchQueryBuilder().withFilter(QueryBuilders.matchQuery("content_", "are")).build();
SearchHits<BookIndexMapping> list = elasticsearchRestTemplate.search(query, BookIndexMapping.class);
System.out.println(JSON.toJSONString(list));
}
@Document 和 @Field 注解
@Document
- indexName:索引名称
- shards:分片总数
- replicas:每个分片默认的备份数
@Field
- type:字段类型
- index:是否分词,默认情况下分词,一般默认分词就好,除非这个字段你确定查询时不会用到
- format:时间类型的格式化,可选的类型由DateFormat定义。
- store:默认情况下不存储原文
- analyzer:指定字段建立索引分词时指定的分词器,比如对索引库中的中国人进行分词。
- searchAnalyzer:指定字段搜索时使用的分词器,比如输入框中写中国人,然后服务器对输入框中的中国人进行分词
- ignoreFields:如果某个字段需要被忽略
- includeInParent:
type的类型是FieldType,常用的有如下可选值:
//会被分词器解析,生成倒排索引,支持模糊、精确查询,不用于排序,很少用于聚合
Text,
//不进行分词,直接索引,支持模糊、精确查询,支持聚合
Keyword,
//64位有符号整数
Long,
//32位有符号整数
Integer,
//16位有符号整数
Short,
//8位有符号整数
Byte,
//64位双精度浮点型
Double,
//32位单进度浮点型
Float,
//16位半精度浮点类型
Half_Float,
//带有缩放银子的浮点数,可以当整型看待
Scaled_Float,
//表示该字段是一个文本,日期类型
Date,
//纳秒
Date_Nanos,
//true、false
Boolean,
//会把值当做经过 base64 编码的字符串,默认不存储,且不可搜索
Binary,
//Long类型的范围
Integer_Range,
//Float的类型
Float_Range,
//Long类型的范围
Long_Range,
//Double范围
Double_Range,
//64位整数,毫秒计时
Date_Range,
//IP的范围
Ip_Range,
//一个对象中可以嵌套对象
Object,
//是object中的一个特例,可以让array类型的Object独立索引和查询。
Nested,
//ip类型的字段用于存储IPV4或者IPV6的地址
Ip,
//用于统计词频
TokenCount,
//抽取类型
Percolator,
Flattened,
Search_As_You_Type
ES内置分析器(Analyzer )
es在索引文档时,会通过各种类型 Analyzer 对text类型字段做分析,不同的 Analyzer 会有不同的分词结果,内置的分词器有以下几种,基本上内置的 Analyzer 包括 Language Analyzers 在内,对中文的分词都不够友好。中文分词需要安装其它 Analyzer。
standard
标准分析器是默认的分析器,如果没有指定,则使用该分析器。它提供了基于文法的标记化(基于 Unicode 文本分割算法) ,并且对大多数语言都有效。
simple
简单分析器将文本分解为任何非字母字符的标记,如数字、空格、连字符和撇号、放弃非字母字符,并将大写字母更改为小写字母。
whitespace
空格分析器在遇到空白字符时将文本分解为术语
stop
停止分析器与简单分析器相同,但增加了删除停止字的支持。默认使用的是 _english_ 停止词。
keyword
不分词,把整个字段当做一个整体返回
pattern
模式分析器使用正则表达式将文本拆分为术语。正则表达式应该匹配令牌分隔符,而不是令牌本身。正则表达式默认为 w+ (或所有非单词字符)。
中文分词器
1)ik_smart
ik分词器中的简单分词器,支持自定义字典,远程字典
2)ik_max_word
ik_分词器的全量分词器,支持自定义字典,远程字典
ElasticsearchRepository接口
把数据存储到es中,有两种方式:一种是 ElasticsearchRepository 接口,另一种是ElasticsearchRestTemplate类,上面基础用法都是使用ElasticsearchRestTemplate。我们接下来看看ElasticsearchRepository 接口。
我们可以通过继承 ElasticsearchRepository 来完成基本的CRUD及分页操作的,和普通的JPA没有什么区别。
1、创建映射对象和普通的pojo
@Document(indexName = "article", shards = 3, replicas = 2)
public class Article {
@Id
private String id;
@Field(type = FieldType.Text)
private String title;
@Field(type = FieldType.Nested, includeInParent = true)
private List<Author> authors;
public Article(String title) {
this.title = title;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public List<Author> getAuthors() {
return authors;
}
public void setAuthors(List<Author> authors) {
this.authors = authors;
}
}
public class Author {
private String name;
public Author(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
2、继承ElasticsearchRepository
@Repository
public interface ArticleRepository extends ElasticsearchRepository<Article,String> {
}
3、测试验证(先创建索引)
@RunWith(SpringRunner.class)
@SpringBootTest(classes = EsApp.class)
public class RepositoryEsTest {
@Autowired
private ArticleRepository articleRepository;
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 创建索引
*/
@Test
public void testCreateIndex() {
boolean exists = elasticsearchRestTemplate.indexOps(Article.class).exists();
// 如果索引已存在,删除索引
if (exists) {
// 删除索引
elasticsearchRestTemplate.indexOps(Article.class).delete();
}
// 创建索引
elasticsearchRestTemplate.indexOps(Article.class).create();
// 创建映射
Document mappings = elasticsearchRestTemplate.indexOps(Article.class).createMapping();
elasticsearchRestTemplate.indexOps(Article.class).putMapping(mappings);
System.out.println("---执行成功Article Index---");
}
/**
* 保存数据
*/
@Test
public void testSaveEsRepository() {
Article article1 = new Article("1234");
article1.setAuthors(Arrays.asList(new Author("12"), new Author("34")));
articleRepository.save(article1);
Article article2 = new Article("5678");
article2.setAuthors(Arrays.asList(new Author("56"), new Author("78")));
articleRepository.save(article2);
Article article3 = new Article("90");
article3.setAuthors(Arrays.asList(new Author("9"), new Author("9")));
articleRepository.save(article3);
}
/**
* 查询数据
*/
@Test
public void testQueryRepository(){
Iterable<Article> articles = articleRepository.findAll();
Iterator<Article> iterator = articles.iterator();
while (iterator.hasNext()){
Article article = iterator.next();
System.out.println("article:" + JSON.toJSONString(article));
}
}
}
注:ElasticsearchRepository的search方法过期了,可以使用ElasticsearchRestTemplate的search来替代。
ElasticsearchRestTemplate API
映射对象
@Document(indexName = "staff", shards = 3, replicas = 2)
public class Staff {
// @Id标记数据主键
@Id
private String id;
// @Field标记字段。name:映射es中的字段, type:字段类型
@Field(type = FieldType.Text)
private String name;
@Field(type = FieldType.Integer)
private Integer age;
@Field(type = FieldType.Double)
private BigDecimal balance;
@Field(type = FieldType.Keyword)
private String jobName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public BigDecimal getBalance() {
return balance;
}
public void setBalance(BigDecimal balance) {
this.balance = balance;
}
public String getJobName() {
return jobName;
}
public void setJobName(String jobName) {
this.jobName = jobName;
}
}
创建索引、新增测试数据
@Autowired
private ElasticsearchRestTemplate elasticsearchRestTemplate;
/**
* 创建索引
*/
@Test
public void createIndex() {
boolean exists = elasticsearchRestTemplate.indexOps(Staff.class).exists();
// 如果索引已存在,删除索引
if (exists) {
// 删除索引
elasticsearchRestTemplate.indexOps(Staff.class).delete();
}
// 创建索引
elasticsearchRestTemplate.indexOps(Staff.class).create();
// 创建映射
Document mappings = elasticsearchRestTemplate.indexOps(Staff.class).createMapping();
elasticsearchRestTemplate.indexOps(Staff.class).putMapping(mappings);
System.out.println("---执行成功 Staff ---");
}
/**
* 新增数据
*/
@Test
public void testSaveDocument() throws InterruptedException {
//随机之内的整数
Random rand = new Random();
//职位
List<String> jobList = Arrays.asList("主任", "高级专员", "中级专员", "初级专员");
List<Staff> staffList = new ArrayList();
for (int i = 14000; i < 15000; i++) {
Staff staff = new Staff();
staff.setId(String.valueOf(i));
staff.setName("小泽" + i);
staff.setAge(rand.nextInt(100));
staff.setJobName(jobList.get(rand.nextInt(4)));
staff.setBalance(new BigDecimal((Math.random() + 5d) * 1000).setScale(2, RoundingMode.HALF_DOWN));
staffList.add(staff);
if(staffList.size() >= 10){
elasticsearchRestTemplate.save(staffList);
TimeUnit.MILLISECONDS.sleep(500);
}
}
elasticsearchRestTemplate.save(staffList);
}
@Test
public void deleteIndex(){
boolean deleted = elasticsearchRestTemplate.indexOps(Staff.class).delete();
System.out.println("是否删除成功 : " + deleted);
}
分页查询
/**
* 搜索全部数据 , 分页显示 , 按 balance字段降序 排序
*/
@Test
public void testAllSearch() {
// 构建查询条件(搜索全部)
MatchAllQueryBuilder queryBuilder1 = QueryBuilders.matchAllQuery();
// 分页
Pageable pageable = PageRequest.of(0, 5);
// 排序
FieldSortBuilder balance = new FieldSortBuilder("balance").order(SortOrder.DESC);
// 执行查询
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(queryBuilder1)
.withPageable(pageable)
.withSort(balance)
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
//封装page对象
List<Staff> accounts = new ArrayList();
for (SearchHit<Staff> hit : searchHits) {
accounts.add(hit.getContent());
}
Page<Staff> page = new PageImpl(accounts, pageable, searchHits.getTotalHits());
//输出分页对象
System.out.println(page.getTotalPages());
System.out.println(page.getTotalElements());
}
条件搜索
/**
* 条件搜索
*/
@Test
public void testConditionSearch() {
// 搜索出 jobName 为 '主任' 的文档
TermQueryBuilder builder = QueryBuilders.termQuery("jobName", "主任");
// 搜索jobName包含 '专员' 模糊匹配, 因为在Staff中该字段是keyword,不会分词,所以查询不到
FuzzyQueryBuilder builder1 = QueryBuilders.fuzzyQuery("jobName", "专员");
// 搜索jobName字段为 '初级专员' 的文档
TermQueryBuilder builder2 = QueryBuilders.termQuery("jobName", "初级专员");
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(builder)
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
for (SearchHit<Staff> hit : searchHits) {
System.out.println(JSON.toJSONString(hit.getContent()));
}
}
QueryBuilders还有很多查询方法。
组合查询
/**
* 组合查询
*/
@Test
public void testAssociation() {
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// must表示同时满足,should满足其中一个,must_not表示同时不满足
boolQueryBuilder.must(QueryBuilders.matchQuery("jobName", "初级专员"));
boolQueryBuilder.must(QueryBuilders.rangeQuery("balance").gte(5800d)); //大于等于5800
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
for (SearchHit<Staff> hit : searchHits) {
System.out.println(JSON.toJSON(hit.getContent()));
}
}
过滤搜索
/**
* 过滤搜索
*/
@Test
public void testFilterQuery() {
// 构建条件
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//大于等于20000,小于等于30000
RangeQueryBuilder balance = QueryBuilders.rangeQuery("balance").gte(5700).lte(5800);
boolQueryBuilder.filter(balance);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.withQuery(boolQueryBuilder)
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
for (SearchHit<Staff> hit : searchHits) {
System.out.println(JSON.toJSONString(hit.getContent()));
}
}
聚合查询
聚合搜索,aggs,类似于group by,对jobName字段进行聚合
/**
* 聚合搜索, 对jobName字段进行聚合
*/
@Test
public void testAggregation() {
NativeSearchQuery query = new NativeSearchQueryBuilder()
.addAggregation(AggregationBuilders.terms("count").field("jobName"))
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
//取出聚合结果
Aggregations aggregations = searchHits.getAggregations();
Terms terms = (Terms) aggregations.asMap().get("count");
for (Terms.Bucket bucket : terms.getBuckets()) {
String keyAsString = bucket.getKeyAsString(); // 聚合字段列的值
long docCount = bucket.getDocCount(); // 聚合字段对应的数量
System.out.println(keyAsString + " " + docCount); //高级专员 576
}
}
嵌套聚合
统计出相同jobName的文档数量,再统计出balance的平均值,降序排序
/**
* 嵌套聚合,统计出相同jobName的文档数量,再统计出balance的平均值,降序排序
*/
@Test
public void testAggregation2() {
// 创建聚合查询条件
TermsAggregationBuilder jobNameAgg = AggregationBuilders.terms("count").field("jobName");
AvgAggregationBuilder balanceAgg = AggregationBuilders.avg("avg_balance").field("balance");
// 嵌套
jobNameAgg.subAggregation(balanceAgg);
// 按balance的平均值降序排序
jobNameAgg.order(BucketOrder.aggregation("avg_balance", false));
NativeSearchQuery build = new NativeSearchQueryBuilder()
.addAggregation(jobNameAgg)
.build();
//执行查询
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(build, Staff.class);
// 取出聚合结果
Aggregations aggregations = searchHits.getAggregations();
Terms terms = (Terms) aggregations.asMap().get("count");
for (Terms.Bucket bucket : terms.getBuckets()) {
// jobName : count : avg
ParsedAvg avg = bucket.getAggregations().get("avg_balance");
System.out.println(bucket.getKeyAsString() + " " + bucket.getDocCount() + " " + avg.getValueAsString());
}
}
范围聚合
按字段的范围进行分段聚合,按age字段[20,30],[30,40],[40,50],之后按gender统计文档个数和balance的平均值。
/**
* 按字段的范围进行分段聚合,按age字段[20,30],[30,40],[40,50],之后按jobName统计文档个数和balance的平均值
*/
@Test
public void testRangeQuery() {
// 创建聚合查询条件
RangeAggregationBuilder group_by_age =
AggregationBuilders.range("group_by_age").field("age")
.addRange(20, 30).addRange(30, 40).addRange(40, 50);
TermsAggregationBuilder count = AggregationBuilders.terms("count").field("jobName");
AvgAggregationBuilder balanceAgg = AggregationBuilders.avg("avg_balance").field("balance");
//嵌套
group_by_age.subAggregation(count);
count.subAggregation(balanceAgg);
NativeSearchQuery query = new NativeSearchQueryBuilder()
.addAggregation(group_by_age)
.build();
SearchHits<Staff> searchHits = elasticsearchRestTemplate.search(query, Staff.class);
ParsedRange parsedRange = searchHits.getAggregations().get("group_by_age");
for (Range.Bucket bucket : parsedRange.getBuckets()) {
// "key" : "20.0-30.0", "doc_count" : 451,
System.out.println(bucket.getKeyAsString() + " : " + bucket.getDocCount());
Terms group_by_gender = bucket.getAggregations().get("count");
for (Terms.Bucket genderBucket : group_by_gender.getBuckets()) {
// "key" : "M", "doc_count" : 232, "key" : "F", "doc_count" : 219,
System.out.println(genderBucket.getKeyAsString() + " : " + genderBucket.getDocCount());
ParsedAvg balanceAvg = genderBucket.getAggregations().get("avg_balance");
System.out.println(balanceAvg.getValueAsString());
}
System.out.println("-----------\n");
}
}
elasticsearch-rest-high-level-client
下面介绍下 SpringBoot 如何通过 elasticsearch-rest-high-level-client 工具操作 ElasticSearch。
为什么没有使用 Spring 家族封装的 spring-data-elasticsearch?
- 主要原因是灵活性和更新速度,Spring 将 ElasticSearch 过度封装,让开发者很难跟 ES 的 DSL 查询语句进行关联。再者就是更新速度,ES 的更新速度是非常快,但是 spring-data-elasticsearch 更新速度比较缓慢。
- 由于上面两点,所以选择了官方推出的 Java 客户端 elasticsearch-rest-high-level-client,它的代码写法跟 DSL 语句很相似,懂 ES 查询的使用其上手很快。
<!--es客户端,不使用springboot封装的客户端-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.2</version>
</dependency>
<!-- fastjson, 仅仅为了方便对象间的转换而已 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.1</version>
</dependency>
版本号对不上,为什么呢?
原来是SpringBoot对其版本也进行了管理,需要重新覆盖管理。
在spring-boot-dependencies中我们可以看到:
这里我们采用排除旧版本,重新导入对应版本号的依赖的方式解决:
<!--es客户端,不使用springboot封装的客户端-->
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.14.2</version>
<!--剔除,原因是spring-boot-dependencies中对elasticsearch的版本进行管理了, 引入的是旧版本-->
<exclusions>
<exclusion>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
</exclusion>
<exclusion>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.14.2</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.14.2</version>
</dependency>
<!-- fastjson, 仅仅为了方便对象间的转换而已 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>2.0.1</version>
</dependency>
说明:要保证elasticsearch三个依赖的版本号一致,否则启动会失败,出现各种异常,如找不到class、IGNORE_DEPRECATIONS等。
索引操作
mapping json
Java客户端
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 创建索引
*/
@Test
public void testCreateIndex() {
try {
// 创建 Mapping
XContentBuilder mapping = XContentFactory.jsonBuilder()
.startObject()
.field("dynamic", true)
.startObject("properties")
.startObject("name")
.field("type", "text")
.startObject("fields")
.startObject("keyword")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.startObject("address")
.field("type", "text")
.startObject("fields")
.startObject("keyword")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.startObject("remark")
.field("type", "text")
.startObject("fields")
.startObject("keyword")
.field("type", "keyword")
.endObject()
.endObject()
.endObject()
.startObject("age")
.field("type", "integer")
.endObject()
.startObject("salary")
.field("type", "float")
.endObject()
.startObject("birthDate")
.field("type", "date")
.field("format", "yyyy-MM-dd")
.endObject()
.startObject("createTime")
.field("type", "date")
.endObject()
.endObject()
.endObject();
// 创建索引配置信息,配置
Settings settings = Settings.builder()
.put("index.number_of_shards", 3)
.put("index.number_of_replicas", 2)
.build();
// 新建创建索引请求对象,然后设置索引类型(ES 7.0 将不存在索引类型)和 mapping 与 index 配置
CreateIndexRequest request = new CreateIndexRequest("dc_user", settings);
request.mapping("doc", mapping);
// RestHighLevelClient 执行创建索引
CreateIndexResponse createIndexResponse = restHighLevelClient.indices().create(request, RequestOptions.DEFAULT);
// 判断是否创建成功
boolean isCreated = createIndexResponse.isAcknowledged();
System.out.println("是否创建成功:" + isCreated);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除索引
*/
public void deleteIndex() {
try {
// 新建删除索引请求对象
DeleteIndexRequest request = new DeleteIndexRequest("dc_user");
// 执行删除索引
AcknowledgedResponse acknowledgedResponse = restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT);
// 判断是否删除成功
boolean siDeleted = acknowledgedResponse.isAcknowledged();
System.out.println("是否删除成功:" + siDeleted);
} catch (IOException e) {
e.printStackTrace();
}
}
mapping对应的实体对象:
public class UserInfo {
//姓名
private String name;
//年龄
private Integer age;
//地址
private String address;
//薪水
private Float salary;
//备注
private String remark;
//创建时间
private Date createTime;
//出生日期
private String birthDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public Float getSalary() {
return salary;
}
public void setSalary(Float salary) {
this.salary = salary;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public Date getCreateTime() {
return createTime;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public String getBirthDate() {
return birthDate;
}
public void setBirthDate(String birthDate) {
this.birthDate = birthDate;
}
}
文档操作
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 批量添加文档(用于测试演练)
*/
@Test
public void batchAddDocument() {
try {
Random random = new Random();
List<String> addressList = Arrays.asList("北京市丰台区", "北京市昌平区", "北京市大兴区", "北京市通州区", "北京市朝阳区");
List<String> birthDateList = Arrays.asList("1990-01-10", "1970-06-10", "1994-10-10", "1990-01-10", "1998-03-10");
for (int i = 0; i < 100; i++) {
// 创建索引请求对象
// es7废除了type, type的值都是doc, 原有的一些方法也被标记为过期了
//IndexRequest indexRequest = new IndexRequest("dc_user", "doc");
IndexRequest indexRequest = new IndexRequest("dc_user");
indexRequest.id(i + "00");
// 创建员工信息
UserInfo userInfo = new UserInfo();
userInfo.setName("张三" + i);
userInfo.setAge(random.nextInt(80) + 10);
userInfo.setSalary(random.nextFloat() + 1000f);
userInfo.setAddress(addressList.get(random.nextInt(5)));
userInfo.setRemark("来自" + userInfo.getAddress() + "的张先生");
userInfo.setCreateTime(new Date());
userInfo.setBirthDate(birthDateList.get(random.nextInt(5)));
// 将对象转换为 byte 数组
byte[] json = JSON.toJSONBytes(userInfo);
// 设置文档内容
indexRequest.source(json, XContentType.JSON);
// 执行增加文档
IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println("创建状态:" + response.status());
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 增加文档信息
*/
@Test
public void addDocument() {
try {
// 创建索引请求对象
// es7废除了type, type的值都是doc, 原有的一些方法也被标记为过期了
//IndexRequest indexRequest = new IndexRequest("dc_user", "doc");
IndexRequest indexRequest = new IndexRequest("dc_user");
indexRequest.id("1");
// 创建员工信息
UserInfo userInfo = new UserInfo();
userInfo.setName("张三");
userInfo.setAge(29);
userInfo.setSalary(100.00f);
userInfo.setAddress("北京市");
userInfo.setRemark("来自北京市的张先生");
userInfo.setCreateTime(new Date());
userInfo.setBirthDate("1990-01-10");
// 将对象转换为 byte 数组
byte[] json = JSON.toJSONBytes(userInfo);
// 设置文档内容
indexRequest.source(json, XContentType.JSON);
// 执行增加文档
IndexResponse response = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);
System.out.println("创建状态:" + response.status());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取文档信息
*/
@Test
public void getDocument() {
try {
// 获取请求对象
GetRequest getRequest = new GetRequest("dc_user", "1");
// 获取文档信息
GetResponse getResponse = restHighLevelClient.get(getRequest, RequestOptions.DEFAULT);
// 将 JSON 转换成对象
if (getResponse.isExists()) {
UserInfo userInfo = JSON.parseObject(getResponse.getSourceAsBytes(), UserInfo.class);
System.out.println("员工信息:" + JSON.toJSONString(userInfo));
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 更新文档信息
*/
@Test
public void updateDocument() {
try {
// 创建索引请求对象
UpdateRequest updateRequest = new UpdateRequest("dc_user", "1");
// 设置员工更新信息
UserInfo userInfo = new UserInfo();
userInfo.setSalary(200.00f);
userInfo.setAddress("北京市海淀区");
// 将对象转换为 byte 数组
byte[] json = JSON.toJSONBytes(userInfo);
// 设置更新文档内容
updateRequest.doc(json, XContentType.JSON);
// 执行更新文档
UpdateResponse response = restHighLevelClient.update(updateRequest, RequestOptions.DEFAULT);
System.out.println("创建状态:" + response.status());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 删除文档信息
*/
@Test
public void deleteDocument() {
try {
// 创建删除请求对象
DeleteRequest deleteRequest = new DeleteRequest("dc_user", "1");
// 执行删除文档
DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println("删除状态:" + response.status());
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据条件删除文档
*
* @throws IOException
*/
@Test
public void deleteByQueryRequest() throws IOException {
//参数为索引名,可以不指定,可以一个,可以多个
DeleteByQueryRequest request = new DeleteByQueryRequest("dc_user");
// 设置版本冲突时继续
request.setConflicts("proceed");
// 设置查询条件,第一个参数是字段名,第二个参数是字段的值
// 我们在创建索引的时候的name是text类型,是会进行分词的,不一定存在张三这个文本
// 所以我们又定义了keyword字段类型,type=keyword,来存储张三这个完整文本
request.setQuery(new TermQueryBuilder("name.keyword", "张三"));
// 更新最大文档数
request.setMaxDocs(1);
// 单批次大小
request.setBatchSize(1);
//并行, max_docs必须大于等于slices
request.setSlices(1);
// 使用滚动参数来控制“搜索上下文”保持多长时间
request.setScroll(TimeValue.timeValueMinutes(10));
// 超时
request.setTimeout(TimeValue.timeValueMinutes(2));
// 刷新索引
request.setRefresh(true);
BulkByScrollResponse response = restHighLevelClient.deleteByQuery(request, RequestOptions.DEFAULT);
System.out.println("isDeleted : " + response.getStatus().getUpdated()); //0 删除成功
}
/**
* 查询所有的文档, 并逐一迭代进行删除
*
* @throws IOException
*/
@Test
public void findAll() throws IOException {
SearchRequest searchRequest = new SearchRequest();
//指定索引
searchRequest.indices("dc_user");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// 查询所有
searchSourceBuilder.query(QueryBuilders.matchAllQuery());
//设置查询数量大小, 不设置默认是10
searchSourceBuilder.size(100);
searchRequest.source(searchSourceBuilder);
//获取到结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = searchResponse.getHits();
System.out.println("查询到的数据总数:" + hits.getTotalHits().value);
List<String> idList = new ArrayList();
Iterator<SearchHit> iterator = hits.iterator();
while (iterator.hasNext()) {
//es中的一条完整数据,包括_index、_type、_id、_score以及实际的业务数据
SearchHit hit = iterator.next();
System.out.println("实际的业务数据:" + hit.getSourceAsString());
//删除文档, 根据拿到的_id:hit.getId()
System.out.println("待删除的_id = " + hit.getId());
DeleteRequest deleteRequest = new DeleteRequest("dc_user", hit.getId());
DeleteResponse response = restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
System.out.println("删除状态:" + response.status());
}
}
精确查询(term)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 精确查询(查询条件不会进行分词,但是查询内容可能会分词,导致查询不到)
*/
@Test
public void termQuery() {
try {
// 构建查询条件(注意:termQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termQuery("address.keyword", "北京市通州区"));
//不设置查询大小, 默认是查询10条
searchSourceBuilder.size(20);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 多个内容在一个字段中进行查询
*/
@Test
public void termsQuery() {
try {
// 构建查询条件(注意:termsQuery 支持多种格式查询,如 boolean、int、double、string 等,这里使用的是 string 的查询)
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.termsQuery("address.keyword", "北京市丰台区", "北京市昌平区", "北京市大兴区"));
//不设置查询大小, 默认是查询10条
searchSourceBuilder.size(20);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
匹配查询(match)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 匹配查询符合条件的所有数据,并设置分页
*/
@Test
public void matchAllQuery() {
try {
// 构建查询条件
MatchAllQueryBuilder matchAllQueryBuilder = QueryBuilders.matchAllQuery();
// 创建查询源构造器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(matchAllQueryBuilder);
// 设置分页
searchSourceBuilder.from(0);
searchSourceBuilder.size(3);
// 设置排序
searchSourceBuilder.sort("salary", SortOrder.ASC);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 匹配查询数据
*/
@Test
public void matchQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchQuery("address", "*通州区"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 词语匹配查询
*/
@Test
public void matchPhraseQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.matchPhraseQuery("address", "北京市通州区"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 内容在多字段中进行查询
*/
@Test
public void matchMultiQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.multiMatchQuery("北京市", "address", "remark"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
模糊查询(fuzzy)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 模糊查询所有以 “三” 结尾的姓名
*/
@Test
public void fuzzyQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.fuzzyQuery("name", "三").fuzziness(Fuzziness.AUTO));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
范围查询(range)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 查询岁数 ≥ 30 岁的员工数据
*/
@Test
public void rangeQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.rangeQuery("age").gte(30));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查询距离现在 30 年间的员工数据
* [年(y)、月(M)、星期(w)、天(d)、小时(h)、分钟(m)、秒(s)]
* 例如:
* now-1h 查询一小时内范围
* now-1d 查询一天内时间范围
* now-1y 查询最近一年内的时间范围
*/
@Test
public void dateRangeQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
// includeLower(是否包含下边界)、includeUpper(是否包含上边界)
searchSourceBuilder.query(QueryBuilders.rangeQuery("birthDate")
.gte("now-30y").includeLower(true).includeUpper(true));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
通配符查询(wildcard)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 查询所有以 “三” 结尾的姓名
* <p>
* *:表示多个字符(0个或多个字符)
* ?:表示单个字符
*/
@Test
public void wildcardQuery() {
try {
// 构建查询条件
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(QueryBuilders.wildcardQuery("name.keyword", "*三?"));
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
布尔查询(bool)
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 布尔查询
*/
@Test
public void boolQuery() {
try {
// 创建 Bool 查询构建器
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
// 构建查询条件
boolQueryBuilder.must(QueryBuilders.termsQuery("address.keyword", "北京市昌平区", "北京市大兴区", "北京市房山区"))
.filter().add(QueryBuilders.rangeQuery("birthDate").format("yyyy").gte("1990").lte("1995"));
// 构建查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(boolQueryBuilder);
// 创建查询请求对象,将查询对象配置到其中
SearchRequest searchRequest = new SearchRequest("dc_user");
searchRequest.source(searchSourceBuilder);
// 执行查询,然后处理响应结果
SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
// 根据状态和数据条数验证是否返回了数据
if (RestStatus.OK.equals(searchResponse.status()) && searchResponse.getHits().getTotalHits().value > 0) {
SearchHits hits = searchResponse.getHits();
for (SearchHit hit : hits) {
// 将 JSON 转换成对象
UserInfo userInfo = JSON.parseObject(hit.getSourceAsString(), UserInfo.class);
// 输出查询信息
System.out.println(JSON.toJSONString(userInfo));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
Metric 聚合分析
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* stats 统计员工总数、员工工资最高值、员工工资最低值、员工平均工资、员工工资总和
*/
@Test
public void aggregationStats() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.stats("salary_stats").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
// 设置查询结果不返回,只返回聚合结果
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Stats 对象
ParsedStats aggregation = aggregations.get("salary_stats");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("count:" + aggregation.getCount());
System.out.println("avg:" + aggregation.getAvg());
System.out.println("max:" + aggregation.getMax());
System.out.println("min:" + aggregation.getMin());
System.out.println("sum:" + aggregation.getSum());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* min 统计员工工资最低值
*/
@Test
public void aggregationMin() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.min("salary_min").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Min 对象
ParsedMin aggregation = aggregations.get("salary_min");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("min:" + aggregation.getValue());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* max 统计员工工资最高值
*/
@Test
public void aggregationMax() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.max("salary_max").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Max 对象
ParsedMax aggregation = aggregations.get("salary_max");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("max:" + aggregation.getValue());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* avg 统计员工工资平均值
*/
@Test
public void aggregationAvg() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.avg("salary_avg").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Avg 对象
ParsedAvg aggregation = aggregations.get("salary_avg");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("avg:" + aggregation.getValue());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* sum 统计员工工资总值
*/
@Test
public void aggregationSum() {
try {
// 设置聚合条件
SumAggregationBuilder aggr = AggregationBuilders.sum("salary_sum").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Sum 对象
ParsedSum aggregation = aggregations.get("salary_sum");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("sum:" + aggregation.getValue());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* count 统计员工总数
*/
@Test
public void aggregationCount() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.count("employee_count").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 ValueCount 对象
ParsedValueCount aggregation = aggregations.get("employee_count");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
System.out.println("count:" + aggregation.getValue());
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* percentiles 统计员工工资百分位
*/
@Test
public void aggregationPercentiles() {
try {
// 设置聚合条件
AggregationBuilder aggr = AggregationBuilders.percentiles("salary_percentiles").field("salary");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.aggregation(aggr);
searchSourceBuilder.size(0);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status()) || aggregations != null) {
// 转换为 Percentiles 对象
ParsedPercentiles aggregation = aggregations.get("salary_percentiles");
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Percentile percentile : aggregation) {
System.out.println(String.format("百分位:%s:%s", percentile.getPercent(), percentile.getValue()));
}
System.out.println("-------------------------------------------");
}
// 根据具体业务逻辑返回不同结果,这里为了方便直接将返回响应对象Json串
String responseResult = response.toString();
System.out.println("responseResult : " + responseResult);
} catch (Exception e) {
e.printStackTrace();
}
}
Bucket 聚合分析
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* 按岁数进行聚合分桶
*/
@Test
public void aggrBucketTerms() {
try {
AggregationBuilder aggr = AggregationBuilders.terms("age_bucket").field("age");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(10);
searchSourceBuilder.aggregation(aggr);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Terms byCompanyAggregation = aggregations.get("age_bucket");
List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Terms.Bucket bucket : buckets) {
System.out.println(String.format("桶名:%s | 总数:%s", bucket.getKeyAsString(), bucket.getDocCount()));
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 按工资范围进行聚合分桶
*/
@Test
public void aggrBucketRange() {
try {
AggregationBuilder aggr = AggregationBuilders.range("salary_range_bucket")
.field("salary")
.addUnboundedTo("低级员工", 3000)
.addRange("中级员工", 5000, 9000)
.addUnboundedFrom("高级员工", 9000);
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
searchSourceBuilder.aggregation(aggr);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Range byCompanyAggregation = aggregations.get("salary_range_bucket");
List<? extends Range.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Range.Bucket bucket : buckets) {
System.out.println(String.format("桶名:%s | 总数:%s", bucket.getKeyAsString(), bucket.getDocCount()));
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 按照时间范围进行分桶
*/
@Test
public void aggrBucketDateRange() {
try {
AggregationBuilder aggr = AggregationBuilders.dateRange("date_range_bucket")
.field("birthDate")
.format("yyyy")
.addRange("1985-1990", "1985", "1990")
.addRange("1990-1995", "1990", "1995");
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
searchSourceBuilder.aggregation(aggr);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Range byCompanyAggregation = aggregations.get("date_range_bucket");
List<? extends Range.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Range.Bucket bucket : buckets) {
System.out.println(String.format("桶名:%s | 总数:%s", bucket.getKeyAsString(), bucket.getDocCount()));
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 按工资多少进行聚合分桶
*/
@Test
public void aggrBucketHistogram() {
try {
AggregationBuilder aggr = AggregationBuilders.histogram("salary_histogram")
.field("salary")
.extendedBounds(0, 12000)
.interval(3000);
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
searchSourceBuilder.aggregation(aggr);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Histogram byCompanyAggregation = aggregations.get("salary_histogram");
List<? extends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Histogram.Bucket bucket : buckets) {
System.out.println(String.format("桶名:%s | 总数:%s", bucket.getKeyAsString(), bucket.getDocCount()));
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 按出生日期进行分桶
*/
@Test
public void aggrBucketDateHistogram() {
try {
DateHistogramAggregationBuilder aggr = AggregationBuilders.dateHistogram("birthday_histogram").field("birthDate");
aggr.calendarInterval(DateHistogramInterval.YEAR);
//field.calendarInterval(DateHistogramInterval.days(10));
aggr.format("yyyy");
aggr.minDocCount(0);//强制返回空 buckets,既空的月份也返回
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
searchSourceBuilder.aggregation(aggr);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Histogram byCompanyAggregation = aggregations.get("birthday_histogram");
List<? extends Histogram.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Histogram.Bucket bucket : buckets) {
System.out.println(String.format("桶名:%s | 总数:%s", bucket.getKeyAsString(), bucket.getDocCount()));
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
Metric 与 Bucket 聚合分析
@Autowired
private RestHighLevelClient restHighLevelClient;
/**
* topHits 按岁数分桶、然后统计每个员工工资最高值
*/
@Test
public void aggregationTopHits() {
try {
AggregationBuilder testTop = AggregationBuilders.topHits("salary_max_user")
.size(1)
.sort("salary", SortOrder.DESC);
AggregationBuilder salaryBucket = AggregationBuilders.terms("salary_bucket")
.field("age")
.size(10);
salaryBucket.subAggregation(testTop);
// 查询源构建器
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.size(0);
searchSourceBuilder.aggregation(salaryBucket);
// 创建查询请求对象,将查询条件配置到其中
SearchRequest request = new SearchRequest("dc_user");
request.source(searchSourceBuilder);
// 执行请求
SearchResponse response = restHighLevelClient.search(request, RequestOptions.DEFAULT);
// 获取响应中的聚合信息
Aggregations aggregations = response.getAggregations();
// 输出内容
if (RestStatus.OK.equals(response.status())) {
// 分桶
Terms byCompanyAggregation = aggregations.get("salary_bucket");
List<? extends Terms.Bucket> buckets = byCompanyAggregation.getBuckets();
// 输出各个桶的内容
System.out.println("-------------------------------------------");
System.out.println("聚合信息:");
for (Terms.Bucket bucket : buckets) {
System.out.println("桶名:" + bucket.getKeyAsString());
ParsedTopHits topHits = bucket.getAggregations().get("salary_max_user");
for (SearchHit hit : topHits.getHits()) {
System.out.println(hit.getSourceAsString());
}
}
System.out.println("-------------------------------------------");
}
} catch (Exception e) {
e.printStackTrace();
}
}
QueryBuilders使用API
public class EsQueryBuildersDSL {
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* match query 单个匹配
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder matchQuery() {
return QueryBuilders.matchQuery("name", "葫芦4032娃");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* multimatch query
* 创建一个匹配查询的布尔型提供字段名称和文本。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder multiMatchQuery() {
//现住址和家乡在【山西省太原市7429街道】的人
return QueryBuilders.multiMatchQuery(
"山西省太原市7429街道", // Text you are looking for
"home", "now_home" // Fields you query on
);
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* boolean query and 条件组合查询
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder booleanQuery() {
return QueryBuilders
.boolQuery()
.must(QueryBuilders.termQuery("name", "葫芦3033娃"))
.must(QueryBuilders.termQuery("home", "山西省太原市7967街道"))
.mustNot(QueryBuilders.termQuery("isRealMen", false))
.should(QueryBuilders.termQuery("now_home", "山西省太原市"));
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* ids query
* 构造一个只会匹配的特定数据 id 的查询。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder idsQuery() {
return QueryBuilders.idsQuery().addIds("CHszwWRURyK08j01p0Mmug", "ojGrYKMEQCCPvh75lHJm3A");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* constant score query
* 另一个查询和查询,包裹查询只返回一个常数分数等于提高每个文档的查询。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder constantScoreQuery() {
/*return // Using with Filters
QueryBuilders.constantScoreQuery(FilterBuilders.termFilter("name", "kimchy"))
.boost(2.0f);*/
// With Queries
return QueryBuilders.constantScoreQuery(QueryBuilders.termQuery("name", "葫芦3033娃"))
.boost(2.0f);
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* disjunction max query
* 一个生成的子查询文件产生的联合查询,
* 而且每个分数的文件具有最高得分文件的任何子查询产生的,
* 再加上打破平手的增加任何额外的匹配的子查询。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder disMaxQuery() {
return QueryBuilders.disMaxQuery()
.add(QueryBuilders.termQuery("name", "kimchy")) // Your queries
.add(QueryBuilders.termQuery("name", "elasticsearch")) // Your queries
.boost(1.2f)
.tieBreaker(0.7f);
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* fuzzy query
* 使用模糊查询匹配文档查询。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder fuzzyQuery() {
return QueryBuilders.fuzzyQuery("name", "葫芦3582");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* matchall query
* 查询匹配所有文件。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder matchAllQuery() {
return QueryBuilders.matchAllQuery();
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* prefix query
* 包含与查询相匹配的文档指定的前缀。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder prefixQuery() {
return QueryBuilders.prefixQuery("name", "葫芦31");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* querystring query
* 查询解析查询字符串,并运行它。有两种模式,这种经营。
* 第一,当没有添加字段(使用{ @link QueryStringQueryBuilder #字段(String)},将运行查询一次,非字段前缀
* 将使用{ @link QueryStringQueryBuilder # defaultField(字符串)}。
* 第二,当一个或多个字段
* (使用{ @link QueryStringQueryBuilder #字段(字符串)}),将运行提供的解析查询字段,并结合
* 他们使用DisMax或者一个普通的布尔查询(参见{ @link QueryStringQueryBuilder # useDisMax(布尔)})。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder queryString() {
return QueryBuilders.queryStringQuery("+kimchy -elasticsearch");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* range query
* 查询相匹配的文档在一个范围。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder rangeQuery() {
return QueryBuilders
.rangeQuery("name")
.from("葫芦1000娃")
.to("葫芦3000娃")
.includeLower(true) //包括下界
.includeUpper(false); //包括上界
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* term query
* 一个查询相匹配的文件包含一个术语。。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder termQuery() {
return QueryBuilders.termQuery("name", "葫芦580娃");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* wildcard query
* 实现了通配符搜索查询。支持通配符* < /tt>,<tt>
* 匹配任何字符序列(包括空),<tt> ? < /tt>,
* 匹配任何单个的字符。注意该查询可以缓慢,因为它
* 许多方面需要遍历。为了防止WildcardQueries极其缓慢。
* 一个通配符词不应该从一个通配符* < /tt>或<tt>
* < /tt> <tt> ?。
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder wildcardQuery() {
return QueryBuilders.wildcardQuery("name", "葫芦*2娃");
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* more like this (field) query (mlt and mlt_field)
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder moreLikeThisQuery() {
// mlt Query
return QueryBuilders.moreLikeThisQuery(new String[]{"jobName", "name"}, new String[]{"高级专员"}, null)
.minTermFreq(1) // Ignore Threshold
.maxQueryTerms(12); // Max num of Terms
// in generated queries
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* span queries (first, near, not, or, term)
* 跨度查询
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder spanQueries() {
QueryBuilder queryBuilder1 = QueryBuilders.spanFirstQuery(QueryBuilders.spanTermQuery("name", "葫芦580娃"), 30000); // Max查询范围的结束位置
QueryBuilder queryBuilder2 = QueryBuilders.spanNearQuery(QueryBuilders.spanTermQuery("name", "葫芦3812娃"), 30000)
.addClause(QueryBuilders.spanNearQuery(QueryBuilders.spanTermQuery("name", "葫芦7139娃"), 10000)) // Span Term Queries
.inOrder(false);
// Span Not
QueryBuilder queryBuilder3 = QueryBuilders.spanNotQuery(
QueryBuilders.spanTermQuery("name", "葫芦580娃"),
QueryBuilders.spanTermQuery("home", "山西省太原市2552街道")
);
// Span Or
QueryBuilder queryBuilder4 = QueryBuilders.spanOrQuery(QueryBuilders.spanTermQuery("name", "葫芦580娃"))
.addClause(QueryBuilders.spanTermQuery("name", "葫芦3812娃"))
.addClause(QueryBuilders.spanTermQuery("name", "葫芦7139娃"));
// Span Term
QueryBuilder queryBuilder5 = QueryBuilders.spanTermQuery("name", "葫芦580娃");
return queryBuilder5;
}
/**
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* nested query
* 嵌套查询
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*/
public static QueryBuilder nestedQuery() {
return QueryBuilders.nestedQuery("location", // Path
QueryBuilders.boolQuery() // Your query
.must(QueryBuilders.matchQuery("location.lat", 0.962590433140581))
.must(QueryBuilders.rangeQuery("location.lon").lt(0.00000000000000000003)),
ScoreMode.Total); // max, total, avg or none
}
}
范围查询
1)数字范围
//闭区间查询
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").from(${fieldValue1}).to(${fieldValue2});
//开区间查询
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").from(${fieldValue1}, false).to(${fieldValue2}, false);
//大于
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").gt(${fieldValue});
//大于等于
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").gte(${fieldValue});
//小于
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").lt(${fieldValue});
//小于等于
QueryBuilder qb1 = QueryBuilders.rangeQuery("${fieldName}").lte(${fieldValue});
常见示例:
// 增加价格区间的检索条件
if (searchRequest.getMinPrice() != null) {
queryBuilder.must(QueryBuilders.rangeQuery(avagePriceStr)
.from(searchRequest.getMinPrice().toString()).includeLower(true)
.includeUpper(true));
}
if (searchRequest.getMaxPrice() != null) {
queryBuilder.must(QueryBuilders.rangeQuery(avagePriceStr)
.to(searchRequest.getMaxPrice().add(new BigDecimal(1)).toString())
.includeUpper(false));
}
2)时间范围
public SearchResponse getApiResponseByDetail(SearchRequestBuilder responseBuilder, String condition) {
String time1 = "2020-01-02T00:00:00.000Z";
String time2 = "2020-01-02T15:59:59.000Z";
RangeQueryBuilder rangequerybuilder = QueryBuilders
//传入时间,目标格式2020-01-02T03:17:37.638Z
.rangeQuery("@timestamp")
.from(time1).to(time2);
SearchResponse searchResponse = responseBuilder.setQuery(
QueryBuilders.boolQuery()
//must表示and
.must(rangequerybuilder) //根据时间范围查询
.must(QueryBuilders.existsQuery("api_id"))
.must(QueryBuilders.matchPhraseQuery("detail", condition))
).setExplain(true).execute().actionGet();
return searchResponse;
}
多条件查询
QueryBuilder qb1 = QueryBuilders.moreLikeThisQuery(new String[]{"${fieldName1}"}, new String[]{"${fieldValue1}"}, null);
QueryBuilder qb2 = QueryBuilders.rangeQuery("${fieldName2}").gt("${fieldValue2}");
QueryBuilder qb3 = QueryBuilders.boolQuery().must(qb1).must(qb2);
标签:SpringBoot,System,查询,elasticsearch,简单,println,new,public,out
From: https://blog.51cto.com/zhenxi/6592099