-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Order by: some_other_aggregation
enhancements (#788)
A couple of small enhancements here: * During Kibana QA, I found out that it really happens a lot that in `order by: some_other_aggregation`, this aggregation is not a direct child, but some further descender in the aggregation tree, so added support for it (before `order by: "agg1"` worked, now `order by: "agg1>agg2>agg3"` also works (`agg2` is a child of `agg1`, `agg3` of `agg2`, etc.) * Before we had support for a) `order by: "2"` (where `2` is a metric aggr with single value like e.g. `avg`) b) `order by: "2.10"` (where `2` is a `percentile[s|_ranks]` aggr, and `10` is a percentile) Here added also support for `2.count`, `2.std_deviation`, and other stats from `stats` or `extended_stats` aggregations. * Before we could only order by 1 expression, 2 or more weren't supported. Fixed that. Sorry for quite a big PR, but it turned out to be like that out of necessity. E.g. I implemented the last point only because without it, proper tests for previous points would need to be much larger 😆 --------- Co-authored-by: Jacek Migdal <[email protected]>
- Loading branch information
Showing
13 changed files
with
1,070 additions
and
361 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
11 changes: 11 additions & 0 deletions
11
quesma/model/metrics_aggregations/multiple_metric_columns_interface.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright Quesma, licensed under the Elastic License 2.0. | ||
// SPDX-License-Identifier: Elastic-2.0 | ||
package metrics_aggregations | ||
|
||
// MultipleMetricColumnsInterface is an interface for metrics aggregations | ||
// that have multiple columns in the response. | ||
// It allows to get the index of the column by its name, e.g. | ||
// "count", or "standard_deviation" for extended_stats, or "50" for quantile. | ||
type MultipleMetricColumnsInterface interface { | ||
ColumnIdx(name string) int | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
// Copyright Quesma, licensed under the Elastic License 2.0. | ||
// SPDX-License-Identifier: Elastic-2.0 | ||
package queryparser | ||
|
||
import ( | ||
"context" | ||
"quesma/logger" | ||
"quesma/model" | ||
"quesma/model/metrics_aggregations" | ||
"strings" | ||
) | ||
|
||
type pancakeOrderByTransformer struct { | ||
ctx context.Context | ||
} | ||
|
||
func newPancakeOrderByTransformer(ctx context.Context) *pancakeOrderByTransformer { | ||
return &pancakeOrderByTransformer{ctx: ctx} | ||
} | ||
|
||
// transformSingleOrderBy transforms a single order by expression, of query `query` and bucket aggregation `bucketAggrInternalName`. | ||
// What it does, it finds metric aggregation that corresponds to the order by expression, and returns a new aliased expression | ||
// | ||
// TODO: maybe the same logic needs to be applied to pipeline aggregations, needs checking. | ||
func (t *pancakeOrderByTransformer) transformSingleOrderBy(orderBy model.Expr, bucketAggregation *pancakeModelBucketAggregation, query *pancakeModel) *model.AliasedExpr { | ||
fullPathToOrderByExprRaw, isPath := orderBy.(model.LiteralExpr) | ||
if !isPath { | ||
return nil | ||
} | ||
|
||
fullPathToOrderByExpr, ok := fullPathToOrderByExprRaw.Value.(string) | ||
if !ok { | ||
logger.ErrorWithCtx(t.ctx).Msgf("path to metric is not a string, but %T (val: %v)", | ||
fullPathToOrderByExprRaw.Value, fullPathToOrderByExprRaw.Value) | ||
return nil | ||
} | ||
|
||
// fullPathToOrderByExpr is in the form of "[aggr1][>aggr2...]>metric_aggr[.submetric]" ([] means optional) | ||
// submetric: e.g. "percentiles.50", or "stats.sum", "extended_stats.std_deviation" | ||
// Most metric aggregations don't have submetrics | ||
var fullPathWithoutSubmetric, submetricName string | ||
splitByDot := strings.Split(fullPathToOrderByExpr, ".") | ||
switch len(splitByDot) { | ||
case 1: | ||
fullPathWithoutSubmetric = splitByDot[0] | ||
case 2: | ||
fullPathWithoutSubmetric, submetricName = splitByDot[0], splitByDot[1] | ||
default: | ||
logger.ErrorWithCtx(t.ctx).Msgf("path to metric is not valid: %s", fullPathToOrderByExpr) | ||
return nil | ||
} | ||
|
||
foundLayerIdx := -1 | ||
for layerIdx, layer := range query.layers { | ||
if layer.nextBucketAggregation == bucketAggregation { | ||
foundLayerIdx = layerIdx | ||
break | ||
} | ||
} | ||
if foundLayerIdx == -1 { | ||
logger.ErrorWithCtx(t.ctx).Msgf("bucket aggregation not found in query") | ||
return nil | ||
} | ||
foundLayerIdx += 1 | ||
fullPath := strings.Split(fullPathWithoutSubmetric, ">") | ||
path := fullPath | ||
|
||
for len(path) > 1 { | ||
if foundLayerIdx >= len(query.layers) { | ||
logger.ErrorWithCtx(t.ctx).Msgf("out of layers in path: %s", fullPathToOrderByExpr) | ||
return nil | ||
} | ||
if query.layers[foundLayerIdx].nextBucketAggregation == nil { | ||
logger.ErrorWithCtx(t.ctx).Msgf("no bucket aggregation in path: %s", fullPathToOrderByExpr) | ||
return nil | ||
} | ||
if query.layers[foundLayerIdx].nextBucketAggregation.name != path[0] { | ||
logger.ErrorWithCtx(t.ctx).Msgf("bucket aggregation mismatch in path: %s, expected: %s, was: %s", | ||
fullPathToOrderByExpr, path[0], query.layers[foundLayerIdx].nextBucketAggregation.name) | ||
return nil | ||
} | ||
foundLayerIdx += 1 | ||
path = path[1:] | ||
} | ||
|
||
if foundLayerIdx >= len(query.layers) { | ||
logger.ErrorWithCtx(t.ctx).Msgf("out of layers in path: %s", fullPathToOrderByExpr) | ||
return nil | ||
} | ||
|
||
for _, metric := range query.layers[foundLayerIdx].currentMetricAggregations { | ||
columnIdx := 0 // when no multiple columns, it must be 0 | ||
if multipleColumnsMetric, ok := metric.queryType.(metrics_aggregations.MultipleMetricColumnsInterface); ok { | ||
columnIdx = multipleColumnsMetric.ColumnIdx(submetricName) | ||
} | ||
|
||
if metric.name == path[0] { | ||
result := model.NewAliasedExpr(orderBy, metric.InternalNameForCol(columnIdx)) | ||
return &result | ||
} | ||
} | ||
|
||
logger.ErrorWithCtx(t.ctx).Msgf("no metric found for path: %s", fullPathToOrderByExpr) | ||
return nil | ||
} |
Oops, something went wrong.