一、非聚合复杂查询(这儿展示了非聚合复杂查询的常用流程)

  1. 查询条件QueryBuilder的构建方法
  • 1.1 精确查询(必须完全匹配上,相当于SQL语句中的“=”)

① 单个匹配 termQuery
//不分词查询 参数1: 字段名,参数2:字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termQuery("fieldName", "fieldlValue");
//分词查询,采用默认的分词器
QueryBuilder queryBuilder2 = QueryBuilders.matchQuery("fieldName", "fieldlValue");

② 多个匹配
//不分词查询,参数1: 字段名,参数2:多个字段查询值,因为不分词,所以汉字只能查询一个字,英语是一个单词.
QueryBuilder queryBuilder=QueryBuilders.termsQuery("fieldName", "fieldlValue1","fieldlValue2...");
//分词查询,采用默认的分词器
QueryBuilder queryBuilder= QueryBuilders.multiMatchQuery("fieldlValue", "fieldName1", "fieldName2", "fieldName3");
//匹配所有文件,相当于就没有设置查询条件
QueryBuilder queryBuilder=QueryBuilders.matchAllQuery();

  • 1.2 模糊查询(只要包含即可,相当于SQL语句中的“LIKE”)

① 常用的字符串查询
//左右模糊
QueryBuilders.queryStringQuery("fieldValue").field("fieldName");

② 常用的用于推荐相似内容的查询
//如果不指定filedName,则默认全部,常用在相似内容的推荐上
QueryBuilders.moreLikeThisQuery(new String[] {"fieldName"}).addLikeText("pipeidhua");
③ 前缀查询 如果字段没分词,就匹配整个字段前缀
QueryBuilders.prefixQuery("fieldName","fieldValue");
④fuzzy query:分词模糊查询,通过增加fuzziness模糊属性来查询,如能够匹配hotelName为tel前或后加一个字母的文档,fuzziness 的含义是检索的
term 前后增加或减少n个单词的匹配查询
QueryBuilders.fuzzyQuery("hotelName", "tel").fuzziness(Fuzziness.ONE);
⑤ wildcard query:通配符查询,支持* 任意字符串;?任意一个字符
//前面是fieldname,后面是带匹配字符的字符串
QueryBuilders.wildcardQuery("fieldName","ctr*");
QueryBuilders.wildcardQuery("fieldName","c?r?");

  • 1.3 范围查询

① 闭区间查询
QueryBuilder queryBuilder0 = QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2");
② 开区间查询
//默认是true,也就是包含
QueryBuilder queryBuilder1 = QueryBuilders.rangeQuery("fieldName").from("fieldValue1").to("fieldValue2")
.includeUpper(false).includeLower(false);
③ 大于
QueryBuilder queryBuilder2 = QueryBuilders.rangeQuery("fieldName").gt("fieldValue");
④ 大于等于
QueryBuilder queryBuilder3 = QueryBuilders.rangeQuery("fieldName").gte("fieldValue");
⑤ 小于
QueryBuilder queryBuilder4 = QueryBuilders.rangeQuery("fieldName").lt("fieldValue");
⑥ 小于等于
QueryBuilder queryBuilder5 = QueryBuilders.rangeQuery("fieldName").lte("fieldValue");

  • 1.4 组合查询/多条件查询/布尔查询

QueryBuilders.boolQuery()
QueryBuilders.boolQuery().must();//文档必须完全匹配条件,相当于and
QueryBuilders.boolQuery().mustNot();//文档必须不匹配条件,相当于not
QueryBuilders.boolQuery().should();//至少满足一个条件,这个文档就符合should,相当于or

二、聚合查询
  ① 【概念】

Elasticsearch有一个功能叫做 聚合(aggregations) ,它允许你在数据上生成复杂的分析统计。它很像SQL中的 GROUP BY 但是功能更强大。
【注】 更好的理解概念,参考 https://blog.csdn.net/dm_vincent/article/details/42387161
Buckets(桶):满足某个条件的文档集合。
Metrics(指标):为某个桶中的文档计算得到的统计信息。
就是这样!每个聚合只是简单地由一个或者多个桶,零个或者多个指标组合而成。
通俗的讲可以粗略转换为SQL:select count(name) from table group by name
以上的COUNT(name)就相当于一个指标。GROUP BY name 则相当于一个桶。
桶和SQL中的组(Grouping)拥有相似的概念,而指标则与COUNT(),SUM(),MAX()等函数相似。
1、桶(Buckets):一个桶就是满足特定条件的一个文档集合:
一名员工要么属于男性桶,或者女性桶。
城市Albany属于New York州这个桶。
日期2014-10-28属于十月份这个桶。
随着聚合被执行,每份文档中的值会被计算来决定它们是否匹配了桶的条件。如果匹配成功,那么该文档会被置入该桶中,同时聚合会继续执行。
桶也能够嵌套在其它桶中,能让你完成层次或者条件划分这些需求。比如,Cincinnati可以被放置在Ohio州这个桶中,而整个Ohio州则能够被放置在美国这个桶中。
ES中有很多类型的桶,让你可以将文档通过多种方式进行划分(按小时,按最流行的词条,按年龄区间,按地理位置,以及更多)。但是从根本上,它们都根据相同的原理运作:按照条件对文档进行划分。

2、指标(Metrics):桶能够让我们对文档进行有意义的划分,但是最终我们还是需要对每个桶中的文档进行某种指标计算。分桶是达到最终目的的手段:提供了对文档进行划分的方法,从而让你能够计算需要的指标。多数指标仅仅是简单的数学运算(比如,min,mean,max以及sum),它们使用文档中的值进行计算。在实际应用中,指标能够让你计算例如平均薪资,最高出售价格,或者百分之95的查询延迟。

3、聚合查询就是将两者结合起来,一个聚合就是一些桶和指标的组合。一个聚合可以只有一个桶,或者一个指标,或者每样一个。在桶中甚至可以有多个嵌套的桶。比如,我们可以将文档按照其所属国家进行分桶,然后对每个桶计算其平均薪资(一个指标)。因为桶是可以嵌套的,我们能够实现一个更加复杂的聚合操作:
将文档按照国家进行分桶。(桶)
然后将每个国家的桶再按照性别分桶。(桶)
然后将每个性别的桶按照年龄区间进行分桶。(桶)
最后,为每个年龄区间计算平均薪资。(指标)

  ② 聚合查询都是使用AggregationBuilders工具类创建,创建的聚合查询如下:

(1)统计某个字段的数量
ValueCountBuilder vcb= AggregationBuilders.count("count_uid").field("uid");
(2)去重统计某个字段的数量(有少量误差)
CardinalityBuilder cb= AggregationBuilders.cardinality("distinct_count_uid").field("uid");
(3)聚合过滤
FilterAggregationBuilder fab= AggregationBuilders.filter("uid_filter").filter(QueryBuilders.queryStringQuery("uid:001"));
(4)按某个字段分组
TermsBuilder tb= AggregationBuilders.terms("group_name").field("name");
(5)求和
SumBuilder sumBuilder= AggregationBuilders.sum("sum_price").field("price");
(6)求平均
AvgBuilder ab= AggregationBuilders.avg("avg_price").field("price");
(7)求最大值
MaxBuilder mb= AggregationBuilders.max("max_price").field("price");
(8)求最小值
MinBuilder min= AggregationBuilders.min("min_price").field("price");
(9)按日期间隔分组
DateHistogramBuilder dhb= AggregationBuilders.dateHistogram("dh").field("date");
(10)获取聚合里面的结果
TopHitsBuilder thb= AggregationBuilders.topHits("top_result");
(11)嵌套的聚合
NestedBuilder nb= AggregationBuilders.nested("negsted_path").path("quests");
(12)反转嵌套
AggregationBuilders.reverseNested("res_negsted").path("kps ");

三、项目中的具体例子

  • 以下例子实现的功能是:使用es查询对数据进行分组然后求和统计,并且倒序排序

  ① 实体类:Project

 package com.adc.da.budget.entity;

 import com.adc.da.base.entity.BaseEntity;
import com.adc.da.budget.annotation.MatchField;
import com.adc.da.excel.annotation.Excel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Getter;
import lombok.Setter;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;
import org.springframework.format.annotation.DateTimeFormat; import java.text.Collator;
import java.util.Date;
import java.util.List;
import java.util.Map; @Getter
@Setter
@Document(indexName = "financial_prd", type = "project")
public class Project extends BaseEntity implements Comparable<Project> { public int compareTo(Project o) {
return Collator.getInstance(java.util.Locale.CHINA).compare(this.getName(), o.getName());
} // matchField
// 项目名称,业务方,人力投入,项目描述,所属业务,创建时间,项目负责人,人员
//@Excel(name = "项目ID", orderNum = "1")
@Id
private String id; @Excel(name = "项目名称", orderNum = "")
@MatchField("项目名称")
//@Field
private String name; //项目负责人ID
@MatchField(value = "项目负责人", checkId = true)
private String projectLeaderId; @Excel(name = "项目负责人", orderNum = "")
@MatchField(value = "项目负责人")
private String projectLeader; private String deptId; @Excel(name = "项目组成员", orderNum = "")
@MatchField("人员")
private String projectMemberNames; private String[] memberNames; @MatchField(value = "人员", checkId = true)
private String[] projectMemberIds; // @Excel(name = "所属业务ID", orderNum = "5")
@MatchField("所属业务ID")
private String budgetId; /**
* 经营类 (OA 项目编号)
*/
private String budgetDomainId; /**
* 经营类 (OA 项目编号)
*/
private String contractNoDomainId; @Excel(name = "所属业务", orderNum = "")
@MatchField("所属业务")
private String budget; //@Excel(name = "业务类型ID", orderNum = "7")
@MatchField("业务类型ID")
private String businessId; @Excel(name = "业务类型", orderNum = "")
@MatchField("业务类型")
private String business; @Excel(name = "业务方", orderNum = "")
@MatchField("业务方")
private String projectOwner; @Excel(name = "创建时间", orderNum = "", exportFormat = "yyyy-MM-dd HH:mm:ss", importFormat = "yyyy-MM-dd HH:mm:ss")
@MatchField("创建时间")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date startTime; @Excel(name = "项目开始时间", orderNum = "", exportFormat = "yyyy-MM-dd HH:mm:ss", importFormat = "yyyy-MM-dd HH:mm:ss")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date projectStartTime; @Excel(name = "项目完成状态", orderNum = "")
private String finishedStatus; @Excel(name = "人力投入(人/天)", orderNum = "")
@MatchField("人力投入")
@Field(type = FieldType.Integer)
private Integer personInput; //项目组成员 name 和 id
private List<Map<String, String>> mapList; private List<Map<String,String>> userIdDeptNameMapList; private Date createTime; private Date modifyTime; //业务 n:1
//质量考核 1:1
private GualityInspection gualityInspection; //任务 1:n
private List<Task> raskList; //收入费用 1:n
private List<RevenueExpense> revenueExpenseList; // 支出费用 1:n
private List<ExpensesIncurred> expensesIncurredList; @MatchField("项目描述")
private String projectDescription; @Field(type = FieldType.String, analyzer = "not_analyzed")
private String createUserId; private String createUserName; //删除标记
private Boolean delFlag; @Field(type = FieldType.String, analyzer = "not_analyzed")
private String pm; @Field(type = FieldType.String, analyzer = "not_analyzed")
/*
* 项目所属业务所在部门
*/
private String projectTeam; @MatchField(value = "合同编号")
private String contractNo; //业务创建人字段
@Field(type = FieldType.String, analyzer = "not_analyzed")
private String businessCreateUserId; //提供给前段判断是否能点状态按钮 0表示进行中
private String btnFlag; private String approveUserId; @ApiModelProperty("是否具有修改权限")
private Boolean manager; /**
* 合同合计,转化为float型,存在失真,所以原始数据用 ,该字段仅应用于范围筛选
*
* @see #contractAmountStr
*/
@Field(type = FieldType.Float)
private float contractAmount; /**
* 保证 表单中的数据精度
* 项目搜索中 这个字段表示累计投入工时
*/
private String contractAmountStr;
/**
* 开票金额
*/
private List<ProjectContractInvoiceEO> projectContractInvoiceEOList; /**
* 开始时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date projectBeginTime; /**
* 结束时间
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date projectEndTime; /** 0. 经营类项目 , 1.日常类事务项目 , 2. 科研类项目*/
private int projectType; private int projectYear; private int projectMonth; /**
* 商务经理id
*
*/
private String businessManagerId; /**
* 商务经理姓名
*
*/
private String businessManagerName; private String businessAdminId ; private String businessAdminName ; private String projectAdminId ; @Field(type = FieldType.String, analyzer = "not_analyzed")
private String projectAdminName ; @Field(type = FieldType.String, analyzer = "not_analyzed")
private String province; //乙方
@Field(type = FieldType.String, analyzer = "not_analyzed")
private String partyB; }

  ② 实现方法

 //根据业务方分组查询其合同金额并且倒序
public List<Project> getProjectContractAmount(){ /**本人觉得使用BoolQueryBuilder工具类也是可以的**/
/* BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
boolQueryBuilder.mustNot(QueryBuilders.termQuery(DEL_FLAG, true)); boolQueryBuilder.must(QueryBuilders.termQuery("projectYear",projectYear));
boolQueryBuilder.must(QueryBuilders.termQuery("projectType",0)); TermsBuilder projectOwnerAgg = AggregationBuilders.terms("projectOwner").order(Terms.Order.aggregation("contractAmount", false));
SumBuilder contractAmountAgg = AggregationBuilders.sum("contractAmount").field("contractAmount"); TermsBuilder aa = projectOwnerAgg.subAggregation(contractAmountAgg);*/ /**业务查询可以不用管Start**/
String[] property = new String[1];
property[0] = "0";
List<BudgetEO> budgetEOList = budgetEODao.findAllBudgetNameNotLike("旧-%", property);
//按中文首字母排序
Collections.sort(budgetEOList);
List<String> budgetIds = new ArrayList<>();
for (BudgetEO budgetEO : budgetEOList) {
budgetIds.add(budgetEO.getId());
} Integer projectYear = Integer.parseInt(new SimpleDateFormat("yyyy").format(new Date()));
/**业务查询可以不用管end**/ //创建查询工具类,然后按照条件查询,可以分开也可以合在一起写
QueryBuilder queryBuilder = QueryBuilders.boolQuery()
.mustNot(QueryBuilders.termQuery(DEL_FLAG, true))
.must(QueryBuilders.termQuery("projectYear",projectYear))
.must(QueryBuilders.termQuery(PROJECT_TYPE,0))
.must(QueryBuilders.termsQuery(BUDGET_ID, budgetIds)); //构建查询
NativeSearchQueryBuilder nativeSearchQueryBuilder = new NativeSearchQueryBuilder().withQuery(queryBuilder); /*
//这里的作用是查询后显示指定的字段和不显示的字段设置 String[] fileds = {"projectOwner","contractAmount"};
nativeSearchQueryBuilder.withFields(fileds);
String[] filterFileds = {"不需要显示的字段"}
nativeSearchQueryBuilder.withSourceFilter(new FetchSourceFilter(fileds,filterFileds)); */ /**
*创建聚合函数:本例子是以 projectOwner公司名称分组然后计算其contractAmount合同金额总和并且按照合同金额倒序排序
* 相当于SQL语句 select projectOwner,sum(contractAmount) as contractAmount from peoject group by projectOwner order by contractAmount desc
*/
TermsBuilder projectOwnerAgg =
AggregationBuilders.terms("projectOwner").field("projectOwner").
order(Terms.Order.aggregation("contractAmount",false));
SumBuilder contractAmountAgg = AggregationBuilders.sum("contractAmount").field("contractAmount");
nativeSearchQueryBuilder.addAggregation(projectOwnerAgg.subAggregation(contractAmountAgg));
SearchQuery searchQuery = nativeSearchQueryBuilder.build(); /***
* 执行查询 参考网上有三种方式
*/
/**
* ① 通过reporitory执行查询,获得有Page包装了的结果集
* //Page<Project> projects = projectRepository.search(searchQuery);
*/ /**
* ② 通过elasticSearch模板elasticsearchTemplate.queryForList方法查询
* List<Project> projects = elasticsearchTemplate.queryForList(searchQuery, Project.class);
*/ /**
* ③ 通过elasticSearch模板elasticsearchTemplate.query()方法查询,获得聚合(常用)
*/
Aggregations aggregations = elasticsearchTemplate.query(searchQuery, new ResultsExtractor<Aggregations>() {
@Override
public Aggregations extract(SearchResponse searchResponse) {
return searchResponse.getAggregations();
}
}); /**
* 对查询结果处理
*/
//转换成map集合
Map<String, Aggregation> aggregationMap = aggregations.asMap();
//获得对应的聚合函数的聚合子类,该聚合子类也是个map集合,里面的value就是桶Bucket,我们要获得Bucket
StringTerms stringTerms = (StringTerms)aggregationMap.get("projectOwner");
//获得所有的桶
List<Terms.Bucket> buckets = stringTerms.getBuckets(); /* 一 、使用Iterator遍历桶
//将集合转换成迭代器遍历桶,当然如果你不删除buckets中的元素,直接foreach遍历就可以了
Iterator<Terms.Bucket> iterator = buckets.iterator();
List<String> projectOwnerList = new ArrayList<>();
while (iterator.hasNext()){
//bucket桶也是一个map对象,我们取它的key值就可以了
String projectOwner = iterator.next().getKeyAsString();
//根据projectOwner去结果中查询即可对应的文档,添加存储数据的集合
projectOwnerList.add(projectOwner);
}*/ //② 使用 foreach遍历就可以了
List<Project> projects = new ArrayList<>();
for (Terms.Bucket bucket : buckets){
Project project = new Project();
String projectOwner = bucket.getKeyAsString();
//得到所有子聚合
Map<String, Aggregation> contractAmountSumbuilder = bucket.getAggregations().asMap();
//sum值获取方法 如果是avg,那么这里就是Avg格式
Sum contractAmounts = (Sum)contractAmountSumbuilder.get("contractAmount");
double contractAmount = contractAmounts.getValue(); project.setProjectOwner(projectOwner);
project.setContractAmount((float) contractAmount);
projects.add(project); } return projects;
}

四、参考博文地址

https://www.cnblogs.com/xionggeclub/p/7975982.html

https://blog.csdn.net/qq_40885085/article/details/105024625

https://my.oschina.net/guozs/blog/716802

https://blog.csdn.net/topdandan/article/details/81436141

https://blog.csdn.net/justlpf/article/details/88105489

https://blog.csdn.net/topdandan/article/details/81436141

ElasticSearch的高级复杂查询:非聚合查询和聚合查询的更多相关文章

  1. LINQ学习系列-----3.1 查询非泛型集合

    一.问题起源 LINQ to object在设计时,是配合IEnumerable<T>接口的泛型集合类型使用的,例如字典.数组.List<T>等,但是对于继承了IEnumera ...

  2. LINQ学习系列-----3.1 查询非泛型集合和多个分组

    一.查询非泛型集合 1.问题起源 LINQ to object在设计时,是配合IEnumerable<T>接口的泛型集合类型使用的,例如字典.数组.List<T>等,但是对于继 ...

  3. 031 Spring Data Elasticsearch学习笔记---重点掌握第5节高级查询和第6节聚合部分

    Elasticsearch提供的Java客户端有一些不太方便的地方: 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的 需要自己把对象序列化为json存储 查询到结果也需要自己 ...

  4. Elasticsearch去重查询/过滤重复数据(聚合)

    带家好,我是马儿,这次来讲一下最近遇到的一个问题 我司某个环境的es中被导入了重复数据,导致查询的时候会出现一些重复数据,所以要我们几个开发想一些解决方案,我们聊了聊,相出了下面一些方案: 1.从源头 ...

  5. 白日梦的Elasticsearch实战笔记,ES账号免费借用、32个查询案例、15个聚合案例、7个查询优化技巧。

    目录 一.导读 二.福利:账号借用 三._search api 搜索api 3.1.什么是query string search? 3.2.什么是query dsl? 3.3.干货!32个查询案例! ...

  6. 白日梦的Elasticsearch实战笔记,32个查询案例、15个聚合案例、7个查询优化技巧。

    目录 一.导读 三._search api 搜索api 3.1.什么是query string search? 3.2.什么是query dsl? 3.3.干货!32个查询案例! 四.聚合分析 4.1 ...

  7. Elasticsearch通关教程(五):如何通过SQL查询Elasticsearch

    前言 这篇博文本来是想放在全系列的大概第五.六篇的时候再讲的,毕竟查询是在索引创建.索引文档数据生成和一些基本概念介绍完之后才需要的.当前面的一些知识概念全都讲解完之后再讲解查询是最好的,但是最近公司 ...

  8. java操作elasticsearch实现前缀查询、wildcard、fuzzy模糊查询、ids查询

    1.前缀查询(prefix) //prefix前缀查询 @Test public void test15() throws UnknownHostException { //1.指定es集群 clus ...

  9. Python学习---ORM查询之基于对象的正向/反向/聚合/分组/Q/F查询

    ORM查询之基于对象的正向查询与反向查询 对象形式的查询 # 正向查询 ret1=models.Book.objects.first() print(ret1.title) print(ret1.pr ...

随机推荐

  1. .Net Core2.2 使用 AutoMapper进行实体转换

    一.遇到的问题 在. Core Api 的编写中,我们经常会对一些功能点进行新增编辑操作,同时我们有时也会进行查询,但是我们查询的表的数据与我们返回的数据相差甚大,这是我们有需要自己手动进行类型的转换 ...

  2. Mob 之 短信验证集成 SMSSDK

    开相关发中总会遇到短信验证这些操作,这周没有来得及写新的东西,借此分享一篇以前学习短信验证的笔记,本文使用的是 Mob 提供的 SMSSDK . 下载 SMSSDK 官网下载地址:SMSSDK 集成 ...

  3. linux压缩及归档

    一.解析 压缩:把大文件,通过压缩成一个比之前小的文件. 归档(打包):把多个文件,归档成一个文件. 二.压缩 1.zip(归档压缩,可以压缩目录,要保存源文件) 压缩:zip  压缩后的文件名 压缩 ...

  4. D 楼房重建

    时间限制 : - MS   空间限制 : - KB  评测说明 : 1s,256m 问题描述 小A的楼房外有一大片施工工地,工地上有N栋待建的楼房.每天,这片工地上的房子拆了又建.建了又拆.他经常无聊 ...

  5. C++STL(二)——vector容器

    STL--vector容器 vector对象的概念 vector基本操作 vector对象的初始化.赋值 vector查找.替换(已在上一片 string类 博客总结过了,不再总结) vector添加 ...

  6. Integer类的进制之间转换的方法

    一.两个通用方法 1.public static String toString(int i,int radix) (1)作用 将十进制的数转化成指定进制数的字符串形式:radix参数指进制数: (2 ...

  7. dis反汇编查看实现

    dis库是python(默认的CPython)自带的一个库,可以用来分析字节码 >>> import dis >>> def add(a, b = 0): ... ...

  8. Pytest系列(9) - 参数化@pytest.mark.parametrize

    如果你还想从头学起Pytest,可以看看这个系列的文章哦! https://www.cnblogs.com/poloyy/category/1690628.html 前言 pytest允许在多个级别启 ...

  9. Pyspider的基本使用

    Pyspider的基本使用 pyspider的任务流程: 每个pyspider的项目对应一个Python的脚本,该脚本中定义了一个Handler类,它有一个on_start方法.爬取首先调用on_st ...

  10. Java第三十二天,IO操作(续集),读写缓冲区的使用

    缓冲区出现原因: Java对IO的读写都是经过中间途经--JVM虚拟机进行管理的,JVM调用OS操作系统继续完成IO操作.如此一来,整个过程耗费了不小的资源,为了尽量减少这样重复的周期性操作,出现了缓 ...