ElasticSearch的高级复杂查询:非聚合查询和聚合查询
一、非聚合复杂查询(这儿展示了非聚合复杂查询的常用流程)
- 查询条件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的高级复杂查询:非聚合查询和聚合查询的更多相关文章
- LINQ学习系列-----3.1 查询非泛型集合
一.问题起源 LINQ to object在设计时,是配合IEnumerable<T>接口的泛型集合类型使用的,例如字典.数组.List<T>等,但是对于继承了IEnumera ...
- LINQ学习系列-----3.1 查询非泛型集合和多个分组
一.查询非泛型集合 1.问题起源 LINQ to object在设计时,是配合IEnumerable<T>接口的泛型集合类型使用的,例如字典.数组.List<T>等,但是对于继 ...
- 031 Spring Data Elasticsearch学习笔记---重点掌握第5节高级查询和第6节聚合部分
Elasticsearch提供的Java客户端有一些不太方便的地方: 很多地方需要拼接Json字符串,在java中拼接字符串有多恐怖你应该懂的 需要自己把对象序列化为json存储 查询到结果也需要自己 ...
- Elasticsearch去重查询/过滤重复数据(聚合)
带家好,我是马儿,这次来讲一下最近遇到的一个问题 我司某个环境的es中被导入了重复数据,导致查询的时候会出现一些重复数据,所以要我们几个开发想一些解决方案,我们聊了聊,相出了下面一些方案: 1.从源头 ...
- 白日梦的Elasticsearch实战笔记,ES账号免费借用、32个查询案例、15个聚合案例、7个查询优化技巧。
目录 一.导读 二.福利:账号借用 三._search api 搜索api 3.1.什么是query string search? 3.2.什么是query dsl? 3.3.干货!32个查询案例! ...
- 白日梦的Elasticsearch实战笔记,32个查询案例、15个聚合案例、7个查询优化技巧。
目录 一.导读 三._search api 搜索api 3.1.什么是query string search? 3.2.什么是query dsl? 3.3.干货!32个查询案例! 四.聚合分析 4.1 ...
- Elasticsearch通关教程(五):如何通过SQL查询Elasticsearch
前言 这篇博文本来是想放在全系列的大概第五.六篇的时候再讲的,毕竟查询是在索引创建.索引文档数据生成和一些基本概念介绍完之后才需要的.当前面的一些知识概念全都讲解完之后再讲解查询是最好的,但是最近公司 ...
- java操作elasticsearch实现前缀查询、wildcard、fuzzy模糊查询、ids查询
1.前缀查询(prefix) //prefix前缀查询 @Test public void test15() throws UnknownHostException { //1.指定es集群 clus ...
- Python学习---ORM查询之基于对象的正向/反向/聚合/分组/Q/F查询
ORM查询之基于对象的正向查询与反向查询 对象形式的查询 # 正向查询 ret1=models.Book.objects.first() print(ret1.title) print(ret1.pr ...
随机推荐
- 巴什博弈 HDU-1846
描述:一堆石子有 n 个 ,两个人开始轮流取,每人最多取m个,最少取1个,最后一个将石子取完的是赢家. 思路:对于先手来说,如果有(m+1)个石子,先手取 k 个,后手就可以取 m+1-k 个,所以有 ...
- ehcahe + redis 实现多级缓存
1,了解数据存储的位置的不同 数据库:存储在磁盘上 redis:存储在内存上 ehcache:应用内缓存 缓存的目的:是为了将数据从一个较慢的介质上读取出来,放到一个较快的介质上,为了下次读取的时候更 ...
- Thread wait notify sleep
wait: 必须暂定当前正在执行的线程,并释放资源锁,让其他线程可以有机会运行 notify/notifyall: 唤醒因锁池中的线程,使之运行 wait与sleep区别 对于sleep()方法,我们 ...
- spring中BeanPostProcessor之一:InstantiationAwareBeanPostProcessor(01)
在spring中beanPostProcessor绝对是开天辟地的产物,给了程序员很多自主权,beanPostProcessor即常说的bean后置处理器. 一.概览 先来说下Instantiatio ...
- win10 安装redis相关问题。
最近需要在win10安装redis,但是redis的msi文件总是报这个错误: Redis on Windows Setup Wizard ended prematurely 都说是.NET fram ...
- 【swagger】C# 中 swagger 的使用及避坑
@ 目录 1 安装 2 修改名称和版本号 3 显示说明 4 显示控制器注释及汉化 5 路由相同,查询参数不同的方法 6 忽略 Model 中的某些字段 7 传递 header 8 出错时的 HTTP ...
- 12.1 flask基础之简单实用
一.Flask介绍(轻量级的框架,非常快速的就能把程序搭建起来) Flask是一个基于Python开发并且依赖jinja2模板和Werkzeug WSGI服务的一个微型框架,对于Werkzeug本质是 ...
- P1006 传纸条(二维、三维dp)
P1006 传纸条 输入输出样例 输入 #1 复制 3 3 0 3 9 2 8 5 5 7 0 输出 #1 复制 34 说明/提示 [限制] 对于 30% 的数据,1≤m,n≤10: 对于 100% ...
- P1004 方格取数(四维dp)
P1004 方格取数 思路如下 这题是看洛谷大佬的思路才写出来的,所以我会把大佬的思路展示如下: 1⃣️:我们可以找到一个叫思维dp的东西,dp[i][j][k][l],其中前两维表示一个人从原点出发 ...
- 解决POST乱码
在web.xml中添加字符过滤器 问题即可解决 <!--如何整合过滤器处理中文乱码问题?--> <filter> <filter-name>EncodingFilt ...