3.6.2 search查询
ElasticsearchRepository
除了给我们提供了CRUD
和分页排序功能以外,还给我们提供了支持Elasticsearch API的search
方法:
@NoRepositoryBean
public interface ElasticsearchRepository<T, ID> extends ElasticsearchCrudRepository<T, ID> {
//...
Iterable<T> search(QueryBuilder query);
Page<T> search(QueryBuilder query, Pageable pageable);
Page<T> search(SearchQuery searchQuery);
//...
}
org.springframework.data.elasticsearch.core.query.SearchQuery
可使用org.elasticsearch.index.query.QueryBuilder
构造查询条件,所以三个search
方法是类似的。如下面的验证代码:
@Bean
CommandLineRunner queryBuilderAndSearchQuery(PersonRepository personRepository){
return args -> {
QueryBuilder queryBuilder = QueryBuilders.matchAllQuery(); //1
SearchQuery searchQuery = new NativeSearchQueryBuilder() //2
.withQuery(queryBuilder) // 3
.withPageable(PageRequest.of(0, 3, Sort.by("age"))) //4
.build(); //5
Page<Person> personPage1 = personRepository.search(queryBuilder, PageRequest.of(0, 3, Sort.by("age"))); //6
Page<Person> personPage2 = personRepository.search(searchQuery); //7
System.out.println("personPage1总数为: " + personPage1.getTotalElements()
+ " 总页数为:" + personPage1.getTotalPages());
personPage1.forEach(System.out::println);
System.out.println("personPage2总数为: " + personPage2.getTotalElements()
+ " 总页数为:" + personPage2.getTotalPages());
personPage2.forEach(System.out::println);
};
}
-
QueryBuilders
有大量的静态方法构造QueryBuilder
matchAllQuery()
:查询所有的数据,返回值是QueryBuilder
的子类MatchAllQueryBuilder
;matchQuery(String name, Object text)
:匹配属性值,返回值是QueryBuilder
的子类MatchQueryBuilder
;fuzzyQuery(String name, String value)
:模糊匹配属性值,返回值是QueryBuilder
的子类FuzzyQueryBuilder
;rangeQuery(String name)
:范围查询,返回值是QueryBuilder
的子类RangeQueryBuilder
。
-
通过
NativeSearchQueryBuilder
构造SearchQuery
; -
使用
withQuery(QueryBuilder queryBuilder)
,通过QueryBuilder
设置查询; -
设置分页和排序信息;
-
建造
SearchQuery
; -
使用
Page<T> search(QueryBuilder query, Pageable pageable)
方法查询; -
使用
Page<T> search(SearchQuery searchQuery)
方法查询,两次查询的结果是一致的。
使用SearchQuery
更友好易读性更强,我们再演示几个查询的例子:
@Bean
CommandLineRunner search(PersonRepository personRepository){
return args -> {
SearchQuery queryByAgeRangeAndNameRegex = new NativeSearchQueryBuilder()
.withQuery(rangeQuery("age").from(30).to(35)) // 1
.withFilter(matchQuery("name", "wyf")) // 2
.build();
SearchQuery fuzzyQuery = new NativeSearchQueryBuilder()
.withQuery(fuzzyQuery("address.city", "shang").fuzziness(Fuzziness.AUTO)) //3
.build();
SearchQuery anotherFuzzyQuery = new NativeSearchQueryBuilder()
.withQuery(matchQuery("address.city", "jing")
.fuzziness(Fuzziness.AUTO))//4
.build();
Page<Person> personPage1 = personRepository.search(queryByAgeRangeAndNameRegex);
Page<Person> personPage2 = personRepository.search(fuzzyQuery);
Page<Person> personPage3 = personRepository.search(anotherFuzzyQuery);
personPage1.forEach(System.out::println);
System.out.println("--------------");
personPage2.forEach(System.out::println);
System.out.println("--------------");
personPage3.forEach(System.out::println);
System.out.println("--------------");
};
}
- 构造范围查询,
form
和to
是RangeQueryBuilder
的方法; withFilter(QueryBuilder filterBuilder)
的入参也为QueryBuilder
;- 构造模糊查询,
fuzziness
是FuzzyQueryBuilder
的方法; - 使用匹配查询构造模糊查询,
fuzziness
是MatchQueryBuilder
的方法。
3.6.3 统计查询
我们还可以使用SearchQuery
构造聚合查询统计,如总数、最大值、最小值、平均值、统计信息等;我们需要通过ElasticsearchOperations
(由AbstractElasticsearchConfiguration
提供的Bean)来执行查询统计。
@Bean
CommandLineRunner aggregateQuery(ElasticsearchOperations elasticsearchOperations){
return args -> {
SearchQuery aggregateQuery = new NativeSearchQueryBuilder().withIndices("person") //1
.addAggregation(AggregationBuilders.sum("sumAge").field("age")) //2
.addAggregation(AggregationBuilders.max("maxAge").field("age"))
.addAggregation(AggregationBuilders.min("minAge").field("age"))
.addAggregation(AggregationBuilders.count("countAge").field("age"))
.addAggregation(AggregationBuilders.avg("avgAge").field("age"))
.addAggregation(AggregationBuilders.stats("ageInfo").field("age")) //3
.addAggregation(AggregationBuilders.range("ageRange")
.field("age")
.addRange(30,35)) //4
.addAggregation(AggregationBuilders.filter("leftPerson",
rangeQuery("age").from(30).to(35))
.subAggregation(AggregationBuilders.sum("leftSum").field("age"))) //5
.build();
Aggregations aggs = elasticsearchOperations.query(aggregateQuery, searchResponse -> {
Aggregations aggregations = searchResponse.getAggregations();
return aggregations;
}); //6
double sum = ((ParsedSum)aggs.get("sumAge")).getValue(); //7
System.out.println("sum age is " + sum);
double max = ((ParsedMax)aggs.get("maxAge")).getValue();
System.out.println("max age is " + max);
double min = ((ParsedMin)aggs.get("minAge")).getValue();
System.out.println("min age is " + min);
double count = ((ParsedValueCount)aggs.get("countAge")).getValue();
System.out.println("count is " + count);
double avg = ((ParsedAvg)aggs.get("avgAge")).getValue();
System.out.println("avg age is " + avg);
ParsedStats stats = ((ParsedStats)aggs.get("ageInfo")); //8
System.out.println("stats sum age is " + stats.getSumAsString());
System.out.println("stats max age is " + stats.getMaxAsString());
System.out.println("stats min age is " + stats.getMinAsString());
System.out.println("stats avg age is " + stats.getAvgAsString());
System.out.println("stats count is " + stats.getCount());
((ParsedRange)aggs.get("ageRange")).getBuckets().forEach(bucket -> { //9
System.out.println(bucket.getFromAsString()
+ "到" + bucket.getToAsString()
+ "数量为: " + bucket.getDocCount());
});
Aggregations filterAggs = ((ParsedFilter)aggs.get("leftPerson")).getAggregations();//10
double leftSum = ((ParsedSum)filterAggs.get("leftSum")).getValue();
System.out.println("left sum is " + leftSum);
};
}
-
通过
withIndices
方法指定索引名称,若不调用此方法会统计整个Elasticsearch所有属性为age
的索引。 -
通过
addAggregation
添加聚合统计;使用AggregationBuilders
的静态方法来构造聚合统计,如sum()
、max()
、min()
、avg()
、count()
等;静态方法接受的参数是聚合统计的名称;通过field
方法设置需要统计的属性。 -
构造统计查询,
stats()
方法包含的信息有sum、max、min、avg、count; -
使用
range()
构造范围统计; -
先用
filter
方法过滤人员,再通过subAggregation
构造子聚合来统计剩下来的人员的岁数总和; -
通过
ElasticsearchOperations
的query(SearchQuery query, ResultsExtractor<T> resultsExtractor)
方法执行查询,聚合是通过ResultsExtractor
函数接口获得:public interface ResultsExtractor<T> { T extract(SearchResponse response); }
-
不同的聚合有不同的类型,可用
Aggregations
的get("聚合名称")
来获得聚合并强制转换成ParsedSum
、ParsedMax
、ParsedMin
、ParsedValueCount
、ParsedAvg
等,再使用用getValue()
方法获取实际的值。 -
ParsedStats
包含了sum、max、min、avg、count; -
ParsedRange
可通过Bucket
的getDocCount()
方法获得符合范围的数量; -
我们可以从
ParsedFilter
聚合里获得它的子聚合ParsedSum
。
新书推荐:
我的新书《从企业级开发到云原生微服务:Spring Boot 实战》已出版,内容涵盖了丰富Spring Boot开发的相关知识
购买地址:https://item.jd.com/12760084.html
主要包含目录有:
第一章 初识Spring Boot(快速领略Spring Boot的美丽)
第二章 开发必备工具(对常用开发工具进行介绍:包含IntelliJ IDEA、Gradle、Lombok、Docker等)
第三章 函数式编程
第四章 Spring 5.x基础(以Spring 5.2.x为基础)
第五章 深入Spring Boot(以Spring Boot 2.2.x为基础)
第六章 Spring Web MVC
第七章 数据访问(包含Spring Data JPA、Spring Data Elasticsearch和数据缓存)
第八章 安全控制(包含Spring Security和OAuth2)
第九章 响应式编程(包含Project Reactor、Spring WebFlux、Reactive NoSQL、R2DBC、Reactive Spring Security)
第十章 事件驱动(包含JMS、RabbitMQ、Kafka、Websocket、RSocket)
第11章 系统集成和批处理(包含Spring Integration和Spring Batch)
第12章 Spring Cloud与微服务
第13章 Kubernetes与微服务(包含Kubernetes、Helm、Jenkins、Istio)
多谢大家支持。