MQE support `sort_values` and `sort_label_values` functions. (#12193)
* MQE support `sort_values` and `sort_label_values` functions. * sync UI.
This commit is contained in:
parent
a63601318d
commit
b3044ee714
|
|
@ -232,9 +232,8 @@ Expression:
|
|||
top_n(<metric_name>, <top_number>, <order>)
|
||||
```
|
||||
|
||||
`top_number` is the number of the top results, should be a positive integer.
|
||||
|
||||
`order` is the order of the top results. The value of `order` can be `asc` or `des`.
|
||||
- `top_number` is the number of the top results, should be a positive integer.
|
||||
- `order` is the order of the top results. The value of `order` can be `asc` or `des`.
|
||||
|
||||
For example:
|
||||
If we want to query the current service's top 10 instances with the highest `service_instance_cpm` metric value, we can use the following expression
|
||||
|
|
@ -273,11 +272,11 @@ AggregateLabels Operation takes an expression and performs an aggregate calculat
|
|||
|
||||
Expression:
|
||||
```text
|
||||
aggregate_labels(Expression, AggregateType<Optional>(<label1_name>,<label2_name>...))
|
||||
aggregate_labels(Expression, <AggregateType>(<label1_name>,<label2_name>...))
|
||||
```
|
||||
|
||||
- `AggregateType` is the type of the aggregation operation.
|
||||
- `<label1_name>,<label2_name>...` is the label names that need to be aggregated. If not specified, all labels will be aggregated.
|
||||
- `<label1_name>,<label2_name>...` is the label names that need to be aggregated. If not specified, all labels will be aggregated. Optional.
|
||||
|
||||
| AggregateType | Definition | ExpressionResultType |
|
||||
|---------------|----------------------------------------------------|----------------------|
|
||||
|
|
@ -379,6 +378,62 @@ V(T1)-V(T1-2), V(T2)-V(T1-1), V(T3)-V(T1)
|
|||
### Result Type
|
||||
TIME_SERIES_VALUES.
|
||||
|
||||
## Sort Operation
|
||||
### SortValues Operation
|
||||
SortValues Operation takes an expression and sorts the values of the input expression result.
|
||||
|
||||
Expression:
|
||||
```text
|
||||
sort_values(Expression, <limit>, <order>)
|
||||
```
|
||||
- `limit` is the number of the sort results, should be a positive integer, if not specified, will return all results. Optional.
|
||||
- `order` is the order of the sort results. The value of `order` can be `asc` or `des`.
|
||||
|
||||
For example:
|
||||
If we want to sort the `service_resp_time` metric values in descending order and get the top 10 values, we can use the following expression:
|
||||
```text
|
||||
sort_values(service_resp_time, 10, des)
|
||||
```
|
||||
|
||||
#### Result Type
|
||||
The result type follows the input expression.
|
||||
|
||||
### SortLabelValues Operation
|
||||
SortLabelValues Operation takes an expression and sorts the label values of the input expression result. This function uses `natural sort order`.
|
||||
|
||||
Expression:
|
||||
```text
|
||||
sort_label_values(Expression, <order>, <label1_name>, <label2_name> ...)
|
||||
```
|
||||
- `order` is the order of the sort results. The value of `order` can be `asc` or `des`.
|
||||
- `<label1_name>, <label2_name> ...` is the label names that need to be sorted by their values. At least one label name should be specified.
|
||||
The labels in the head of the list will be sorted first, and if the label not be included in the expression result will be ignored.
|
||||
|
||||
For example:
|
||||
If we want to sort the `service_percentile` metric label values in descending order by the `p` label, we can use the following expression:
|
||||
```text
|
||||
sort_label_values(service_percentile{p='50,75,90,95,99'}, des, p)
|
||||
```
|
||||
|
||||
For multiple labels, assume the metric has 2 labels:
|
||||
```text
|
||||
metric{label1='a', label2='2a'}
|
||||
metric{label1='a', label2='2c'}
|
||||
metric{label1='b', label2='2a'}
|
||||
metric{label1='b', label2='2c'}
|
||||
```
|
||||
If we want to sort the `metric` metric label values in descending order by the `label1` and `label2` labels, we can use the following expression:
|
||||
```text
|
||||
sort_label_values(metric, des, label1, label2)
|
||||
```
|
||||
And the result will be:
|
||||
```text
|
||||
metric{label1='b', label2='2c'}
|
||||
metric{label1='b', label2='2a'}
|
||||
metric{label1='a', label2='2c'}
|
||||
metric{label1='a', label2='2a'}
|
||||
```
|
||||
|
||||
## Expression Query Example
|
||||
### Labeled Value Metrics
|
||||
```text
|
||||
|
|
|
|||
|
|
@ -120,6 +120,7 @@
|
|||
* Add component definition(ID=152) for `c3p0`(JDBC3 Connection and Statement Pooling).
|
||||
* Fix MQE `top_n` global query.
|
||||
* Fix inaccurate Pulsar and Bookkeeper metrics.
|
||||
* MQE support `sort_values` and `sort_label_values` functions.
|
||||
|
||||
#### UI
|
||||
|
||||
|
|
|
|||
|
|
@ -81,6 +81,10 @@ DES options { caseInsensitive=true; }: 'des';
|
|||
// AGGREGATE_LABELS
|
||||
AGGREGATE_LABELS: 'aggregate_labels';
|
||||
|
||||
// Sort
|
||||
SORT_VALUES: 'sort_values';
|
||||
SORT_LABEL_VALUES: 'sort_label_values';
|
||||
|
||||
// Literals
|
||||
INTEGER: Digit+;
|
||||
DECIMAL: Digit+ DOT Digit+;
|
||||
|
|
|
|||
|
|
@ -36,6 +36,8 @@ expression
|
|||
| topN L_PAREN metric COMMA INTEGER COMMA order R_PAREN #topNOP
|
||||
| relabels L_PAREN expression COMMA label COMMA replaceLabel R_PAREN #relablesOP
|
||||
| aggregateLabels L_PAREN expression COMMA aggregateLabelsFunc R_PAREN #aggregateLabelsOp
|
||||
| sort_values L_PAREN expression (COMMA INTEGER)? COMMA order R_PAREN #sortValuesOP
|
||||
| sort_label_values L_PAREN expression COMMA order COMMA labelNameList R_PAREN #sortLabelValuesOP
|
||||
;
|
||||
|
||||
expressionList
|
||||
|
|
@ -91,3 +93,9 @@ aggregateLabelsFunc: aggregateLabelsFuncName (L_PAREN labelNameList R_PAREN)?;
|
|||
|
||||
aggregateLabelsFuncName:
|
||||
AVG | SUM | MAX | MIN;
|
||||
|
||||
sort_values:
|
||||
SORT_VALUES;
|
||||
|
||||
sort_label_values:
|
||||
SORT_LABEL_VALUES;
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.util.ArrayList;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParserBaseVisitor;
|
||||
|
|
@ -31,6 +32,8 @@ import org.apache.skywalking.mqe.rt.operation.BinaryOp;
|
|||
import org.apache.skywalking.mqe.rt.operation.CompareOp;
|
||||
import org.apache.skywalking.mqe.rt.operation.LogicalFunctionOp;
|
||||
import org.apache.skywalking.mqe.rt.operation.MathematicalFunctionOp;
|
||||
import org.apache.skywalking.mqe.rt.operation.SortLabelValuesOp;
|
||||
import org.apache.skywalking.mqe.rt.operation.SortValuesOp;
|
||||
import org.apache.skywalking.mqe.rt.operation.TrendOp;
|
||||
import org.apache.skywalking.mqe.rt.type.ExpressionResult;
|
||||
import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
|
||||
|
|
@ -311,6 +314,42 @@ public abstract class MQEVisitorBase extends MQEParserBaseVisitor<ExpressionResu
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitSortValuesOP(MQEParser.SortValuesOPContext ctx) {
|
||||
ExpressionResult result = visit(ctx.expression());
|
||||
int order = ctx.order().getStart().getType();
|
||||
Optional<Integer> limit = Optional.empty();
|
||||
if (ctx.INTEGER() != null) {
|
||||
limit = Optional.of(Integer.valueOf(ctx.INTEGER().getText()));
|
||||
}
|
||||
try {
|
||||
return SortValuesOp.doSortValuesOp(result, limit, order);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError(e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExpressionResult visitSortLabelValuesOP(MQEParser.SortLabelValuesOPContext ctx) {
|
||||
ExpressionResult result = visit(ctx.expression());
|
||||
int order = ctx.order().getStart().getType();
|
||||
List<String> labelNames = new ArrayList<>();
|
||||
for (MQEParser.LabelNameContext labelNameContext : ctx.labelNameList().labelName()) {
|
||||
labelNames.add(labelNameContext.getText());
|
||||
}
|
||||
try {
|
||||
return SortLabelValuesOp.doSortLabelValuesOp(result, order, labelNames);
|
||||
} catch (IllegalExpressionException e) {
|
||||
ExpressionResult errorResult = new ExpressionResult();
|
||||
errorResult.setType(ExpressionResultType.UNKNOWN);
|
||||
errorResult.setError(e.getMessage());
|
||||
return errorResult;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public abstract ExpressionResult visitMetric(MQEParser.MetricContext ctx);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.mqe.rt.operation;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.type.ExpressionResult;
|
||||
import org.apache.skywalking.mqe.rt.type.MQEValues;
|
||||
import org.apache.skywalking.oap.server.core.query.type.KeyValue;
|
||||
import org.apache.skywalking.oap.server.library.util.CollectionUtils;
|
||||
|
||||
import static java.util.stream.Collectors.groupingBy;
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
@Slf4j
|
||||
public class SortLabelValuesOp {
|
||||
public static ExpressionResult doSortLabelValuesOp(ExpressionResult expResult,
|
||||
int order,
|
||||
List<String> labelNames) throws IllegalExpressionException {
|
||||
if (CollectionUtils.isNotEmpty(labelNames)) {
|
||||
labelNames = labelNames.stream().distinct().collect(toList());
|
||||
if (MQEParser.ASC == order) {
|
||||
expResult.setResults(
|
||||
sort(expResult.getResults(), labelNames, labelNames.get(0), Comparator.naturalOrder()));
|
||||
} else if (MQEParser.DES == order) {
|
||||
expResult.setResults(
|
||||
sort(expResult.getResults(), labelNames, labelNames.get(0), Comparator.reverseOrder()));
|
||||
} else {
|
||||
throw new IllegalExpressionException("Unsupported sort order.");
|
||||
}
|
||||
}
|
||||
return expResult;
|
||||
}
|
||||
|
||||
private static List<MQEValues> sort(List<MQEValues> results,
|
||||
List<String> sortLabels,
|
||||
String currentSortLabel,
|
||||
Comparator<String> comparator) {
|
||||
if (!sortLabels.contains(currentSortLabel)) {
|
||||
log.error("Current sort label {} not found in the sort labels {} ", currentSortLabel, sortLabels);
|
||||
return results;
|
||||
}
|
||||
if (sortLabels.indexOf(
|
||||
currentSortLabel) == sortLabels.size() - 1) { //only one label or the latest label no need to group
|
||||
results.sort(Comparator.comparing(mqeValues -> mqeValues.getMetric()
|
||||
.getLabels()
|
||||
.stream()
|
||||
.filter(kv -> kv.getKey().equals(currentSortLabel))
|
||||
.findFirst()
|
||||
.orElse(new KeyValue(currentSortLabel, ""))
|
||||
.getValue(), comparator));
|
||||
} else {
|
||||
LinkedHashMap<KeyValue, List<MQEValues>> groupResult = group(results, currentSortLabel);
|
||||
LinkedHashMap<KeyValue, List<MQEValues>> sortedGroup = new LinkedHashMap<>(groupResult.size());
|
||||
for (Map.Entry<KeyValue, List<MQEValues>> entry : groupResult.entrySet()) {
|
||||
//sort by next label
|
||||
List<MQEValues> sortedResult = sort(
|
||||
entry.getValue(), sortLabels, sortLabels.get(sortLabels.indexOf(currentSortLabel) + 1), comparator);
|
||||
sortedGroup.put(entry.getKey(), sortedResult);
|
||||
}
|
||||
//combine the sorted group
|
||||
results = sortedGroup.keySet()
|
||||
.stream()
|
||||
.sorted(Comparator.comparing(KeyValue::getValue, comparator))
|
||||
.flatMap(keyValue -> sortedGroup.get(keyValue).stream())
|
||||
.collect(toList());
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
//group by current label for sub sorting
|
||||
private static LinkedHashMap<KeyValue, List<MQEValues>> group(List<MQEValues> results, String labelName) {
|
||||
return results
|
||||
.stream()
|
||||
.collect(groupingBy(
|
||||
mqeValues -> mqeValues.getMetric().getLabels()
|
||||
.stream()
|
||||
.filter(kv -> kv.getKey().equals(labelName))
|
||||
.findFirst().orElse(new KeyValue(labelName, "")),
|
||||
LinkedHashMap::new,
|
||||
toList()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,57 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.mqe.rt.operation;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.type.ExpressionResult;
|
||||
import org.apache.skywalking.mqe.rt.type.MQEValue;
|
||||
|
||||
public class SortValuesOp {
|
||||
public static ExpressionResult doSortValuesOp(ExpressionResult expResult,
|
||||
Optional<Integer> limit,
|
||||
int order) throws IllegalExpressionException {
|
||||
if (MQEParser.ASC == order || MQEParser.DES == order) {
|
||||
expResult.getResults().forEach(mqeValues -> {
|
||||
List<MQEValue> values = mqeValues.getValues()
|
||||
.stream()
|
||||
// Filter out empty values
|
||||
.filter(mqeValue -> !mqeValue.isEmptyValue())
|
||||
.sorted(MQEParser.ASC == order ? Comparator.comparingDouble(
|
||||
MQEValue::getDoubleValue) :
|
||||
Comparator.comparingDouble(MQEValue::getDoubleValue)
|
||||
.reversed())
|
||||
.collect(
|
||||
Collectors.toList());
|
||||
if (limit.isPresent() && limit.get() < values.size()) {
|
||||
mqeValues.setValues(values.subList(0, limit.get()));
|
||||
} else {
|
||||
mqeValues.setValues(values);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
throw new IllegalExpressionException("Unsupported sort order.");
|
||||
}
|
||||
return expResult;
|
||||
}
|
||||
}
|
||||
|
|
@ -65,6 +65,61 @@ public class MockData {
|
|||
return seriesLabeled;
|
||||
}
|
||||
|
||||
public ExpressionResult newSeriesComplexLabeledResult() {
|
||||
ExpressionResult seriesLabeled = new ExpressionResult();
|
||||
seriesLabeled.setLabeledResult(true);
|
||||
seriesLabeled.setType(ExpressionResultType.TIME_SERIES_VALUES);
|
||||
MQEValues mqeValues111 = new MQEValues();
|
||||
mqeValues111.setMetric(newMetadata("label", "a"));
|
||||
mqeValues111.getMetric().getLabels().add(new KeyValue("label2", "2a"));
|
||||
mqeValues111.getMetric().getLabels().add(new KeyValue("label3", "31"));
|
||||
mqeValues111.getValues().add(newMQEValue("100", 100));
|
||||
MQEValues mqeValues112 = new MQEValues();
|
||||
mqeValues112.setMetric(newMetadata("label", "a"));
|
||||
mqeValues112.getMetric().getLabels().add(new KeyValue("label2", "2a"));
|
||||
mqeValues112.getMetric().getLabels().add(new KeyValue("label3", "32"));
|
||||
mqeValues112.getValues().add(newMQEValue("100", 101));
|
||||
MQEValues mqeValues121 = new MQEValues();
|
||||
mqeValues121.setMetric(newMetadata("label", "a"));
|
||||
mqeValues121.getMetric().getLabels().add(new KeyValue("label2", "2b"));
|
||||
mqeValues121.getMetric().getLabels().add(new KeyValue("label3", "31"));
|
||||
mqeValues121.getValues().add(newMQEValue("100", 102));
|
||||
MQEValues mqeValues122 = new MQEValues();
|
||||
mqeValues122.setMetric(newMetadata("label", "a"));
|
||||
mqeValues122.getMetric().getLabels().add(new KeyValue("label2", "2b"));
|
||||
mqeValues122.getMetric().getLabels().add(new KeyValue("label3", "32"));
|
||||
mqeValues122.getValues().add(newMQEValue("100", 103));
|
||||
MQEValues mqeValues211 = new MQEValues();
|
||||
mqeValues211.setMetric(newMetadata("label", "b"));
|
||||
mqeValues211.getMetric().getLabels().add(new KeyValue("label2", "2a"));
|
||||
mqeValues211.getMetric().getLabels().add(new KeyValue("label3", "31"));
|
||||
mqeValues211.getValues().add(newMQEValue("100", 104));
|
||||
MQEValues mqeValues212 = new MQEValues();
|
||||
mqeValues212.setMetric(newMetadata("label", "b"));
|
||||
mqeValues212.getMetric().getLabels().add(new KeyValue("label2", "2a"));
|
||||
mqeValues212.getMetric().getLabels().add(new KeyValue("label3", "32"));
|
||||
mqeValues212.getValues().add(newMQEValue("100", 105));
|
||||
MQEValues mqeValues221 = new MQEValues();
|
||||
mqeValues221.setMetric(newMetadata("label", "b"));
|
||||
mqeValues221.getMetric().getLabels().add(new KeyValue("label2", "2b"));
|
||||
mqeValues221.getMetric().getLabels().add(new KeyValue("label3", "31"));
|
||||
mqeValues221.getValues().add(newMQEValue("100", 106));
|
||||
MQEValues mqeValues222 = new MQEValues();
|
||||
mqeValues222.setMetric(newMetadata("label", "b"));
|
||||
mqeValues222.getMetric().getLabels().add(new KeyValue("label2", "2b"));
|
||||
mqeValues222.getMetric().getLabels().add(new KeyValue("label3", "32"));
|
||||
mqeValues222.getValues().add(newMQEValue("100", 107));
|
||||
seriesLabeled.getResults().add(mqeValues111);
|
||||
seriesLabeled.getResults().add(mqeValues112);
|
||||
seriesLabeled.getResults().add(mqeValues121);
|
||||
seriesLabeled.getResults().add(mqeValues122);
|
||||
seriesLabeled.getResults().add(mqeValues211);
|
||||
seriesLabeled.getResults().add(mqeValues212);
|
||||
seriesLabeled.getResults().add(mqeValues221);
|
||||
seriesLabeled.getResults().add(mqeValues222);
|
||||
return seriesLabeled;
|
||||
}
|
||||
|
||||
public ExpressionResult newSeriesLabeledResult(double id1001, double id3001, double id1002, double id3002) {
|
||||
ExpressionResult seriesLabeled = new ExpressionResult();
|
||||
seriesLabeled.setLabeledResult(true);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,115 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.mqe.rt;
|
||||
|
||||
import java.util.List;
|
||||
import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.operation.SortLabelValuesOp;
|
||||
import org.apache.skywalking.mqe.rt.type.ExpressionResult;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SortLabelValuesOpTest {
|
||||
private final MockData mockData = new MockData();
|
||||
|
||||
@Test
|
||||
public void sortLabelValueTest() throws IllegalExpressionException {
|
||||
//des
|
||||
ExpressionResult des = SortLabelValuesOp.doSortLabelValuesOp(mockData.newSeriesComplexLabeledResult(),
|
||||
MQEParser.DES, List.of("label", "label2", "label3")
|
||||
);
|
||||
assertDes(des);
|
||||
//asc
|
||||
ExpressionResult asc = SortLabelValuesOp.doSortLabelValuesOp(des,
|
||||
MQEParser.ASC, List.of("label", "label2", "label3")
|
||||
);
|
||||
assertAsc(asc);
|
||||
// distinct
|
||||
ExpressionResult distinctDes = SortLabelValuesOp.doSortLabelValuesOp(mockData.newSeriesComplexLabeledResult(),
|
||||
MQEParser.DES, List.of("label", "label2", "label3", "label")
|
||||
);
|
||||
assertDes(distinctDes);
|
||||
// ignore
|
||||
ExpressionResult ignoreDes = SortLabelValuesOp.doSortLabelValuesOp(mockData.newSeriesComplexLabeledResult(),
|
||||
MQEParser.DES, List.of("label", "labelIgnore", "label2", "label3")
|
||||
);
|
||||
assertDes(ignoreDes);
|
||||
}
|
||||
|
||||
private void assertDes(ExpressionResult des) {
|
||||
//label
|
||||
Assertions.assertEquals("b", des.getResults().get(0).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", des.getResults().get(1).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", des.getResults().get(2).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", des.getResults().get(3).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", des.getResults().get(4).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", des.getResults().get(5).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", des.getResults().get(6).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", des.getResults().get(7).getMetric().getLabels().get(0).getValue());
|
||||
// label2
|
||||
Assertions.assertEquals("2b", des.getResults().get(0).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", des.getResults().get(1).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", des.getResults().get(2).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", des.getResults().get(3).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", des.getResults().get(4).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", des.getResults().get(5).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", des.getResults().get(6).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", des.getResults().get(7).getMetric().getLabels().get(1).getValue());
|
||||
// label3
|
||||
Assertions.assertEquals("32", des.getResults().get(0).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", des.getResults().get(1).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", des.getResults().get(2).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", des.getResults().get(3).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", des.getResults().get(4).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", des.getResults().get(5).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", des.getResults().get(6).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", des.getResults().get(7).getMetric().getLabels().get(2).getValue());
|
||||
}
|
||||
|
||||
private void assertAsc(ExpressionResult asc) {
|
||||
//label
|
||||
Assertions.assertEquals("a", asc.getResults().get(0).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", asc.getResults().get(1).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", asc.getResults().get(2).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("a", asc.getResults().get(3).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", asc.getResults().get(4).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", asc.getResults().get(5).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", asc.getResults().get(6).getMetric().getLabels().get(0).getValue());
|
||||
Assertions.assertEquals("b", asc.getResults().get(7).getMetric().getLabels().get(0).getValue());
|
||||
// label2
|
||||
Assertions.assertEquals("2a", asc.getResults().get(0).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", asc.getResults().get(1).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", asc.getResults().get(2).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", asc.getResults().get(3).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", asc.getResults().get(4).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2a", asc.getResults().get(5).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", asc.getResults().get(6).getMetric().getLabels().get(1).getValue());
|
||||
Assertions.assertEquals("2b", asc.getResults().get(7).getMetric().getLabels().get(1).getValue());
|
||||
// label3
|
||||
Assertions.assertEquals("31", asc.getResults().get(0).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", asc.getResults().get(1).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", asc.getResults().get(2).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", asc.getResults().get(3).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", asc.getResults().get(4).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", asc.getResults().get(5).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("31", asc.getResults().get(6).getMetric().getLabels().get(2).getValue());
|
||||
Assertions.assertEquals("32", asc.getResults().get(7).getMetric().getLabels().get(2).getValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
* (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
package org.apache.skywalking.mqe.rt;
|
||||
|
||||
import java.util.Optional;
|
||||
import org.apache.skywalking.mqe.rt.exception.IllegalExpressionException;
|
||||
import org.apache.skywalking.mqe.rt.grammar.MQEParser;
|
||||
import org.apache.skywalking.mqe.rt.operation.SortValuesOp;
|
||||
import org.apache.skywalking.mqe.rt.type.ExpressionResult;
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
public class SortValuesOpTest {
|
||||
private final MockData mockData = new MockData();
|
||||
|
||||
@Test
|
||||
public void sortValueTest() throws IllegalExpressionException {
|
||||
//no label
|
||||
ExpressionResult des = SortValuesOp.doSortValuesOp(mockData.newSeriesNoLabeledResult(), Optional.of(3),
|
||||
MQEParser.DES);
|
||||
Assertions.assertEquals(300, des.getResults().get(0).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(100, des.getResults().get(0).getValues().get(1).getDoubleValue());
|
||||
ExpressionResult asc = SortValuesOp.doSortValuesOp(mockData.newSeriesNoLabeledResult(), Optional.of(3),
|
||||
MQEParser.ASC);
|
||||
Assertions.assertEquals(100, asc.getResults().get(0).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(300, asc.getResults().get(0).getValues().get(1).getDoubleValue());
|
||||
|
||||
//labeled
|
||||
ExpressionResult desLabeled = SortValuesOp.doSortValuesOp(mockData.newSeriesLabeledResult(), Optional.of(3),
|
||||
MQEParser.DES);
|
||||
Assertions.assertEquals(300, desLabeled.getResults().get(0).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(100, desLabeled.getResults().get(0).getValues().get(1).getDoubleValue());
|
||||
Assertions.assertEquals(301, desLabeled.getResults().get(1).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(101, desLabeled.getResults().get(1).getValues().get(1).getDoubleValue());
|
||||
ExpressionResult ascLabeled = SortValuesOp.doSortValuesOp(mockData.newSeriesLabeledResult(), Optional.of(2),
|
||||
MQEParser.ASC);
|
||||
Assertions.assertEquals(100, ascLabeled.getResults().get(0).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(300, ascLabeled.getResults().get(0).getValues().get(1).getDoubleValue());
|
||||
Assertions.assertEquals(101, ascLabeled.getResults().get(1).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(301, ascLabeled.getResults().get(1).getValues().get(1).getDoubleValue());
|
||||
|
||||
//limit
|
||||
ExpressionResult desLabeledLimit = SortValuesOp.doSortValuesOp(mockData.newSeriesLabeledResult(), Optional.of(1),
|
||||
MQEParser.DES);
|
||||
Assertions.assertEquals(1, desLabeledLimit.getResults().get(0).getValues().size());
|
||||
Assertions.assertEquals(1, desLabeledLimit.getResults().get(1).getValues().size());
|
||||
Assertions.assertEquals(300, desLabeledLimit.getResults().get(0).getValues().get(0).getDoubleValue());
|
||||
Assertions.assertEquals(301, desLabeledLimit.getResults().get(1).getValues().get(0).getDoubleValue());
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
Subproject commit 13b2693f291903349ebaffa661092e2747141f1c
|
||||
Subproject commit e4b2203cf6f33b1fd84b79ced30ee7c50e15e6d8
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
type: TIME_SERIES_VALUES
|
||||
results:
|
||||
{{- contains .results }}
|
||||
- metric:
|
||||
labels:
|
||||
- key: p
|
||||
value: "90"
|
||||
values:
|
||||
{{- contains .values }}
|
||||
- id: {{ notEmpty .id }}
|
||||
value: {{ .value }}
|
||||
traceid: null
|
||||
- id: {{ notEmpty .id }}
|
||||
value: null
|
||||
traceid: null
|
||||
{{- end}}
|
||||
- metric:
|
||||
labels:
|
||||
- key: p
|
||||
value: "75"
|
||||
values:
|
||||
{{- contains .values }}
|
||||
- id: {{ notEmpty .id }}
|
||||
value: {{ .value }}
|
||||
traceid: null
|
||||
- id: {{ notEmpty .id }}
|
||||
value: null
|
||||
traceid: null
|
||||
{{- end}}
|
||||
- metric:
|
||||
labels:
|
||||
- key: p
|
||||
value: "50"
|
||||
values:
|
||||
{{- contains .values }}
|
||||
- id: {{ notEmpty .id }}
|
||||
value: {{ .value }}
|
||||
traceid: null
|
||||
- id: {{ notEmpty .id }}
|
||||
value: null
|
||||
traceid: null
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
error: null
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership.
|
||||
# The ASF licenses this file to You under the Apache License, Version 2.0
|
||||
# (the "License"); you may not use this file except in compliance with
|
||||
# the License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
type: TIME_SERIES_VALUES
|
||||
results:
|
||||
{{- contains .results }}
|
||||
- metric:
|
||||
labels:
|
||||
{{- contains .metric.labels }}
|
||||
- key: p
|
||||
value: {{ .value }}
|
||||
{{- end}}
|
||||
values:
|
||||
{{- contains .values }}
|
||||
- id: {{ notEmpty .id }}
|
||||
value: {{ .value }}
|
||||
traceid: null
|
||||
- id: {{ notEmpty .id }}
|
||||
value: {{ .value }}
|
||||
traceid: null
|
||||
{{- end}}
|
||||
{{- end}}
|
||||
error: null
|
||||
|
|
@ -81,3 +81,14 @@ cases:
|
|||
expected: expected/trend-OP.yml
|
||||
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="rate(service_resp_time,2)" --service-name=e2e-service-provider
|
||||
expected: expected/trend-OP.yml
|
||||
|
||||
# sort-OP e2e used for test MQE expression, more tests can refer to the UT
|
||||
# sort-value-OP
|
||||
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="sort_values(service_percentile,2,asc)" --service-name=e2e-service-provider
|
||||
expected: expected/sort-value-OP.yml
|
||||
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="sort_values(service_percentile,des)" --service-name=e2e-service-provider
|
||||
expected: expected/sort-value-OP.yml
|
||||
|
||||
# sort-label-value-OP
|
||||
- query: swctl --display yaml --base-url=http://${oap_host}:${oap_12800}/graphql metrics exec --expression="sort_label_values(service_percentile{p='50,75,90'},des,p)" --service-name=e2e-service-provider
|
||||
expected: expected/sort-label-value-OP.yml
|
||||
|
|
|
|||
Loading…
Reference in New Issue