使用统计聚合功能可以实现求最小值、求最大值、求和、求平均值、统计行数、去重统计行数、百分位统计、按字段值分组、按范围分组、按地理位置分组、按过滤条件分组、直方图统计、日期直方图统计、获取统计聚合分组内的行、嵌套查询等;同时多个统计聚合功能可以组合使用,满足复杂的查询需求。
流程
统计聚合的完整执行流程如下图所示。
统计聚合是在服务端的“查询”结束后执行,服务端会将“查询”阶段命中的所有文档根据查询请求进行统计聚合,因此统计聚合请求相比没有统计聚合的请求会复杂。
背景信息
统计聚合的详细功能请参见下表。
功能 | 说明 |
最小值 | 返回一个字段中的最小值,类似于SQL中的min。 |
最大值 | 返回一个字段中的最大值,类似于SQL中的max。 |
和 | 返回数值字段的总数,类似于SQL中的sum。 |
平均值 | 返回数值字段的平均值,类似于SQL中的avg。 |
统计行数 | 返回指定字段值的数量或者多元索引数据总行数,类似于SQL中的count。 |
去重统计行数 | 返回指定字段不同值的数量,类似于SQL中的count(distinct)。 |
百分位统计 | 百分位统计常用来统计一组数据的百分位分布情况,例如在日常系统运维中统计每次请求访问的耗时情况时,需要关注系统请求耗时的P25、P50、P90、P99值等分布情况。 |
字段值分组 | 根据一个字段的值对查询结果进行分组,相同的字段值放到同一分组内,返回每个分组的值和该值对应的个数。 说明 当分组较大时,按字段值分组可能会存在误差。 |
多字段分组 | 根据多个字段对查询结果进行分组,支持使用token进行翻页。 |
范围分组 | 根据一个字段的范围对查询结果进行分组,字段值在某范围内放到同一分组内,返回每个范围中相应的item个数。 |
地理位置分组 | 根据距离某一个中心点的范围对查询结果进行分组,距离差值在某范围内放到同一分组内,返回每个范围中相应的item个数。 |
过滤条件分组 | 按照过滤条件对查询结果进行分组,获取每个过滤条件匹配到的数量,返回结果的顺序和添加过滤条件的顺序一致。 |
直方图统计 | 按照指定数据间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。 |
日期直方图统计 | 对日期字段类型的数据按照指定间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。 |
获取统计聚合分组中的行 | 对查询结果进行分组后,获取每个分组内的一些行数据,可实现和MySQL中ANY_VALUE(field)类似的功能。 |
嵌套 | 分组类型的统计聚合功能支持嵌套,其内部可以添加子统计聚合。 |
多个统计聚合 | 多个统计聚合功能可以组合使用。 说明 当多个统计聚合的复杂度较高时可能会影响响应速度。 |
接口
统计聚合功能的接口为Search。
前提条件
您可以使用控制台、命令行工具或者SDK进行统计聚合。进行统计聚合之前,您需要完成如下准备工作。
使用控制台或命令行工具进行操作时只支持部分统计聚合功能,请以实际情况为准。
使用阿里云账号或者使用具有表格存储操作权限的RAM用户进行操作。如果需要为RAM用户授权表格存储操作权限,请参见通过RAM Policy为RAM用户授权进行配置。
使用SDK方式和命令行工具方式进行操作时,如果当前无可用AccessKey,则需要为阿里云账号或者RAM用户创建AccessKey。具体操作,请参见创建AccessKey。
已创建数据表。具体操作,请参见数据表操作。
已为数据表创建多元索引。具体操作,请参见创建多元索引。
使用SDK方式进行操作时,还需要完成初始化Client。具体操作,请参见初始化OTSClient。
使用命令行工具方式进行操作前,还需要完成下载并启动命令行工具,然后配置接入实例信息并选择要操作的表。具体操作,请参见下载命令行工具、启动并配置接入信息和数据表操作。
使用控制台
进入索引管理页签。
登录表格存储控制台。
在页面上方,选择资源组和地域。
在概览页面,单击实例名称或在操作列单击实例管理。
在实例详情页签下的数据表列表页签,单击数据表名称或在操作列单击索引管理。
在索引管理页签,单击目标多元索引操作列的搜索。
在查询数据对话框,查询数据。
系统默认返回所有列,如需显示指定属性列,关闭获取所有列并输入需要返回的属性列,多个属性列之间用半角逗号(,)隔开。
说明系统默认会返回数据表的主键列。
根据需要选择逻辑操作符为And、Or或者Not。
当选择逻辑操作符为And时,返回满足指定条件的数据。当选择逻辑操作符为Or时,如果配置了单个条件,则返回满足指定条件的数据;如果配置了多个条件,则返回满足任意一个条件的数据。当选择逻辑操作符为Not时,返回不满足指定条件的数据。
选择索引字段,单击添加,然后设置索引字段的查询类型和输入要查询的值。
您可以根据需要重复此步骤添加多个索引字段的查询配置。
系统默认关闭排序功能,如需根据指定字段对返回结果进行排序,打开是否排序开关后,根据需要添加要进行排序的字段并配置排序方式。
系统默认关闭统计功能,当需对指定字段进行数据统计时,请打开是否统计开关后,根据需要添加要进行统计的字段、配置统计类型和统计名称以及按需配置默认值。
系统支持一次添加多个字段进行统计操作。其中统计类型的可选项包括最小值、最大值、和、平均值、统计行数、去重统计行数,默认值参数用于表示当字段在某行中不存在时的取值。
单击确定。
符合查询条件的数据和统计结果会显示在索引管理页签中。
使用命令行工具
通过命令行工具执行search
命令查询数据时配置Aggregations实现统计聚合,支持的统计类型包括最小值(min)、最大值(max)、和(sum)、平均值(avg)、统计行数(count)等。更多信息,请参见多元索引。
执行
search
命令使用search_index多元索引查询和分析表中数据,并返回所有建立索引的列。search -n search_index --return_all_indexed
根据系统提示输入查询条件,示例如下:
以下示例用于查询满足gid列值小于10或gid列值精确匹配77中至少一个条件的行数据,并对gid列值求平均值。
{ "Offset": -1, "Limit": 10, "Collapse": null, "Sort": null, "GetTotalCount": true, "Token": null, "Query": { "Name": "BoolQuery", "Query": { "MinimumShouldMatch": null, "MustQueries": null, "MustNotQueries": null, "FilterQueries": null, "ShouldQueries": [{ "Name": "RangeQuery", "Query": { "FieldName": "gid", "From": null, "To": 10, "IncludeLower": false, "IncludeUpper": false } }, { "Name": "TermQuery", "Query": { "FieldName": "gid", "Term": 77 } }] } }, "Aggregations": [{ "Name": "avg", "Aggregation": { "AggName": "agg1", "Field": "gid", "MissingValue": null } }] }
使用SDK
您可以通过Java SDK、Go SDK、Python SDK、Node.js SDK、.NET SDK和PHP SDK使用统计聚合功能。此处以Java SDK为例介绍统计聚合的使用。
最小值
返回一个字段中的最小值,类似于SQL中的min。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double和Date类型。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 商品库中有每一种商品的价格,求产地为浙江省的商品中,价格最低的商品价格是多少。 * 等效的SQL语句是SELECT min(column_price) FROM product where place_of_production="浙江省"。 */ public void min(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.term("place_of_production", "浙江省")) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.min("min_agg_1", "column_price").missing(100)) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsMinAggregationResult("min_agg_1").getValue()); } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); TermQuery query = new TermQuery(); query.setTerm(ColumnValue.fromString("浙江省")); query.setFieldName("place_of_production"); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.term("place_of_production", "浙江省").build(); searchQuery.setQuery(query); searchQuery.setLimit(0); MinAggregation aggregation = new MinAggregation(); aggregation.setAggName("min_agg_1"); aggregation.setFieldName("column_price"); aggregation.setMissing(ColumnValue.fromLong(100)); //下述注释的builder写法等效于上述aggregation构建写法。 // MinAggregation aggregation2 = AggregationBuilders.min("min_agg_1", "column_price").missing(100).build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsMinAggregationResult("min_agg_1").getValue()); } }
最大值
返回一个字段中的最大值,类似于SQL中的max。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double和Date类型。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 商品库中有每一种商品的价格,求产地为浙江省的商品中,价格最高的商品价格是多少。 * 等效的SQL语句是SELECT max(column_price) FROM product where place_of_production="浙江省"。 */ public void max(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.term("place_of_production", "浙江省")) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.max("max_agg_1", "column_price").missing(0)) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsMaxAggregationResult("max_agg_1").getValue()); } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); TermQuery query = new TermQuery(); query.setTerm(ColumnValue.fromString("浙江省")); query.setFieldName("place_of_production"); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.term("place_of_production", "浙江省").build(); searchQuery.setQuery(query); searchQuery.setLimit(0); MaxAggregation aggregation = new MaxAggregation(); aggregation.setAggName("max_agg_1"); aggregation.setFieldName("column_price"); aggregation.setMissing(ColumnValue.fromLong(100)); //下述注释的builder写法等效于上述aggregation构建写法。 // MaxAggregation aggregation2 = AggregationBuilders.max("max_agg_1", "column_price").missing(100).build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsMaxAggregationResult("max_agg_1").getValue()); } }
和
返回数值字段的总数,类似于SQL中的sum。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long和Double类型。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 商品库中有每一种商品的价格,求产地为浙江省的商品中,价格最高的商品价格是多少。 * 等效的SQL语句是SELECT sum(column_price) FROM product where place_of_production="浙江省"。 */ public void sum(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.term("place_of_production", "浙江省")) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.sum("sum_agg_1", "column_number").missing(10)) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsSumAggregationResult("sum_agg_1").getValue()); } // 使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); TermQuery query = new TermQuery(); query.setTerm(ColumnValue.fromString("浙江省")); query.setFieldName("place_of_production"); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.term("place_of_production", "浙江省").build(); searchQuery.setQuery(query); searchQuery.setLimit(0); SumAggregation aggregation = new SumAggregation(); aggregation.setAggName("sum_agg_1"); aggregation.setFieldName("column_number"); aggregation.setMissing(ColumnValue.fromLong(100)); //下述注释的builder写法等效于上述aggregation构建写法。 // SumAggregation aggregation2 = AggregationBuilders.sum("sum_agg_1", "column_number").missing(10).build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsSumAggregationResult("sum_agg_1").getValue()); } }
平均值
返回数值字段的平均值,类似于SQL中的avg。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double和Date类型。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 商品库中有每一种商品的售出数量,求产地为浙江省的商品中,平均价格是多少。 * 等效的SQL语句是SELECT avg(column_price) FROM product where place_of_production="浙江省"。 */ public void avg(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.term("place_of_production", "浙江省")) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.avg("avg_agg_1", "column_price")) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsAvgAggregationResult("avg_agg_1").getValue()); } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); TermQuery query = new TermQuery(); query.setTerm(ColumnValue.fromString("浙江省")); query.setFieldName("place_of_production"); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.term("place_of_production", "浙江省").build(); searchQuery.setQuery(query); searchQuery.setLimit(0); AvgAggregation aggregation = new AvgAggregation(); aggregation.setAggName("avg_agg_1"); aggregation.setFieldName("column_price"); //下述注释的builder写法等效于上述aggregation构建写法。 // AvgAggregation aggregation2 = AggregationBuilders.avg("avg_agg_1", "column_price").build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsAvgAggregationResult("avg_agg_1").getValue()); } }
统计行数
返回指定字段值的数量或者多元索引数据总行数,类似于SQL中的count。
通过如下方式可以统计多元索引数据总行数或者某个query匹配的行数。
使用统计聚合的count功能,在请求中设置count(*)。
使用query功能的匹配行数,在query中设置setGetTotalCount(true);如果需要统计多元索引数据总行数,则使用MatchAllQuery。
如果需要获取多元索引数据某列出现的次数,则使用count(列名),可应用于稀疏列的场景。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double、Boolean、Keyword、Geo_point和Date类型。
示例
/** * 商家库中有每一种商家的惩罚记录,求浙江省的商家中,有惩罚记录的一共有多少个商家。如果商家没有惩罚记录,则商家信息中不存在该字段。 * 等效的SQL语句是SELECT count(column_history) FROM product where place_of_production="浙江省"。 */ public void count(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.term("place_of_production", "浙江省")) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.count("count_agg_1", "column_history")) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsCountAggregationResult("count_agg_1").getValue()); } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); TermQuery query = new TermQuery(); query.setTerm(ColumnValue.fromString("浙江省")); query.setFieldName("place_of_production"); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.term("place_of_production", "浙江省").build(); searchQuery.setQuery(query); searchQuery.setLimit(0); CountAggregation aggregation = new CountAggregation(); aggregation.setAggName("count_agg_1"); aggregation.setFieldName("column_history"); //下述注释的builder写法等效于上述aggregation构建写法。 // CountAggregation aggregation2 = AggregationBuilders.count("count_agg_1", "column_history").build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsCountAggregationResult("count_agg_1").getValue()); } }
去重统计行数
返回指定字段不同值的数量,类似于SQL中的count(distinct)
。
去重统计行数的计算结果是个近似值。
当去重统计行数小于1万时,计算结果接近精确值。
当去重统计行数达到1亿时,计算结果的误差为2%左右。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double、Boolean、Keyword、Geo_point和Date类型。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 求所有商品的产地一共来自多少个省份。 * 等效的SQL语句是SELECT count(distinct column_place) FROM product。 */ public void distinctCount(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.distinctCount("dis_count_agg_1", "column_place")) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsDistinctCountAggregationResult("dis_count_agg_1").getValue()); } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); MatchAllQuery query = new MatchAllQuery(); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.matchAll().build(); searchQuery.setQuery(query); searchQuery.setLimit(0); DistinctCountAggregation aggregation = new DistinctCountAggregation(); aggregation.setAggName("dis_count_agg_1"); aggregation.setFieldName("column_place"); //下述注释的builder写法等效于上述aggregation构建写法。 // DistinctCountAggregation aggregation2 = AggregationBuilders.distinctCount("dis_count_agg_1", "column_place").build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 System.out.println(resp.getAggregationResults().getAsDistinctCountAggregationResult("dis_count_agg_1").getValue()); } }
百分位统计
百分位统计常用来统计一组数据的百分位分布情况,例如在日常系统运维中统计每次请求访问的耗时情况时,需要关注系统请求耗时的P25、P50、P90、P99值等分布情况。
百分位统计为非精确统计,对不同百分位数值的计算精确度不同,较为极端的百分位数值更加准确,例如1%或99%的百分位数值会比50%的百分位数值准确。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double和Date类型。
percentiles
百分位分布例如50、90、99,可根据需要设置一个或者多个百分位。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 分析系统请求耗时百分位数分布情况。 */ public void percentilesAgg(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("indexName") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addAggregation(AggregationBuilders.percentiles("percentilesAgg", "latency") .percentiles(Arrays.asList(25.0d, 50.0d, 99.0d)) .missing(1.0)) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取结果。 PercentilesAggregationResult percentilesAggregationResult = resp.getAggregationResults().getAsPercentilesAggregationResult("percentilesAgg"); for (PercentilesAggregationItem item : percentilesAggregationResult.getPercentilesAggregationItems()) { System.out.println("key:" + item.getKey() + " value:" + item.getValue().asDouble()); } } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); MatchAllQuery query = new MatchAllQuery(); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.matchAll().build(); searchQuery.setQuery(query); searchQuery.setLimit(0); PercentilesAggregation aggregation = new PercentilesAggregation(); aggregation.setAggName("percentilesAgg"); aggregation.setFieldName("latency"); aggregation.setPercentiles(Arrays.asList(25.0d, 50.0d, 99.0d)); //下述注释的builder写法等效于上述aggregation构建写法。 // AggregationBuilders.percentiles("percentilesAgg", "latency").percentiles(Arrays.asList(25.0d, 50.0d, 99.0d)).missing(1.0).build(); List<Aggregation> aggregationList = new ArrayList<Aggregation>(); aggregationList.add(aggregation); searchQuery.setAggregationList(aggregationList); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取结果。 PercentilesAggregationResult percentilesAggregationResult = resp.getAggregationResults().getAsPercentilesAggregationResult("percentilesAgg"); for (PercentilesAggregationItem item : percentilesAggregationResult.getPercentilesAggregationItems()) { System.out.println("key:" + item.getKey() + " value:" + item.getValue().asDouble()); } } }
字段值分组
根据一个字段的值对查询结果进行分组,相同的字段值放到同一分组内,返回每个分组的值和该值对应的个数。
当分组较大时,按字段值分组可能会存在误差。
要实现多字段分组,您可以通过字段分组嵌套或者多字段分组方式实现。关于两种实现方式的功能对比,请参见附录:多字段分组不同实现方式对比。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long、Double、Boolean、Keyword和Date类型。
groupBySorter
分组中的item排序规则,默认按照分组中item的数量降序排序,多个排序则按照添加的顺序进行排列。支持的排序类型如下。
按照值的字典序升序排列
按照值的字典序降序排列
按照行数升序排列
按照行数降序排列
按照子统计聚合结果中值升序排列
按照子统计聚合结果中值降序排列
size
返回的分组数量,默认值为10。最大值为2000。当分组数量超过2000时,只会返回前2000个分组。
subAggregation和subGroupBy
子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。
场景
统计每个类别的商品数量,且统计每个类别价格的最大值和最小值。
方法
最外层的统计聚合是根据类别进行分组,再添加两个子统计聚合求价格的最大值和最小值。
结果示例
水果:5个(其中价格的最大值为15,最小值为3)
洗漱用品:10个(其中价格的最大值为98,最小值为1)
电子设备:3个(其中价格的最大值为8699,最小值为2300)
其它:15个(其中价格的最大值为1000,最小值为80)
示例
按单字段分组
/** * 所有商品中每一个类别各有多少个,且统计每一个类别的价格最大值和最小值。 * 返回结果举例:"水果:5个(其中价格的最大值为15,最小值为3),洗漱用品:10个(其中价格的最大值为98,最小值为1),电子设备:3个(其中价格的最大值为8699,最小值为2300), * 其它:15个(其中价格的最大值为1000,最小值为80)"。 */ public void groupByField(SyncClient client) { //使用builder模式构建查询语句。 { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("indexName") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) //如果只关心统计聚合结果,不关心具体数据,您可以将limit设置为0来提高性能。 .addGroupBy(GroupByBuilders .groupByField("name1", "column_type") .addSubAggregation(AggregationBuilders.min("subName1", "column_price")) .addSubAggregation(AggregationBuilders.max("subName2", "column_price")) ) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 for (GroupByFieldResultItem item : resp.getGroupByResults().getAsGroupByFieldResult("name1").getGroupByFieldResultItems()) { //打印值。 System.out.println(item.getKey()); //打印个数。 System.out.println(item.getRowCount()); //打印价格的最小值。 System.out.println(item.getSubAggregationResults().getAsMinAggregationResult("subName1").getValue()); //打印价格的最大值。 System.out.println(item.getSubAggregationResults().getAsMaxAggregationResult("subName2").getValue()); } } //使用非builder模式构建查询语句。 { SearchRequest searchRequest = new SearchRequest(); searchRequest.setTableName("<TABLE_NAME>"); searchRequest.setIndexName("<SEARCH_INDEX_NAME>"); SearchQuery searchQuery = new SearchQuery(); MatchAllQuery query = new MatchAllQuery(); //下述注释的builder写法等效于上述TermQuery构建写法。 // Query query2 = QueryBuilders.matchAll().build(); searchQuery.setQuery(query); searchQuery.setLimit(0); GroupByField groupByField = new GroupByField(); groupByField.setGroupByName("name1"); groupByField.setFieldName("column_type"); //设置子统计聚合。 MinAggregation minAggregation = AggregationBuilders.min("subName1", "column_price").build(); MaxAggregation maxAggregation = AggregationBuilders.max("subName2", "column_price").build(); groupByField.setSubAggregations(Arrays.asList(minAggregation, maxAggregation)); //下述注释的builder写法等效于上述aggregation构建写法。 // GroupByBuilders.groupByField("name1", "column_type") // .addSubAggregation(AggregationBuilders.min("subName1", "column_price")) // .addSubAggregation(AggregationBuilders.max("subName2", "column_price").build()); List<GroupBy> groupByList = new ArrayList<GroupBy>(); groupByList.add(groupByField); searchQuery.setGroupByList(groupByList); searchRequest.setSearchQuery(searchQuery); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 for (GroupByFieldResultItem item : resp.getGroupByResults().getAsGroupByFieldResult("name1").getGroupByFieldResultItems()) { //打印值。 System.out.println(item.getKey()); //打印个数。 System.out.println(item.getRowCount()); //打印价格的最小值。 System.out.println(item.getSubAggregationResults().getAsMinAggregationResult("subName1").getValue()); //打印价格的最大值。 System.out.println(item.getSubAggregationResults().getAsMaxAggregationResult("subName2").getValue()); } } }
通过字段分组嵌套进行多字段分组
/** * 按照嵌套多字段分组的示例。 * 多元索引支持通过嵌套使用两个groupBy实现与SQL中的groupBy多字段相似的功能。 * 等效的SQL语句是select a,d, sum(b),sum(c) from user group by a,d。 */ public void GroupByMultiField() { SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .returnAllColumns(true) //设置为false时,指定addColumnsToGet,性能会高。 //.addColumnsToGet("col_1","col_2") .searchQuery(SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) //此处相当于SQL中的where条件,可以通过QueryBuilders.bool()嵌套查询实现复杂的查询。 .addGroupBy( GroupByBuilders .groupByField("任意唯一名字标识_1", "field_a") .size(20) .addSubGroupBy( GroupByBuilders .groupByField("任意唯一名字标识_2", "field_d") .size(20) .addSubAggregation(AggregationBuilders.sum("任意唯一名字标识_3", "field_b")) .addSubAggregation(AggregationBuilders.sum("任意唯一名字标识_4", "field_c")) ) ) .build()) .build(); SearchResponse response = client.search(searchRequest); //查询符合条件的行。 List<Row> rows = response.getRows(); //获取统计聚合结果。 GroupByFieldResult groupByFieldResult1 = response.getGroupByResults().getAsGroupByFieldResult("任意唯一名字标识_1"); for (GroupByFieldResultItem resultItem : groupByFieldResult1.getGroupByFieldResultItems()) { System.out.println("field_a key:" + resultItem.getKey() + " Count:" + resultItem.getRowCount()); //获取子统计聚合结果。 GroupByFieldResult subGroupByResult = resultItem.getSubGroupByResults().getAsGroupByFieldResult("任意唯一名字标识_2"); for (GroupByFieldResultItem item : subGroupByResult.getGroupByFieldResultItems()) { System.out.println("field_a " + resultItem.getKey() + " field_d key:" + item.getKey() + " Count:" + item.getRowCount()); double sumOf_field_b = item.getSubAggregationResults().getAsSumAggregationResult("任意唯一名字标识_3").getValue(); double sumOf_field_c = item.getSubAggregationResults().getAsSumAggregationResult("任意唯一名字标识_4").getValue(); System.out.println("sumOf_field_b:" + sumOf_field_b); System.out.println("sumOf_field_c:" + sumOf_field_c); } } }
按字段分组时使用统计聚合排序
/** * 使用统计聚合排序的示例。 * 使用方法:按顺序添加GroupBySorter即可,添加多个GroupBySorter时排序结果按照添加顺序生效。GroupBySorter支持升序和降序两种方式。 * 默认排序是按照行数降序排列即GroupBySorter.rowCountSortInDesc()。 */ public void groupByFieldWithSort(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .addGroupBy(GroupByBuilders .groupByField("name1", "column_type") //.addGroupBySorter(GroupBySorter.subAggSortInAsc("subName1")) //按照子统计聚合结果中的值升序排序。 .addGroupBySorter(GroupBySorter.groupKeySortInAsc()) //按照统计聚合结果中的值升序排序。 //.addGroupBySorter(GroupBySorter.rowCountSortInDesc()) //按照统计聚合结果中的行数降序排序。 .size(20) .addSubAggregation(AggregationBuilders.min("subName1", "column_price")) .addSubAggregation(AggregationBuilders.max("subName2", "column_price")) ) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); }
多字段分组
根据多个字段对查询结果进行分组,支持使用token进行翻页。
目前只有Java SDK和Go SDK支持此功能。
要实现多字段分组,您可以通过字段分组嵌套或者多字段分组方式实现。关于两种实现方式的功能对比,请参见附录:多字段分组不同实现方式对比。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
sources
多列分组聚合字段对象,最多支持设置32列。支持的分组类型如下:
重要sources内分组类型的groupBySorter仅支持groupKeySort,不支持subAggSort和rowCountSort。
默认降序排序。
当某一列字段值不存在时,系统会返回NULL。
字段值分组GroupByField:仅支持设置groupByName、fieldName和groupBySorter参数。
直方图统计GroupByHistogram:仅支持设置groupByName、fieldName、interval和groupBySorter参数
日期直方图统计GroupByDateHistogram:仅支持设置groupByName、fieldName、interval、timeZone和groupBySorter参数。
nextToken
进行翻页时用于继续获取分组的token值。多列的字段值分组请求结果GroupByCompositeResult中会返回nextToken值,通过nextToken可翻页获取全量分组结果。
size
返回分组的数量。当满足条件的分组数量超过size限制时,您可以通过nextToken继续翻页获取分组。
重要当要限制返回的分组数量时,不支持同时配置size和suggestedSize参数。一般情况下只需配置size参数即可。
如果是用于对接例如Spark、Presto等计算引擎的高吞吐场景,则只需配置suggestedSize参数。
suggestedSize
可设置为大于服务端最大限制的值或-1, 服务端按实际能力返回实际行数。适用于对接例如Spark、Presto等计算引擎的高吞吐场景。
当该值超过服务端最大值限制后会被修正为最大值。实际返回的分组结果数量为
min(suggestedSize, 服务端分组数量限制,总分组数量)
。subAggregation和subGroupBy
子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。
重要GroupByComposite不支持作为subGroupBy。
示例
/** * 组合类型分组聚合:根据传入的多个SourceGroupBy(支持groupbyField、groupByHistogram和groupByDataHistogram)进行分组聚合 * 多列的聚合返回结果以扁平化结构返回。 */ public static void groupByComposite(SyncClient client) { GroupByComposite.Builder compositeBuilder = GroupByBuilders .groupByComposite("groupByComposite") .size(2000) .addSources(GroupByBuilders.groupByField("groupByField", "Col_Keyword") .addGroupBySorter(GroupBySorter.groupKeySortInAsc()).build()) .addSources(GroupByBuilders.groupByHistogram("groupByHistogram", "Col_Long") .addGroupBySorter(GroupBySorter.groupKeySortInAsc()) .interval(5) .build()) .addSources(GroupByBuilders.groupByDateHistogram("groupByDateHistogram", "Col_Date") .addGroupBySorter(GroupBySorter.groupKeySortInAsc()) .interval(5, DateTimeUnit.DAY) .timeZone("+05:30").build()); SearchRequest searchRequest = SearchRequest.newBuilder() .indexName("<SEARCH_INDEX_NAME>") .tableName("<TABLE_NAME>") .returnAllColumnsFromIndex(true) .searchQuery(SearchQuery.newBuilder() .addGroupBy(compositeBuilder.build()) .build()) .build(); SearchResponse resp = client.search(searchRequest); while (true) { if (resp.getGroupByResults() == null || resp.getGroupByResults().getResultAsMap().size() == 0) { System.out.println("groupByComposite Result is null or empty"); return; } GroupByCompositeResult result = resp.getGroupByResults().getAsGroupByCompositeResult("groupByComposite"); if(!result.getSourceNames().isEmpty()) { for (String sourceGroupByNames: result.getSourceNames()) { System.out.printf("%s\t", sourceGroupByNames); } System.out.print("rowCount\t\n"); } for (GroupByCompositeResultItem item : result.getGroupByCompositeResultItems()) { for (String value : item.getKeys()) { String val = value == null ? "NULL" : value; System.out.printf("%s\t", val); } System.out.printf("%d\t\n", item.getRowCount()); } // 利用token,继续翻页获取后续分组 if (result.getNextToken() != null) { searchRequest.setSearchQuery( SearchQuery.newBuilder() .addGroupBy(compositeBuilder.nextToken(result.getNextToken()).build()) .build() ); resp = client.search(searchRequest); } else { break; } } }
范围分组
根据一个字段的范围对查询结果进行分组,字段值在某范围内放到同一分组内,返回每个范围中相应的item个数。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long和Double类型。
range[double_from, double_to)
分组的范围。
起始值double_from可以使用最小值Double.MIN_VALUE,结束值double_to可以使用最大值Double.MAX_VALUE。
subAggregation和subGroupBy
子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。
例如按销量分组后再按省份分组,即可获得某个销量范围内哪个省比重比较大,实现方法是GroupByRange下添加一个GroupByField。
示例
/** * 求商品销量时按[0,1000)、[1000,5000)、[5000,Double.MAX_VALUE)这些分组计算每个范围的销量。 */ public void groupByRange(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .addGroupBy(GroupByBuilders .groupByRange("name1", "column_number") .addRange(0, 1000) .addRange(1000, 5000) .addRange(5000, Double.MAX_VALUE) ) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 for (GroupByRangeResultItem item : resp.getGroupByResults().getAsGroupByRangeResult("name1").getGroupByRangeResultItems()) { //打印个数。 System.out.println(item.getRowCount()); } }
地理位置分组
根据距离某一个中心点的范围对查询结果进行分组,距离差值在某范围内放到同一分组内,返回每个范围中相应的item个数。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Geo_point类型。
origin(double lat, double lon)
起始中心点的经纬度。
double lat是起始中心点纬度,double lon是起始中心点经度。
range[double_from, double_to)
分组的范围,单位为米。
起始值double_from可以使用最小值Double.MIN_VALUE,结束值double_to可以使用最大值Double.MAX_VALUE。
subAggregation和subGroupBy
子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。
示例
/** * 求距离万达广场[0,1000)、[1000,5000)、[5000,Double.MAX_VALUE)这些范围内的人数,距离的单位为米。 */ public void groupByGeoDistance(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .addGroupBy(GroupByBuilders .groupByGeoDistance("name1", "column_geo_point") .origin(3.1, 6.5) .addRange(0, 1000) .addRange(1000, 5000) .addRange(5000, Double.MAX_VALUE) ) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //获取统计聚合结果。 for (GroupByGeoDistanceResultItem item : resp.getGroupByResults().getAsGroupByGeoDistanceResult("name1").getGroupByGeoDistanceResultItems()) { //打印个数。 System.out.println(item.getRowCount()); } }
过滤条件分组
按照过滤条件对查询结果进行分组,获取每个过滤条件匹配到的数量,返回结果的顺序和添加过滤条件的顺序一致。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
filter
过滤条件,返回结果的顺序和添加过滤条件的顺序一致。
subAggregation和subGroupBy
子统计聚合,子统计聚合会根据分组内容再进行一次统计聚合分析。
示例
/** * 按照过滤条件进行分组,例如添加三个过滤条件(销量大于100、产地是浙江省、描述中包含杭州关键词),然后获取每个过滤条件匹配到的数量。 */ public void groupByFilter(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .addGroupBy(GroupByBuilders .groupByFilter("name1") .addFilter(QueryBuilders.range("number").greaterThanOrEqual(100)) .addFilter(QueryBuilders.term("place","浙江省")) .addFilter(QueryBuilders.match("text","杭州")) ) .build()) .build(); //执行查询。 SearchResponse resp = client.search(searchRequest); //按照过滤条件的顺序获取的统计聚合结果。 for (GroupByFilterResultItem item : resp.getGroupByResults().getAsGroupByFilterResult("name1").getGroupByFilterResultItems()) { //打印个数。 System.out.println(item.getRowCount()); } }
直方图统计
按照指定数据间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Long和Double类型。
interval
统计间隔。
fieldRange[min,max]
统计范围,与interval参数配合使用限制分组的数量。
(fieldRange.max-fieldRange.min)/interval
的值不能超过2000。minDocCount
最小行数。当分组中的行数小于最小行数时,不会返回此分组的统计结果。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
示例
/** * 统计不同年龄段用户数量分布情况。 */ public static void groupByHistogram(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .tableName("<TABLE_NAME>") .indexName("<SEARCH_INDEX_NAME>") .searchQuery( SearchQuery.newBuilder() .addGroupBy(GroupByBuilders .groupByHistogram("groupByHistogram", "age") .interval(10) .minDocCount(0L) .addFieldRange(0, 99)) .build()) .build(); //执行查询。 SearchResponse resp = ots.search(searchRequest); //获取直方图的统计聚合结果。 GroupByHistogramResult results = resp.getGroupByResults().getAsGroupByHistogramResult("groupByHistogram"); for (GroupByHistogramItem item : results.getGroupByHistogramItems()) { System.out.println("key:" + item.getKey().asLong() + " value:" + item.getValue()); } }
日期直方图统计
对日期字段类型的数据按照指定间隔对查询结果进行分组,字段值在相同范围内放到同一分组内,返回每个分组的值和该值对应的个数。
表格存储Java SDK从5.16.1版本开始支持此功能。
参数
参数
说明
groupByName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
fieldName
用于统计聚合的字段,仅支持Date类型。
interval
统计间隔。
fieldRange[min,max]
统计范围,与interval参数配合使用限制分组的数量。
(fieldRange.max-fieldRange.min)/interval
的值不能超过2000。minDocCount
最小行数。当分组中的行数小于最小行数时,不会返回此分组的统计结果。
missing
当某行数据中的字段为空时,字段值的默认值。
如果未设置missing值,则在统计聚合时会忽略该行。
如果设置了missing值,则使用missing值作为字段值的默认值参与统计聚合。
timeZone
时区。格式为
+hh:mm
或者-hh:mm
,例如+08:00
、-09:00
。只有当字段数据类型为Date时才需要配置。当Date类型字段的Format未设置时区信息时,可能会导致聚合结果存在N小时的偏移,此时请设置timeZone来解决该问题。
示例
/** * 统计2017-05-01 10:00到2017-05-21 13:00:00时间段内每天的col_date数据分布情况。 */ public static void groupByDateHistogram(SyncClient client) { //构建查询语句。 SearchRequest searchRequest = SearchRequest.newBuilder() .returnAllColumns(false) .indexName("<SEARCH_INDEX_NAME>") .tableName("<TABLE_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .getTotalCount(false) .addGroupBy(GroupByBuilders .groupByDateHistogram("groupByDateHistogram", "col_date") .interval(1, DateTimeUnit.DAY) .minDocCount(1) .missing("2017-05-01 13:01:00") .fieldRange("2017-05-01 10:00", "2017-05-21 13:00:00")) .build()) .build(); //执行查询。 SearchResponse resp = ots.search(searchRequest); //获取直方图的统计聚合结果。 List<GroupByDateHistogramItem> items = resp.getGroupByResults().getAsGroupByDateHistogramResult("groupByDateHistogram").getGroupByDateHistogramItems(); for (GroupByDateHistogramItem item : items) { System.out.printf("millisecondTimestamp:%d, count:%d \n", item.getTimestamp(), item.getRowCount()); } }
获取统计聚合分组中的行
对查询结果进行分组后,获取每个分组内的一些行数据,可实现和MySQL中ANY_VALUE(field)类似的功能。
获取统计聚合分组中的行时,如果多元索引中包含嵌套类型、地理位置类型或者数组类型的字段,则返回结果中只会包含主键信息,请手动反查数据表获取所需字段。
参数
参数
说明
aggregationName
自定义的统计聚合名称,用于区分不同的统计聚合,可根据此名称获取本次统计聚合结果。
limit
每个分组内最多返回的数据行数,默认返回1行数据。
sort
分组内数据的排序方式。
columnsToGet
指定返回的字段,仅支持多元索引中的字段,且不支持数组、Date、Geopoint和Nested类型的字段。
该参数复用SearchRequest中的columnsToGet参数,在SearchRequest中指定columnsToGet即可。
示例
/** * 某学校有一个活动报名表,活动报名表中包含学生姓名、班级、班主任、班长等信息,如果希望按班级进行分组,以查看每个班级的报名情况,同时获取班级的属性信息。 * 等效的SQL语句是select className, teacher, monitor, COUNT(*) as number from table GROUP BY className。 */ public void testTopRows(SyncClient client) { SearchRequest searchRequest = SearchRequest.newBuilder() .indexName("<SEARCH_INDEX_NAME>") .tableName("<TABLE_NAME>") .searchQuery( SearchQuery.newBuilder() .query(QueryBuilders.matchAll()) .limit(0) .addGroupBy(GroupByBuilders.groupByField("groupName", "className") .size(5) //返回的分组数量,最大值请参见“多元索引使用限制”文档中的限制性“GroupByField返回的分组个数”。 .addSubAggregation(AggregationBuilders.topRows("topRowsName") .limit(1) .sort(new Sort(Arrays.asList(new FieldSort("teacher", SortOrder.DESC)))) // topRows的排序 ) ) .build()) .addColumnsToGet(Arrays.asList("teacher", "monitor")) .build(); SearchResponse resp = client.search(searchRequest); List<GroupByFieldResultItem> items = resp.getGroupByResults().getAsGroupByFieldResult("groupName").getGroupByFieldResultItems(); for (GroupByFieldResultItem item : items) { String className = item.getKey(); long number = item.getRowCount(); List<Row> topRows = item.getSubAggregationResults().getAsTopRowsAggregationResult("topRowsName").getRows(); Row row = topRows.get(0); String teacher = row.getLatestColumn("teacher").getValue().asString(); String monitor = row.getLatestColumn("monitor").getValue().asString(); } }
嵌套
分组类型的统计聚合功能支持嵌套,其内部可以添加子统计聚合。
主要用于在分组内再次进行统计聚合,以两层的嵌套为例:
GroupBy+SubGroupBy:按省份分组后再按照城市分组,获取每个省份下每个城市的数据。
GroupBy+SubAggregation:按照省份分组后再求某个指标的最大值,获取每个省的某个指标最大值。
为了性能、复杂度等综合考虑,嵌套的层级只开放了一定的层数。更多信息,请参见多元索引限制。
public void subGroupBy(SyncClient client) {
//构建查询语句。
SearchRequest searchRequest = SearchRequest.newBuilder()
.indexName("index_name")
.tableName("table_name")
.returnAllColumns(true)
.searchQuery(
SearchQuery.newBuilder()
.query(QueryBuilders.match("textField", "hello"))
.limit(10)
.addAggregation(AggregationBuilders.min("name1", "fieldName1"))
.addAggregation(AggregationBuilders.max("name2", "fieldName2"))
.addGroupBy(GroupByBuilders
.groupByField("name3", "fieldName3")
.addSubAggregation(AggregationBuilders.max("subName1", "fieldName4"))
.addSubAggregation(AggregationBuilders.sum("subName2", "fieldName5"))
.addSubGroupBy(GroupByBuilders
.groupByRange("subName3", "fieldName6")
.addRange(12, 90)
.addRange(100, 900)
))
.build())
.build();
//执行查询。
SearchResponse resp = client.search(searchRequest);
//获取第一层求最小值和求最大值的统计聚合结果。
AggregationResults aggResults = resp.getAggregationResults();
System.out.println(aggResults.getAsMinAggregationResult("name1").getValue());
System.out.println(aggResults.getAsMaxAggregationResult("name2").getValue());
//获取第一层按字段值分组的统计聚合结果,并同时获取其嵌套的子统计聚合结果。
GroupByFieldResult results = resp.getGroupByResults().getAsGroupByFieldResult("name3");
for (GroupByFieldResultItem item : results.getGroupByFieldResultItems()) {
System.out.println("数量:" + item.getRowCount());
System.out.println("key:" + item.getKey());
//获取子统计聚合结果。
//打印求最大值的子统计聚合结果。
System.out.println(item.getSubAggregationResults().getAsMaxAggregationResult("subName1"));
//打印求和的子统计聚合结果。
System.out.println(item.getSubAggregationResults().getAsSumAggregationResult("subName2"));
//打印按范围分组的子统计聚合结果。
GroupByRangeResult subResults = item.getSubGroupByResults().getAsGroupByRangeResult("subName3");
for (GroupByRangeResultItem subItem : subResults.getGroupByRangeResultItems()) {
System.out.println(String.format("from:%s, to:%s, count:%s", subItem.getFrom(), subItem.getTo(), subItem.getRowCount()));
}
}
}
多个统计聚合
多个统计聚合功能可以组合使用。
当多个统计聚合的复杂度较高时可能会影响响应速度。
组合使用多个Aggregation
public void multipleAggregation(SyncClient client) {
//构建查询语句。
SearchRequest searchRequest = SearchRequest.newBuilder()
.tableName("<TABLE_NAME>")
.indexName("<SEARCH_INDEX_NAME>")
.searchQuery(
SearchQuery.newBuilder()
.query(QueryBuilders.matchAll())
.limit(0)
.addAggregation(AggregationBuilders.min("name1", "long"))
.addAggregation(AggregationBuilders.sum("name2", "long"))
.addAggregation(AggregationBuilders.distinctCount("name3", "long"))
.build())
.build();
//执行查询。
SearchResponse resp = client.search(searchRequest);
//获取求最小值的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsMinAggregationResult("name1").getValue());
//获取求和的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsSumAggregationResult("name2").getValue());
//获取去重统计行数的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsDistinctCountAggregationResult("name3").getValue());
}
组合使用Aggregation和GroupBy
public void multipleGroupBy(SyncClient client) {
//构建查询语句。
SearchRequest searchRequest = SearchRequest.newBuilder()
.tableName("<TABLE_NAME>")
.indexName("<SEARCH_INDEX_NAME>")
.searchQuery(
SearchQuery.newBuilder()
.query(QueryBuilders.matchAll())
.limit(0)
.addAggregation(AggregationBuilders.min("name1", "long"))
.addAggregation(AggregationBuilders.sum("name2", "long"))
.addAggregation(AggregationBuilders.distinctCount("name3", "long"))
.addGroupBy(GroupByBuilders.groupByField("name4", "type"))
.addGroupBy(GroupByBuilders.groupByRange("name5", "long").addRange(1, 15))
.build())
.build();
//执行查询。
SearchResponse resp = client.search(searchRequest);
//获取求最小值的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsMinAggregationResult("name1").getValue());
//获取求和的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsSumAggregationResult("name2").getValue());
//获取去重统计行数的统计聚合结果。
System.out.println(resp.getAggregationResults().getAsDistinctCountAggregationResult("name3").getValue());
//获取按字段值分组的统计聚合结果。
for (GroupByFieldResultItem item : resp.getGroupByResults().getAsGroupByFieldResult("name4").getGroupByFieldResultItems()) {
//打印key。
System.out.println(item.getKey());
//打印个数。
System.out.println(item.getRowCount());
}
//获取按范围分组的统计聚合结果。
for (GroupByRangeResultItem item : resp.getGroupByResults().getAsGroupByRangeResult("name5").getGroupByRangeResultItems()) {
//打印个数。
System.out.println(item.getRowCount());
}
}
附录:多字段分组不同实现方式对比
如果要根据多个字段对查询结果进行分组,您可以通过字段分组嵌套(即嵌套多层groupBy)或者多字段分组(即直接使用GroupByComposite)功能实现。具体实现方式的功能对比说明请参见下表。
功能对比 | 字段分组嵌套 | 多字段分组 |
size | 2000 | 2000 |
字段列限制 | 最高支持5层 | 最高支持32列 |
翻页 | 不支持 | 支持通过GroupByComposite中的nextToken翻页 |
分组中Item排序规则 |
| 按照值的字典序升序或降序排列 |
是否支持子统计聚合 | 支持 | 支持 |
兼容性 | 日期类型根据format返回 | 日期类型返回时间戳字符串 |