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:
Wan Kai 2024-05-07 16:42:14 +08:00 committed by GitHub
parent a63601318d
commit b3044ee714
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 617 additions and 6 deletions

View File

@ -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

View File

@ -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

View File

@ -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+;

View File

@ -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;

View File

@ -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);

View File

@ -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()
));
}
}

View File

@ -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;
}
}

View File

@ -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);

View File

@ -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());
}
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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